Ryan Harter

Freelance Android Developer

JSP/Servlet Authentication With Hibernate

| Comments

The web is full of tutorials about setting up Java Web App authentication using j_security_check, but they mostly only cover authenticating users which are stored in an xml file on the server.  While this has it’s uses, it’s really not practical for enterprise applications.  Enterprise apps need more robust user management than can be accomplished with an xml file. Enter SQL and Hibernate.

The Hibernate Model

When it came time for me to set up database authentication, the most difficult part was figuring out how to create my Hibernate model objects such that I could use them with j_security_check. It was obvious that I need to create a User and Group class and give them a many-to-many association, but the problem is that Hibernate translates that association into SQL the same way many DBAs would, with a join table containing the primary keys for each object. This is fine if you make the username and group name the primary keys, but I prefer to have auto incremented ids. The solution is to force Hibernate to build this join table using the properties you want the user to log in with. After creating the POJO User and Group classes, I added the annotations required by Hibernate. I chose annotations because of the convenience of having all of the markup in one file instead of having to modify two files every time I want to make a change. Each class needs very similar annotations to ensure that the join table gets created in a useful fashion. We just need to specify the name of the join table, the names of the columns, and the names of the properties that those columns reference on each object. This ensures that the join table will not be built with the object’s id value, but rather the username and group name. User.java
@ManyToMany(cascade = { CascadeType.ALL })
@JoinTable(name = "User_Group",
	joinColumns = { @JoinColumn(name = "user", referencedColumnName = "username") },
	inverseJoinColumns = { @JoinColumn(name = "groupname", referencedColumnName = "name") })
@ManyToMany(cascade = { CascadeType.ALL })
@JoinTable(name = "User_Group",
	inverseJoinColumns = { @JoinColumn(name = "user", referencedColumnName = "username") },
	joinColumns = { @JoinColumn(name = "groupname", referencedColumnName = "name") })


Make sure you add a @Table(name = “groupp”) annotation to the group table. That extra ‘p’ isn’t a typo, without it Hibernate won’t be able to create the table since “group” is a reserved word in SQL (at least in MySQL).

The Error Servlet

The j_security_check mechanism requires us to supply a login error page.  This, obviously, is where the user is directed if there is an error with their credentials.  This can be useful, but most apps and websites tend to redirect you back to the login page and simply display an error message, allowing the user to try again.  This is the functionality that we are going to emulate using a servlet and redirects.  All we need here is a servlet which redirects the user to the landing page with a URL parameter.
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
	response.sendRedirect(request.getContextPath() + "/manage/listGroupsUsers.jsp?loginerror=true");

A note about the redirect

You may be tempted to make the error page redirect the user directly to the login page. Don’t. j_security_check works by intercepting the user’s request of a secured resource and redirecting the user to that resource after authentication is complete. Accessing the login page directly will allow the user to authenticate, but the user will be redirected to the login page. This can be quite confusing and should be avoided unless you implement a login page which checks for an active session and does something different once the user is authenticated.

The Login Page

The two servlets we just created utilize the fact that when j_security_check forwards the request to the login screen, the parameters are also forwarded. So all we have to do is implement our login screen to handle that parameter in addition to containing the login form.
<%  // Print error message
    String error = request.getParameter("loginerror");
    if (error != null && error.equals("true")) {
    <span>Invalid username or password.</span>
<% } %>

<%-- Login Form --%>
<form action="j_security_check" method="post">
				<td colspan="2">Login</td>
			<td align="right">Username:</td>
			<td><input type="text" name="j_username" /></td>
			<td align="right">Password:</td>
			<td><input type="password" name="j_password" /></td>
			<td> </td>
			<td><input type="submit" value="Login" /><td>

The web.xml File

The last two changes we need to make are to the web.xml file. First we need to tell Tomcat we want to use forms authentication and tell it which forms (pages) to use. This is handled with the login-config element.
The next step is to tell Tomcat which resources are protected and which role is required to access them. This is done with the security-constraint element. Be sure to replace the blue items with relevant values.
    <web-resource-name>User Management</web-resource-name>
    <description>Accessible only to admins</description>
    <description>These are the roles with access</description>

All Done

And that’s all there is to it. You now have a protected web app using the “not-quite-free-but-close” j_security_check and the all powerful Hibernate. Play around with this code and if you find some better solutions feel free to share them in the comments.