JDBC Realm
This is a configuration entirely made in the application server. It may demand some time, but at least your application is going to be free from the basic security complexity. Yasmim uses JDBC Realm because we stored the user data in a database, which could be a third party database (single sign-on for multiple applications in the context of an organization) or the database of the application (users and groups embedded to be independent from the organizational context).
The figure below proposes a data structure for users and groups. It is more complex and realistic than most examples found on the web, but it follows basically the same principle: a group contains several users and a user can be part of several groups. We created a N-N relationship between the tables account_group and user_account, using the intermediary table user_group.
The script to create this structure is available in the repository. The beginning of the script creates the structure above in a MySQL database. For the purpose of this application, you can create the database with just these three tables. If you don't know how to create a MySQL database, follow this post. You will need a connection poll pointing to this database. If you don't know how to create a connection pool, I also wrote a post explaining this.
The sql scripts below consider the model above and they are used to populate the tables for test purposes. The first one inserts 2 users in the database: John Smith and Mary Allen. Their password is secret but it is not readable because a MD5 algorithm was applied on it. The realm will need encoded passwords because we have specified the MD5 as the diggest algorithm in the realm configuration detailed later on. The column confirmation_code is not relevant now, so we defined it as null.
The figure below proposes a data structure for users and groups. It is more complex and realistic than most examples found on the web, but it follows basically the same principle: a group contains several users and a user can be part of several groups. We created a N-N relationship between the tables account_group and user_account, using the intermediary table user_group.
The script to create this structure is available in the repository. The beginning of the script creates the structure above in a MySQL database. For the purpose of this application, you can create the database with just these three tables. If you don't know how to create a MySQL database, follow this post. You will need a connection poll pointing to this database. If you don't know how to create a connection pool, I also wrote a post explaining this.
The sql scripts below consider the model above and they are used to populate the tables for test purposes. The first one inserts 2 users in the database: John Smith and Mary Allen. Their password is secret but it is not readable because a MD5 algorithm was applied on it. The realm will need encoded passwords because we have specified the MD5 as the diggest algorithm in the realm configuration detailed later on. The column confirmation_code is not relevant now, so we defined it as null.
insert into user_account values
('johnsmith@acme.com','Xr4ilOzQ4PCOq3aQ0qbuaQ==',
'John','Smith',1,'1980-02-09',null);
insert into user_account values
('maryallen@acme.com','Xr4ilOzQ4PCOq3aQ0qbuaQ==',
'Mary','Allen',0,'1976-05-10',null);Then, we insert the basic groups admins and users. The group users was defined as the default for registered users, but this attribute is not relevant either here. Maybe we can explore it in a future post.insert into access_group values
('admins','Administrative Group', null);
insert into access_group values
('users','Default User Group', 1);Finally, we associate the users with the groups. Mary Allen is part of the groups admins and users and John Smith is part of the group users. So, Mary can do everything John can do, but John can't do everything that Mary can.insert into user_group values
('admins', 'maryallen@acme.com');
insert into user_group values
('users', 'maryallen@acme.com');
insert into user_group values
('users', 'johnsmith@acme.com');Follow the steps below to configure the JDBC Realm:- Go to the Glassfish admin console (http://localhost:4848/), navigate to Configuration > Security > Realms and press "New...";
- Name: yasmim-realm - it will be used to refer to this realm in the application (tag realm-name in the file web.xml);
- Class Name: com.sun.enterprise.security
.auth.realm.jdbc.JDBCRealm; - Properties specific to this Class (using the model above as a reference):
- JAAS Context: jdbcRealm - identifier for the login module;
- JNDI: jdbc/yasmim - the data source JNDI name pointing to the connection pool where the tables are available;
- User Table: user_account - user table according to the model above;
- User Name Column: user_id - column where the user name is stored;
- Password Column: password - column where the password is stored;
- Group Table: user_group - notice that the group table is not the access_group, but the intermediary table user_group. access_group is used to store further details about the groups;
- Group Name Column: group_id - this is the foreign key to the access_group table, thus this is the id of the group;
- Digest Algorithm: MD5 - of course you are not going to save the password as it is. This is actually embarrassing for you to know the password of your users. So, let's inform to the realm that the password was scrambled by the MD5 algorithm; and
- Encoding: Base64 - the encoding used by the MD5 algorithm.
That's all for the server-side. Now, we are going to configure the application to use this realm.
Application Configuration
We have to configure the files web.xml and sun-web.xml. An example of web.xml configured for this purpose is available in the Yasmim repository. Here, some comments about the configuration:
Since some pages are going to be protected, we'll need an access denied page to alert the user:<error-page>
<error-code>403</error-code>
<location>/access_denied.xhtml</location>
</error-page>
On the application side, the access to resources of the system is given to roles, that can be assumed by users and groups. Roles are declared this way:<security-role>
<description/>
<role-name>admin</role-name>
</security-role>
<security-role>
<description/>
<role-name>user</role-name>
</security-role>
This security constraint states that only users with the role admin can access the content of the /admin folder. These users can perform GET and POST http methods only.<security-constraint>
<display-name>Admin Constraints</display-name>
<web-resource-collection>
<web-resource-name>Administrators</web-resource-name>
<description/>
<url-pattern>/admin/*</url-pattern>
<http-method>GET</http-method>
<http-method>POST</http-method>
</web-resource-collection>
<auth-constraint>
<role-name>admin</role-name>
</auth-constraint>
</security-constraint>
This second constraint states that only users with the role user can access the content of the /media folder. The tag url-pattern is flexible enough to consider several complex situations, but you may find some difficulty if the name of the files and folders are not so well organized.<security-constraint>
<display-name>Users Constraints</display-name>
<web-resource-collection>
<web-resource-name>Users</web-resource-name>
<description/>
<url-pattern>/media/*</url-pattern>
<http-method>GET</http-method>
<http-method>POST</http-method>
</web-resource-collection>
<auth-constraint>
<role-name>user</role-name>
</auth-constraint>
</security-constraint>
Now, we define which authentication method will be presented to the user. We chose the most common one, which is the FORM method. yasmim-realm is the name of the realm we have just created. /login.xhtml is the authentication page and /login_error.xhtml is shown in case of authentication failure.<login-config>
<auth-method>FORM</auth-method>
<realm-name>yasmim-realm</realm-name>
<form-login-config>
<form-login-page>/login.xhtml</form-login-page>
<form-error-page>/login_error.xhtml</form-error-page>
</form-login-config>
</login-config>
In addition to the web.xml file, the sun-web.xml file is used to associate the application roles with the user groups. It is a practical way to associate roles to several users at the same time. The file is located in the WEB-INF folder, together with web.xml, and its content is shown below:<sun-web-app error-url="">
<context-root>/yasmim</context-root>
<security-role-mapping>
<role-name>admin</role-name>
<group-name>admins</group-name>
</security-role-mapping>
<security-role-mapping>
<role-name>user</role-name>
<group-name>users</group-name>
<group-name>admins</group-name>
</security-role-mapping>
</sun-web-app>In this case, the role admin is associated with the group admins and the role user is associated with the groups admins and users.Implementation of the Authentication Page
There are, at least, three pages to consider: login.xhtml, login_error.xhtml and access_denied.xhtml. The login.xhtml page was declared in the web.xml file as the page that collects the authentication data from the user though a standardized form. The form contains the username and password fields and the submit command to post those data to the server. The action of the form and the name of each field are pre-defined. Look at the code below:
<form method="post" action="j_security_check">
<h:panelGrid columns="2">
<h:outputLabel for="j_username" value="E-mail"/>
<input type="text" name="j_username"/>
<h:outputLabel for="j_password" value="Password"/>
<input type="password" name="j_password"/>
<h:outputText value=" "/>
<h:panelGrid columns="2">
<input type="submit" name="submit" value="Sign in"/>
<h:button outcome="index" value="Cancel"/>
</h:panelGrid>
</h:panelGrid>
</form>
This is a JSF 2.0 page with Facelets, but it can be a simple HTML page too. The important thing is the form action, which is j_security_check, and the username and password fields, which are named respectively as j_username and j_password. When the user submit the data the web container will handle it in a special way without interfering in the logic of your application. If you don't need any other fancy feature, the work is done here.According to the authentication and authorization data in the database and what was declared in the application configuration, Once authenticated, Mary Allen has full access to the content of the folders admin and media and John Smith has access only to the content of the folder media. To save your time, take the Yasmim source code as a starting point and explore other possibilities.

Initially, I was planning to write two posts about the JDBC realm but it was taking too long and losing the original focus. So, I decided to take the content of the second post and merge with this one. I hope to write about other details in a near future.
ReplyDeleteGood Hildeberto.
ReplyDeleteThanks for sharing.
Currently I'm using the same mechanism but without JSF, in fact the UI developer is using ExtJs.
Ciao,
Antonio
Nice! The good thing about the Java Authentication and Authorization Service is that it has a small impact on the application, letting most of changes be restricted to the configuration files. Then we have freedom to adopt different presentation technologies.
ReplyDeleteHow would you seed the hash algorithm you are using? I would like to seed the hash, making the password more secure.
ReplyDeleteHi Eddie,
ReplyDeleteTo insert new users in the user_account table, you will definitively need a way to hash the password. In the example above I inserted it already hashed, which is not completely fair. I usually hash is using MD5 + Base64.
In my recent open source project (JUG Management), I have an improved implementation of the mechanism above. Please, have a look on the project as a whole (http://java.net/projects/cejug/sources/jug-management/show) specially the method encryptPassword at http://java.net/projects/cejug/sources/jug-management/content/cejug-ejb/src/java/org/cejug/business/UserAccountBsn.java and the Base64 encoding class at http://java.net/projects/cejug/sources/jug-management/content/cejug-ejb/src/java/org/cejug/util/Base64Encoder.java.
Questions are welcomed! :)
HELP ME, HELP ME
ReplyDeleteavaliação bimestral
1 - Restaure o backup na base de dados correspondente - você encontra esta informação no código fonte.
2 - Em uma pasta admin, implementar as telas de inclusão para as tabelas "access_group" e "user_account"; Lembrando que um user_account está associado a N access_group, logo na página de inclusão deve ter uma forma de permitir ao usuário informar quantos access_group quiser informar. Usar JSF+ManagedBeans+PrimeFaces e outros recursos necessários.
3 - Mudar a maneira de executar as páginas com jsf para nomeDaPágina.seunome
4 - Não esquecer de implementar o form de login
5 - Seguir o roteiro para configurar o JAAS no Glassfish
http://www.hildeberto.com/2010/05/yasmim-security-part-1-user.html
Entregar até a próxima quarta-feira
Hehehe... olha que o professor vai acabar vendo isso em?! :D Os itens 2 e 4 você pode encontrar no projeto open source Jug Management: http://java.net/projects/cejug/sources/jug-management/show. O item 3 você configura no arquivo web.xml, que você também encontra na pasta do projeto jug-web/WEB-INF. Eu usei esse recurso nomeando minhas extensões para 'xhtml'. O 5 você encontra aqui e no projeto. ;) Aproveite bem o final de semana! :)
ReplyDelete