czwartek, 18 lutego 2010

JAAS - podstawy

Co to jest JAAS?
JAAS czyli Java Authentication and Authorization Service, jest to narzędzie wbudowane do Javy 1.4 i nowszej, które pozwala na łatwą autoryzacje i uwierzytelnianie użytkownika.

Aplikacja
Zaczniemy od zrobienia prostej aplikacje, którą później zabezpieczymy przy pomocy JAAS'a tak, aby osoba przeglądająca naszą witrynę musiała potwierdzić że jest w gronie osób które mogą przeglądać treść danych podstron.
Moja prosta aplikacja wygląda tak:

Do stworzenia mojej aplikacji skorzystałem z popularnego edytora Eclipse. Projekt nazwałem JAASHelloWorld. Widzimy że cała aplikacja składa się z trzech podstron:
  • admin/index.jsp - strona do której będzie miał dostęp tylko administrator
  • user/index.jsp - strona do której będzie miał dostęp zalogowany użytkownik i administartor
  • index.jsp - strona główna do której mają dostęp wszyscy
Aplikacja będzie uruchomiana na popularnym serwerze aplikacji JBoss.

Panel logowania
Bardzo ważną rzeczą jest formularz logowania, dzięki któremu użytkownik będzie mógł potwierdzić swoją tożsamość. Dlatego zaczniemy od stworzenia nowego katalogu login a w nim strony login.jsp. Strona zawiera prosty formularz z dwoma polami i przyciskiem. Formularz musi zawierać trzy bardzo istotne elementy:
  • akcję formularza ustawiamy na j_security_check
  • pole login zawiera atrybut name z wartością j_username
  • pole hasło zawiera atrybut name z wartością j_password
U mnie plik ten wygląda:
<html>
 <head>
  <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
 <title>Login</title>
 </head>
 
 <body>
 
  <form method="post" action="j_security_check">
   <div>
    Login:
    <input type="text" name="j_username" />
   </div>
   
   <div>
    Hasło:
    <input type="password" name="j_password"/>
   </div>
   
   <div>
    <input type="submit" value="LOGIN" />
   </div>
  </form>
 
 </body>
</html>

Potrzebujemy jeszcze strony na którą zostaniemy przekierowani w razie błędnego logowania. Do tego celu tworzymy stronę login/login_err.jsp która będzie zawierać komunika o nieudanym logowaniu. U mnie wygląda to tak:
<html>
 <head>
  <title>Login Error</title>
 </head>
 
 <body>
  <div>Logowanie się nie powiodło. Spróbuj jeszcze raz.</div>
 </body>
</html>

Po wykonaniu tego kroku aplikacja wygląda tak:


Baza danych
Następnym krokiem jakim się zajmiemy jest stworzeniem bazy danych. Skrypt który to zrobi wygląda tak:

CREATE TABLE users (
 login VARCHAR(20),
 password VARCHAR(100)
);

CREATE TABLE user_roles (
 login VARCHAR(20),
 rolename VARCHAR(20)
);

Pierwsza tabela users przechowuje nazwy użytkowników (login) i hasła (password) do ich kont.
Druga tabela user_roles przechowuje nazwę użytkownika (login) i nazwę roli (rolename) do której użytkownika należy. Zgodnie z polityką bezpieczeństwa wszystkie hasła przechowywane w bazie danych muszą być szyfrowane. Tak będzie i u nas, dlatego przy pomocy pierwszej strony jaką udało mi się znaleźć w
wyszukiwarce google.com, pod hasłem: "md5 generator" szyfrujemy hasła.
Tak wiec po zaszyfrowaniu haseł admin i user uzyskujemy wyniki:
  • admin = 21232f297a57a5a743894a0e4a801fc3
  • user = ee11cbb19052e40b07aac0ca060c23ee

INSERT INTO users VALUES('admin','21232f297a57a5a743894a0e4a801fc3');
INSERT INTO user_roles VALUES('admin','admin');
INSERT INTO users VALUES('user1','ee11cbb19052e40b07aac0ca060c23ee');
INSERT INTO user_roles VALUES('user1','user');

Konfiguracja JBoss i JAAS
Nadszedł czas na najważniejszą części naszej konfiguracji. Na początek stworzymy DataSource który będzie wskazywał JBoss'owi jakiej bazy należy użyć. JBoss wraz z samym serwerem udostępnia przykładowe pliki konfiguracyjne, które znajdują się w katalogu jboss_home\docs\examples. Zgodnie z zasadą "nie rób więcej niż musisz" wchodzi do tego
jboss_home\docs\examples\jca i kopiujemy plik z nazwą nazwa_bazy-ds, u mnie mysql-ds do katalogu jboss_home\server\default\deploy. Plik ten oczywiście musimy delikatnie zmodyfikować, dlatego otwieramy go i zmieniamy wartości elementów:
  • <jndi-name> - określa nazwę naszego DataSource'a
  • <connection-url> - lokalizacja bazy danych, my używamy MySQl ktory działa pod adresem localhost/nazwa_bazy_danych
  • <user-name> - nazwa użytkownika w bazie danych
  • <password> - hasło do bazy danych
U mnie plik ten po zmodyfikowaniu wygląda tak:
<?xml version="1.0" encoding="UTF-8"?>

<!-- $Id: mysql-ds.xml 41017 2006-02-07 14:26:14Z acoliver $ -->
<!-- Datasource config for MySQL using 3.0.9 available from:
http://www.mysql.com/downloads/api-jdbc-stable.html
-->

<datasources>
<local-tx-datasource>
<jndi-name>MySqlDS</jndi-name>
<connection-url>jdbc:mysql://localhost:3306/testdb</connection-url>
<driver-class>com.mysql.jdbc.Driver</driver-class>
<user-name>root</user-name>
<password>mysql</password>
<exception-sorter-class-name>org.jboss.resource.adapter.jdbc.vendor.MySQLExceptionSorter</exception-sorter-class-name>
<!-- should only be used on drivers after 3.22.1 with "ping" support
<valid-connection-checker-class-name>org.jboss.resource.adapter.jdbc.vendor.MySQLValidConnectionChecker</valid-connection-checker-class-name>
-->
<!-- sql to call when connection is created
<new-connection-sql>some arbitrary sql</new-connection-sql>
-->
<!-- sql to call on an existing pooled connection when it is obtained from pool - MySQLValidConnectionChecker is preferred for newer drivers
<check-valid-connection-sql>some arbitrary sql</check-valid-connection-sql>
-->

<!-- corresponding type-mapping in the standardjbosscmp-jdbc.xml (optional) -->
<metadata>
<type-mapping>mySQL</type-mapping>
</metadata>
</local-tx-datasource>
</datasources>

Używamy bazy danych MySQL, dlatego potrzebujemy jeszcze connector do bazy danych. Plik ten można ściągnąc z ofincjalne strony MySQL , a po rozpakowaniu skopiować do katalogu jboss_home\server\default\lib.

Wrcamy ponownie do katalogu jboss_home\server\default\deploy i tworzymy w nim plik jboss-service.xml. Plik ten będzie zawierał informacje, gdzie JAAS ma szukać informacji na temat loginu, hasła i grupy do jakiej użytkownik należy. U mnie plik ten wygląda tak:
<?xml version="1.0" encoding="UTF-8"?>
<server>
 <mbean code="org.jboss.security.auth.login.DynamicLoginConfig"
  name="jboss.seminarium:name=seminarium.login,service=DynamicLoginConfig">

  <attribute name="PolicyConfig" serialDataType="jbxb">
   <jaas:policy
    xsi:schemaLocation="urn:jboss:security-config:4.1 resource:security-config_4_1.xsd"
    xmlns:jaas="urn:jboss:security-config:4.1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
    <jaas:application-policy name="my-auth">
     <jaas:authentication>
      <jaas:login-module
       code="org.jboss.security.auth.spi.DatabaseServerLoginModule"
       flag="required">
       <jaas:module-option name="dsJndiName">java:/MySqlDS</jaas:module-option>
       <jaas:module-option name="principalsQuery">
        select u.password from users u where u.login = ?
</jaas:module-option>
       <jaas:module-option name="rolesQuery">
        select r.rolename, 'Roles' from user_roles r, users u
        where u.login = ? and u.login = r.login
</jaas:module-option>
       <jaas:module-option name="hashAlgorithm">MD5</jaas:module-option>
       <jaas:module-option name="hashEncoding">hex</jaas:module-option>
       <jaas:module-option name="hashCharset">UTF-8</jaas:module-option>
      </jaas:login-module>
     </jaas:authentication>
    </jaas:application-policy>
   </jaas:policy>
  </attribute>
  <depends optional-attribute-name="LoginConfigService">
   jboss.security:service=XMLLoginConfig
</depends>
  <depends optional-attribute-name="SecurityManagerService">
   jboss.security:service=JaasSecurityManager
</depends>
 </mbean>
</server>

Krótko powiem co zawierają główne elementy powyższego pliku:
  • <jaas:application-policy> - atrybut name zawiera nazwę dla JAAS'a
  • <jaas:module-option name="dsJndiName"> - wartością tego elementu jest nazwa DataSource'a
  • <jaas:module-option name="principalsQuery"> - wartością tego elementu jest zapytanie select wskazujące na hasło użytkownika
  • <jaas:module-option name="rolesQuery"> - wartością tego elementu jest zapytanie select wskazujące na nazwę roli użytkownika
Wracamy teraz z powrotem do aplikacji. W katalogu WebContent\WEB-INF tworzymy plik jboss-web.xml. U mnie plik ten wygląda tak:
<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE jboss-web PUBLIC
"-//JBoss//DTD Web Application 2.4//EN"
"http://www.jboss.org/j2ee/dtd/jboss-web_4_0.dtd">

<jboss-web>
<security-domain>java:/jaas/my-auth</security-domain>
</jboss-web>

Plik ten zawiera element <jboss-web> w którym znajduje się element <security-domain> zawierający nazwę dla JAAS'a którą ustalilismy w pliku jboss-service.xml.
Ostatnim plikiem jaki musimy zmodyfikować jak plik WebContent\WEB-INF\web.xml. W tym pliku ustawiamy:
  • jakie katalogi (strony) są zablokowane dla niezalogowanych użytkoników
  • jakie role obsługiwane są w naszej aplikacji (u mnie jest to user i admin)
  • gdzie znajduje się formularz do logowania

U mnie plik ten po modyfikacji wygląda tak:
<?xml version="1.0" encoding="UTF-8"?>
<web-app id="WebApp_ID" version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
 <display-name>JAASHelloWorld</display-name>
 <welcome-file-list>
  <welcome-file>index.jsp</welcome-file>
 </welcome-file-list>
 
 <security-constraint>
  <web-resource-collection>
   <web-resource-name>Restricted to user</web-resource-name>
   <url-pattern>/user/*</url-pattern>
  </web-resource-collection>
  <auth-constraint>
   <role-name>user</role-name>
   <role-name>admin</role-name>
  </auth-constraint>
 </security-constraint>

 <security-constraint>
  <web-resource-collection>
   <web-resource-name>Restricted to Admin</web-resource-name>
   <url-pattern>/admin/*</url-pattern>
  </web-resource-collection>
  <auth-constraint>
   <role-name>admin</role-name>
  </auth-constraint>
 </security-constraint>

 <login-config>
  <auth-method>FORM</auth-method>
  <realm-name>my-auth</realm-name>
  <form-login-config>
   <form-login-page>/login/login.jsp</form-login-page>
   <form-error-page>/login/login_err.jsp</form-error-page>
  </form-login-config>
 </login-config>

 <security-role>
  <description>Typowy uzytkownik systemu</description>
  <role-name>user</role-name>
 </security-role>

 <security-role>
  <description>Administrator systemu</description>
  <role-name>admin</role-name>
 </security-role>
 
</web-app>

To teraz krótki opis, co znajduje się w powyższym pliku.
Jak można zauważyć, w pliku wydzielone są trzy główne elementy: <security-constraint>, <login-config> i <security-role>. Element <security-constraint> zawiera informacje o blokowanych zasobach, oraz o tym jakie grupy użytkowników mają dostęp do informacje zawartych na tych stronach. Element <login-config> zawiera informacje o rodzaju uwierzytelniania (<auth-method>) oraz o formularzu który ma się wyświetlić w razie odniesienia się do zablokowanych zasobów, w celu sprawdzenia czy użytkownik ma wystarczające prawa do oglądania treści zawartych na stronie. Przy konfiguracji JAAS'a ustawiamy również pod element <realm-name>, który zawiera nazwę realm (wartość ustawiana była w pliku jboss-service.xml). Element <security-role> przechowuje nazwę grupy, nasza mini aplikacja posiada dwie grupy, user - czyli zwykli użytkonicy, oraz admin - czyli administrator strony.

Możemy teraz przetestować to co stworzyliśmy. Uruchamiamy serwer JBoss i depoloy'ujemy na nim aplikację. Strona będzie dostępna pod adresem http://localhost:8080/JAASHelloWorld/ lub http://127.0.0.1:8080/JAASHelloWorld/.

Wylogowanie
Kiedy logowanie już działa, potrzebujemy jeszcze mechanizmu wylogowania. Jest na to bardzo szybki sposób. W katalogu WebContent/login tworzymy plik logout.jsp i umieszczamy w nim treść:
<%
session.invalidate();
response.sendRedirect("../index.jsp"); 
%>

Teraz gdy mamy już logowanie i wylogowywanie gotowe, struktura naszej aplikacji powinna wyglądać tak:


1 komentarz: