|  Java na web X. - Autentizace a autorizace
Webová aplikace potřebuje své uživatele a mezi nimi je potřeba nějakým způsobem rozlišovat. V dnešním díle se budeme věnovat dvoum pojmům – autentizaci a autorizaci.
 16.7.2013 12:00 | 
Petr Horáček 				
| Články autora			
| přečteno 11575×
 V minulém díle jsme si aplikaci rozšířili o databázi, dnes přijde na řadu správa uživatelů a jejich přístupů. Nejdříve se seznámíme se základními pojmy, poté si popíšeme konfiguraci autentizace a autorizace, nakonec si opět vylepšíme naši aplikaci. Autentizace a autorizaceTyto dva pojmy sice vypadají velmi podobně, ale je velmi důležité si je neplést. Autentizace označuje zjištění identity uživatele, nejčastěji pomocí přihlášení. Autorizace určuje, které akce bude moci uživatel vykonat a které nikoliv. Ukládání uživatelůAsi bude dobré začít u samotného „skladování“ uživatelů, pro tyto účely můžeme použít například XML soubor nebo SQL databázi. V obou případech musíme nakonfigurovat tzv. Realm odkazující na Resource. Realm (česky říše) je seznam všech uživatelů a skupin náležících k jedné aplikaci (nebo skupině aplikací). UserDatabaseRealmPři použití XML si ulehčíme práci spojenou s konfigurací databáze, pro pár stálých uživatelů (například správce) je toto řešení dostačující, hlavní nevýhoda tkví v nutnosti restartování aplikace pro načtení nově zapsaných uživatelů.  Konfigurace UserDatabaseRealm se nachází v souboru Tomcatu server.xml hned po instalaci (pozn.: lze ji ale vložit i do souboru context.xml aplikace). Skládá se ze dvou částí, první je Resource (vnořený do tagu GlobalNamingResources) odkazující na soubor conf/tomcat-users.xml ve složce  Tomcatu. <Resource auth="Container" description="Databáze uživatelů" 
    factory="org.apache.catalina.users.MemoryUserDatabaseFactory" 
    name="UserDatabase" 
    pathname="conf/tomcat-users.xml" type="org.apache.catalina.UserDatabase"/>
Druhou částí je tag Realm (vnořený do tagu <Realm className="org.apache.catalina.realm.LockOutRealm">) odkazující na zdroj (Resource) a definující název pomocí kterého budeme k uživatelům přistupovat. <Realm className="org.apache.catalina.realm.UserDatabaseRealm" resourceName="UserDatabase"/>
 Obsah souboru tomcat-users.xml má standardně zakomentován všechno nastavení, po úpravě může vypadat například takto: <?xml version='1.0' encoding='utf-8'?>
<tomcat-users>
  <role rolename="role1"/>
  <user username="uzivatel" password="heslo" roles="role1"/>
</tomcat-users>
 Tagem role vytvoříme novou roli, v tagu user vytvoříme nového uživatele, přiřadíme mu uživatelské jméno, heslo a roli. JDBCRealmV případě použití databáze pro ukládání uživatelů nás sice čeká poněkud delší příprava, poté ale budeme moci snadno a dynamicky spravovat více uživatelů. Začněme s tím, jak by měla vypadat databáze pro ukládání uživatelů a rolí. a) Pro naše účely je potřeba alespoň jedna tabulka (může se nacházet ve společené databázi s dalšími daty aplikace) se jménem, heslem a rolí uživatele, řešení s jednou tabulkou nám ale dovoluje přiřazení jediné role na uživatele. b) Lepším řešením je vytvoření dvou tabulek. V první z nich budeme ukládata uživatele a hesla, v druhé budeme párovat uživatele s rolemi. Když už máme databázi vytvořenou, můžeme přikročit ke konfiguraci Realm-u, ten je stejně jako UserDatabaseRealm možné vložit do souborů context.xml i server.xml. a) Můžeme buď nakonfigurovat Realm a Resource v jednom tagu: <Realm className="org.apache.catalina.realm.JDBCRealm"
      driverName="org.gjt.mm.mysql.Driver"
      connectionURL="jdbc:mysql://localhost/databaze?user=uzivatel&password=heslo"
       userTable="users" userNameCol="username" userCredCol="password"
       userRoleTable="user_roles" roleNameCol="rolename"/>
b) Pokud ale už máte Resource s přístupem k databázi vytvořený, je možné se na něj z Realm-u pouze odkázat: <Realm className="org.apache.catalina.realm.DataSourceRealm"
   dataSourceName="jdbc/mysql"
   userTable="users" userNameCol="username" userCredCol="password"
   userRoleTable="user_roles" roleNameCol="rolename"/>
 ŠifrováníHesla by neměla být ukládána jako prostý text, ale v šifrované podobě. Tomcat standardně podporuje tři hashovací algoritmy: SHA, MD2 a MD5. Pro jejich použití stačí do tagu Realm přidat atribut digest a jako jeho parametr dosadit název algoritmu (např. digest="MD5"). AutorizaceNyní zpět k autorizaci, ta se v Tomcatu konfiguruje jako omezení přístupu k jednotlivým servletům pouze pro určité role. web.xmlPrvní možností konfigurace autorizací je pomocí tagu security-constraintv souboru web.xml aplikace: <security-constraint>
    <display-name>Private Security Constraint</display-name>
    <web-resource-collection>
        <web-resource-name>Protected Area</web-resource-name>
        <url-pattern>/private/*</url-pattern>
        <url-pattern>/administration</url-pattern>
        <http-method>GET</http-method>
        <http-method>POST</http-method>
    </web-resource-collection>
    <auth-constraint>
        <role-name>user</role-name>
        <role-name>admin</role-name>
    </auth-constraint>
</security-constraint>
Tag url-pattern zde určuje adresy, ke kterým se toto opatření vztahuje. Http-method určuje HTTP metody na kterých se má ochrana používat (pokud nazadáte žádnou, bude použita na všechny). Role-name určuje role, které mají povolený přístup. Pokud chcete používat na chráněných místech SSL protokol, můžete do security-constraint přidat tyto tagy: <user-data-constraint>
    <transport-guarantee>CONFIDENTIAL</transport-guarantee>
</user-data-constraint>
AnotaceDruhou možností konfigurace autorizací je definování autorizace pomocí anotace přímo u servletu. @ServletSecurity(
    @HttpConstraint(rolesAllowed = {"user"}, transportGuarantee = TransportGuarantee.CONFIDENTIAL))
Touto anotací povolíme přístup k servletu pouze uživatelům s rolí user, při spojení bude navíc vynuceno šifrování dat pomocí SSL. AutentizaceTomcat podstatně ulehčuje starosti kolem autentizace uživatelů, po minimální konfiguraci totiž sám kontroluje zda se daný uživatel v databázi nachází a zda jsou údaje správné. Je zde možné použít čtyři metody přihlašování: HTTP BASIC, HTTP DIGEST, HTTPS CLIENT a vlastní formuláře. Konfigurace se provádí v souboru web.xml a vypadá následovně: <login-config>
  <auth-method>BASIC</auth-method>
  <realm-name>Realm</realm-name>
</login-config>
 Touto konfigurací nastavíme jako způsob přihlašování BASIC (podobně je to i s DIGEST a CLIENT), při konfiguraci vlastního formuláře je kód o trochu delší: <login-config>
    <auth-method>FORM</auth-method>
    <realm-name>Realm</realm-name>
    <form-login-config>
        <form-login-page>/prihlaseni</form-login-page>
        <form-error-page>/chybaprihlaseni</form-error-page>
    </form-login-config>
</login-config>
Zde je nutné navíc nastavit stránku s přihlašovacím formulářem a s hlášením o neúspěchu. Úprava aplikacePřejděme už k úpravě samotné aplikace. Uživatele budeme ukládat do již vytvořené databáze, hesla budou zahashována algoritmem MD5, autentizace bude probíhat pomocí HTML formuláře a každý uživatel bude mít přístup pouze ke svým zápiskům. DatabázeZačněme s úpravou databáze, přidáme do ní dvě nové tabulky – users a user_roles. Otevřete tedy v NetBeans spojení s naší databázi (vytvořenou v minulém díle) a odešlete první příkaz pro vytvoření tabulky users (username bude primární klíč, password bude řetězec o délce 32, což je délka hashe MD5): create table users (
  username varchar(15) not null primary key,
  password varchar(32) not null
);
 Druhým příkazem vytvořte tabulku user_roles (pár username-rolename bude sloužit jako primární klíč): create table user_roles (
  username varchar(15) not null,
  rolename varchar(15) not null,
  primary key (username, rolename)
);
 Rovnou si vytvoříme i nového uživatele. Klikněte na tabulku users a zvolte View Data, do zobrazené tabulky přidejte nový řádek. Jako username zadejte např. „uzivatel“. Heslo si přeložte do MD5 (například zde:  http://www.adamek.biz/md5-generator.php, „heslo“ → 955db0b81ef1989b4a4dfeae8061a9a6) a vložte do sloupce password, přidání nového řádku potvrďte. Nyní klikněte na tabulku user_roles, zvolte View Data a vložte nový řádek. Username zadejte „uzivatel“ a role „user“.  Teď je nový uživatel připraven k použití. Poslední změnou na databázi bude přidání nového sloupce autor do tabulky zapisky. Klikněte tedy pravým tlačítkem na tuto tabulku a zvolte Add Column, jméno (name) sloupce nastavte na autor, type varchar a size 15, nakonec odoznačte možnost null a potvrďte formulář. RealmDatabázi máme upravenou a můžeme se dát do konfigurace Realmu, otevřete tedy context.xml aplikace a vložte do těla tagu Context tento kód: <Realm className="org.apache.catalina.realm.DataSourceRealm"
    dataSourceName="jdbc/mysql" digest="MD5" localDataSource="true"
    userTable="users" userNameCol="username" userCredCol="password"
    userRoleTable="user_roles" roleNameCol="rolename"/>
Tím nastavíme jako zdroj Realm-u minule vytvořený Resource, hashování hesel na MD5, tabulka uživatelů má název users, sloupec se jmény username, spoupec s hesly password, tabulka rolí user_roles a sloupec s rolemi rolename. UpravaZapisku.javaNyný můžeme upravit třídu UpravaZapisku. Abychom zajistili, že bude každý uživatel moci přistupovat pouze ke svým záznamům, přidáme ke všem dotazům určení autora: ...
    public List<Zapisek> getZapisky(String uzivatel) throws SQLException {
        ...
        try {
            String query = "SELECT * FROM zapisky WHERE autor = ?";            
            connection = getConnection();            
            stmt = connection.prepareStatement(query);        
            stmt.setString(1, uzivatel);
            rs = stmt.executeQuery();
            
        ...
    }
    
    public Zapisek getZapisek(int id, String uzivatel) throws SQLException {
        ...
        try {
            String query = "SELECT * FROM zapisky WHERE id = ? AND autor = ?";            
            connection = getConnection();            
            stmt = connection.prepareStatement(query); 
            stmt.setInt(1, id);
            stmt.setString(2, uzivatel);
            rs = stmt.executeQuery();
            
        ...
    }
    
    public void setZapisek(int id, String nadpis, String obsah, String uzivatel) throws SQLException {
        ...       
        try {
            String query = "UPDATE zapisky SET nadpis = ?, obsah = ? WHERE id = ? AND autor = ?";            
            connection = getConnection();            
            stmt = connection.prepareStatement(query); 
            stmt.setString(1, nadpis);
            stmt.setString(2, obsah);
            stmt.setInt(3, id);
            stmt.setString(4, uzivatel);
            stmt.executeUpdate();
           
        ...
    }
    
    public void addZapisek(String nadpis, String obsah, String uzivatel) throws SQLException {
        ...
        try {
            String query = "INSERT INTO zapisky (nadpis, obsah, autor) VALUES (?, ?, ?)";            
            connection = getConnection();            
            stmt = connection.prepareStatement(query); 
            stmt.setString(1, nadpis);
            stmt.setString(2, obsah);
            stmt.setString(3, uzivatel);
            stmt.executeUpdate();
            
        ...
    }
    
    public void removeZapisek(int id, String uzivatel) throws SQLException {
        ...
        try {
            String query = "DELETE FROM zapisky WHERE id = ? AND autor = ?";            
            connection = getConnection();            
            stmt = connection.prepareStatement(query); 
            stmt.setInt(1, id);
            stmt.setString(2, uzivatel);
            stmt.executeUpdate();
            
        ...
    }  
}
AutorizacePřejděme ke konfiguraci autorizace a úpravě servletu. Protože máme pouze jediný servlet, nepoužijeme web.xml, ale anotaci. Přidejte tedy tento kód (povolující přístup pouze roli user) nad servlet Controller: @ServletSecurity(
    @HttpConstraint(rolesAllowed = {"user"}))
Abychom rozeznali jednotlivé uživatele přistupující k servletu, přidejte do metod doPost a doGet tento kód: String uzivatel = request.getRemoteUser();
 Nakonec upravte všechna volání databáze tak, aby jako poslední parametr předávaly jméno přihlášeného uživatele: ...
List<Zapisek> zapisky = databaze.getZapisky(uzivatel);
...
Zapisek zapisek = databaze.getZapisek(id, uzivatel);
...
databaze.addZapisek(nadpis, obsah, uzivatel);
...
databaze.setZapisek(id, nadpis, obsah, uzivatel);
...
databaze.removeZapisek(id, uzivatel); 
...
 AutentizaceUž máme nastavené úložiště uživatelů i autorizace, nyní zbývá už jen nastavit autentizaci. Začneme vytvořením vlastního přihlašovacího formuláře. Abychom nemuseli vytvářet druhý (veřejný) controller, vytvoříme soubor prihlasit.jsp ve veřejné složce Web Pages a vložíme do něj tento kód: <%@page contentType="text/html" pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib tagdir="/WEB-INF/tags" prefix="m" %>
<m:Base titulek="Zápisky">
    <h1>Zápisník - přihlášení</h1>
    
    <form method="POST" action="j_security_check" >
        <input type="hidden" name="id" value="${zapisek.id}" />
        <label for="jmeno">Přihlašovací jméno</label><br>
        <input type="text" name="j_username" id="jmeno" />
        <br>
        <label for="heslo">Heslo</label><br>
        <input type="password" name="j_password" id="heslo" />
        <br>
        <input value="Přihlásit" type="submit" />
    </form>
    <c:if test="${param.upozorneni}">
        <span>Zadané údaje nejsou platné.</span>
    </c:if>
</m:Base>
Abychom vytvořili přihlašovací formulář spravovaný Tomcatem, je potřeba jen velice málo: Formulář musí mít za atribut action dosazený parametr j_security_check, vstup pro jméno musí být označen j_username a heslo j_password. Navíc je zde upozornění na špatně vyplněné údaje. Zbývá už jen konfigurovat přihlašování v souboru web.xml. Otevřete tedy tento soubor, přesuňte se do karty Security a rozbalte Login Configuration. Nyní označte možnost Form, login page nastavte na /prihlasit.jsp a error page na /prihlasit.jsp?upozorneni=true, Realm name zadejte libovolné. ZávěrTo je vše, nyní můžete aplikaci spustit, přihlásit se a vkládat nové zápisky. V příštím díle se budeme věnovat neoddělitelné části vývoje (a provozu) aplikací – logování, testování a debuggování. Zdrojové kódy aplikace naleznete na GitHubu: https://github.com/PetrHoracek/JavaNaWeb Verze pro tisk 
   
 
 
|  |  
| Nejsou žádné diskuzní příspěvky u dané položky. 
 Příspívat do diskuze mohou pouze registrovaní uživatelé.
 
 |  |