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.
