Monday, December 10, 2007

Glassfish JDBC Realm Authentication

Judging by the numbers of posters on the Glassfish and NetBeans users list that are having problems with this, I thought I would explain how I got Glassfish to play well with JDBC for authentication.

1. The first gotcha is the database driver. Most developers get their copy of Glassfish bundled with NetBeans. Please note that even if the NetBeans IDE comes with drivers for Java DB, MySQL, PostgreSQL and the ODBC bridge, that is not the case for the bundled copy of Glassfish that ships with the IDE.

You will need to install the proper JDBC driver for your database in $GLASSFISH_HOME/domains/yourDomainNameHere/lib or in $GLASSFISH_HOME/lib if you want the drivers to be available from all domains. Additionally, you can also use the database drivers that come with NetBeans and just copy them over to Glassfish instead of hunting them down. The JDBC drivers installed with NetBeans 6 are found in "$NB_HOME/ide8/modules/ext".

2. If you do not have a database pool setup. You will need to define a database resource from Glassfish. You can do this from the admin console by going to "resources" -> "JDBC" -> "Connection Pool". Setup a pool using "javax.sql.DataSource". Complete the wizard and make sure you can "ping" the database.

3. Setup a new JDBC resource for the pool you selected in step 2. JDBC resources can be defined in "resources" -> "JDBC" -> "JDBC Resources". Select whatever JNDI name you want for the resource. Assign the JDBC pool you selected in step 2 above to the new resource.

4. You need to define a new realm for authentication. You can do this in "Configuration" -> "Security" -> "Realms". Assign a name for this realm, let's say "dbtest" for this example and select "com.sun.enterprise.security.auth.realm.jdbc.JDBCRealm" as the class name.

We now need to setup all the properties here for the new realm:

  • JNDI. Set the name you selected in step 3 above.
  • User Table. The table name in your database that has users
  • User Name Column. The column that represents the user name in the user table
  • User Name Column. The column that represents the column in the user table
  • Group Table. The group table.
  • Group Name Column. The column that represents the column in the group table.
  • Digest Algorithm. Glassfish will transform the clear text password using this algorithm before comparing it to the value in the database.
Gotcha #2 here is that database user/password fields are completely useless in this field, just leave them blank.

Gotcha #3 is that the group name column absolutely needs to be a string. So, for example, if you use numeric foreign keys in your database, you will need to create a view that maps user names to user groups by string. You then use this database view for the group table/group name column fields.

Gotcha #4 is that digest algorithm field is actually required even if this doesn't look to be the case from the web page. Furthermore, the "none" algorithm found in Tomcat for example doesn't exist here, you need to digest your passwords, you can specify any Java supported algorithm here, i.e., MD5 or SHA-256.

Finally, to test it out, just use the NetBeans tutorial here but change the realm name in the web.xml file of the application to the one you specified in step #4 above.

23 comments:

miproyectofincarrera said...

Thank you very much for this article. Now I have my authentication working fine. I think that the real advantage of JDBC Realm is that you can add new users easily from a Servlet, for example, to implement a sing in method in a web application.
In that moment I’m working in Enterprise Applications whit Struts and EJB 3.0 technologies. I’m not sure if the authentication made in a web module is passed automatically to a EJB module when a Servlet try to connect to a EJB component to, for example, access to persistent data. ¿Can you say me if I must change something in my EJB’s descriptors?

How can I code a method that do a logoff. What API must I use?

Really useful, thanks from Spain.

Anonymous said...

I have a problem. In my application I'm able to autenticate to the user admin whit password admin in grup user. But when I try to access with another user like user whit password user in group user I have the next error message: HTTP Status 403 - Access to the requested resource has been denied. My password is correct and I configure the constraint to that source.

pjulien said...

Hi miproyectofincarrera,

I am not exactly sure what you are saying here but yes, you can use this authentication for servlets directly if you so wish.

pjulien said...

Hi anonymous,

Make sure your group or user called 'user' actually has a role assigned in your XML descriptor.

Paris said...

hi there patrick.

I followed all your steps but I think Im having trouble with the table names and column names. I have three tables - users, groups and usersgroups tables.

Btw, i just placed MD5 in the digest field. Is it correct?

I already tried a lot of samples but nothing worked for me. My error:

javax.security.auth.login.LoginException: Security Exception

This is my first time developing an EJB application and I hope you could help me :)

Thank you.

pjulien said...

MD5 is valid, that is, assuming your passwords are encoded using this hash.

The exception you are getting is mostly due to an invalid SQL query being generated from your values. Double check all your parameters and type in the SQL query yourself by copy pasting your arguments to see if you get any errors.

Set the application server log level for security to HIGH or something to get the full exception information. You can do this from the admin console, in the logging section under "Application Server".

Paris said...

Hi, thanks for the quick reply. Do I really have to encrypt the password?

I tried to create a single table with username, password and groupname fields and I just entered manually into the database the values admin, password, admin. Maybe that was the problem, I didn't bother to encrypt the password. I also tried to remove the MD5 but I still have no luck :(

pjulien said...

Please see the original blog posting.

Paris said...

ok thanks. i finally made it worked. I don't know what's up with me.

:)

Thank you. I'm so happy now :D

Paris said...

hello.

I don't know the exact 'term' But do you know any tutorials on how to show the current username and group name?

for example in my welcome page, i want to say:

Hello Ms. UserName of GroupName


Thanks :)

pjulien said...

http://java.sun.com/javaee/5/docs/tutorial/doc/

Chesa said...
This comment has been removed by the author.
Paris said...

hi it's me again. I've tried the getCallerPrincipal in my EJB 3.0 system but it shows an error that the object could not be loaded. Could you kindly give me an example or a line of code on how to show/display the current user either in the managed bean or display it directly to the JSF.

I'm using a jdbc realm form-based authentication in Glassfish.

Thanks. I would really really appreciate it :)

Mike said...
This comment has been removed by the author.
Ryan said...

FYI, 'none' is an acceptable value for Digest Algorithm, and it will evaluate the column as plain text as it would suggest. That said, you should probably be encrypting the passwords anyway...

pjulien said...

It is now, at the time this was written, entering none wouldn't work due to a bug.

Ashok said...

Is it possible to impose different security constraints for different roles? If yes how? Please let me know.

Ashok

pjulien said...

Hi Ashok,

Yes, it is possible, see the last paragraph of the article. It has a link to a document at netbeans.org that explains how to do this.

Sam said...

Thanks for the article it has been handy. However I still cannot get my custom JDBC realm to properly authenticate. I am receiving the following error:
com.sun.enterprise.security.LoginException: Failed jdbc login for [i]username[/i]
The passwords in my DB table are MD5 encoded and I have the realm setup per your blog so I'm at a bit of a loss on how to proceed in debugging my issue. Any hints and tips are appreciated. Thanks

Sam said...

Also, I have the server security log set to FINEST and I see the following WARNING in the Server log:
original security exception with detail msg replaced by new exception with empty detail msg

Any ideas on how to see the original message? Thanks again.

pjulien said...

Sam,

Make sure your MD5 passwords are properly encoded. They need to be in hex format, all lower case with spaces or colons in them.

You cannot see the original message as the original exception is lost. A bug was reported to glassfish about this but to my knowledge isn't yet fixed.

Sam said...

Nevermind Patrick, I was able to figure it out...I guess posting a question got me thinking a bit more on the problem and once I set my the charset property in my realm to UTF-8 it worked. Thanks again for the helpful blog.

TD said...

As an update t, you may be interested to know that while many people seem to believe that you may only use the JAAS name of ‘jbdcRealm’ when configuring your Security Realm in Glassfish, this isn’t exactly true.

If you take a look at the

(glassfish_HOME)/domains/domian1/config/login.conf

file, you’ll see that it contains mapping information linking the JAAS name to the correct Login module.

The default map for the jbdcRealm is:

****CODE****

jdbcRealm {
com.sun.enterprise.security.auth.login.JDBCLoginModule required;
};
****CODE****

You can create a new mapping between any JAAS name you like and the relevant Login module using this file, so for example, to create a new jbdcRealm with the JAAS name ‘MySecurity’ you’d add the following map to the file:


****CODE****

MySecurity {
com.sun.enterprise.security.auth.login.JDBCLoginModule required;
};
****CODE****

Once this map is in place, you can go ahead and create your Glassfish Security Realm with the JAAS name ‘MySecurity’.

Without this step, if you configure a Security Realm with any JAAS name other than those provided by default, you’ll get a ‘Login module not found error’.