Friday, January 22, 2010

Implementing User Authentication

Earlier in the blog, I showed how to use a 3rd party OpenId service to allow people to login to your site using their Google account, their Facebook account, etc.

That isn't appropriate for every type of site, so now I'll show how to allow people to register at your site and create a login and password specific for your site.

I'd planned on using Spring Security for this, so that the authentication would be robust. But, Spring Security has some dependencies on the Spring framework. Supposedly getting it to work without Spring is possible, but not knowing either product it probably won't happen in the limited amount of time I have available (sorry for those of you searching for how to do just that).

I do, by the way, highly recommend Spring for industrial strength applications built on Java servlets. It just isn't appropriate for the class that I'm teaching. So, the authentication we'll roll here won't be industrial grade. But it'll be a start.

Let's talk for a moment about the characteristics of a good authentication mechanism.

First, it must be impossible to fool. For example, an authentication mechanism that simply avoided displaying members only links to non-members would not prevent non-members from typing those links in directly. The server must authenticate on any restricted page load.

Second, it must be easy to use. The more places we have to touch to implement authentication for a page, the higher the chance that we'll get it wrong. In particular, if we have to write Java code for each page we want protected, the chances are good that we'll get it wrong sooner or later.

We'll use Java Servlet filters to accomplish this. A filter is basically a piece of Java code that is run before a servlet is run. The filter can allow the servlet to run, or it can display a page itself and block the servlet. This happens without adding code to the servlet itself.

First step is to set up our web.xml file to use the filter (okay, so we haven't written the filter yet, but editing web.xml is the easy part so we'll get it out of the way). Add something like the following to your web.xml:


<filter>
<filter-name>AuthenticationFilter</filter-name>
<filter-class>omega.server.AuthenticationFilter</filter-class>
</filter>

<filter-mapping>
<filter-name>AuthenticationFilter</filter-name>
<url-pattern>/members/*</url-pattern>
</filter-mapping>


This is like registering a servlet, in that you specify a URL pattern that triggers the filter. In my case, I used /members/*. This means that any URL under the members directory will trigger the filter. This will require changing my servlet URL mappings around a bit, so that any page that should be restricted to logged in users is mapped under /members, and any page that should be visible to anyone is not. So far for me, that's basically just the ProfileServlet, but I'll make a mental note to put future members only pages under /members.

Now we have to write the AuthenticationFilter class. This is similar to writing a servlet. Create a new class in your server package. There's no superclass for this one, but you will implement an interface. So in the new class dialog, click on the Add button to the right of the (empty) interface list, and type in Filter. You want the javax.servlet.Filter interface.

You'll get some Java code that leaves stubs for three methods: init, destroy, and doFilter. The primary purpose of the init is to save off the FilterConfig passed in, so create a private data member for doing that:


private FilterConfig filterConfig = null;

public void init(FilterConfig arg0) throws ServletException {
filterConfig = arg0;

}


You may not need that FilterConfig instance, but it can be used to get the ServletContext of the servlet that you're filtering.

The actual work of authentication will be done in the doFilter method. Here's the basic idea (not working code, but algorithm):


if (user authenticates correctly)
  chain.doFilter(request, response);
else
{
  User the response argument to generate error output, or redirect to a login form
}


So calling chain.doFilter passes the request on to the next filter in the chain, meaning that the AuthenticationFilter is happy with the request. If authentication fails, do something other than calling chain.doFilter.

The details of pulling a user's password from the datastore and comparing it to one passed into the HTTP request we've already covered. You should also be able to write the registration form and servlet to allow users to create their user account in the first place (and you can do verification of their email, too). You should also know what you need to know to have a "I forgot my password" link that will email a new password to them.

The one bit that we do still need to cover is storing passwords in an encrypted form. Storing user's passwords in plain text is bad form, so we won't do it.

Next post, encrypting passwords.

12 comments:

  1. Unfortunately I'm really looking for a way to use Spring Security and GWT on appengine.

    Perhaps you can do such tutorial in the future!

    Pretty pleaseee!

    ReplyDelete
  2. Hi Haris, if you use Spring on appengine, then folding in Spring Security is easy. I didn't want to use Spring, and spent a few hours trying to get Spring Security working without success. I suspect it's easy if you've used both Spring and Spring Security before and know each well. Without that, it's a learning curve.

    ReplyDelete
  3. I'm considering to implement user authentication this way, using servlet filters, for my GAE/GWT application.
    Would you have any demo code to share?

    By the way, thank you for documenting your project here. The informations you are sharing are both invaluable and well explained :)

    ReplyDelete
  4. Hi Florian, the code snippets in the post should be enough to get you going. The details depend on how you're authenticating the users, which will vary from app to app.

    ReplyDelete
  5. Ok, i'm on it, though i have some doubts about how to handle sessions.
    Do you plan to cover this topic?

    ReplyDelete
  6. Sessions in general were discussed here: http://gwtappengine.blogspot.com/2009/06/session-handling.html

    If you have specific questions about sessions in relation to authentication, I'd be glad to answer them. I would assume that you're authenticating against a datastore then creating the session only if the authentication passes.

    ReplyDelete
  7. I've written a blog post about integrating GWT with Spring Security - works on Google App Engine as well. Check it out on http://technowobble.blogspot.com/2010/05/gwt-and-spring-security.html

    ReplyDelete
  8. Thanks, Mattias! That looks like a great guide to integrating the two.

    ReplyDelete
  9. I have been struggling to figure out how to get Authentication working with UIBinder. Most of the Google authentication code or for that matter other authentication example deals with Authentication for different pages. My problem is I have a tabpanel with multiple tabs and I want the admin to be able to be able to access the admin tab. The admin tab is not a different page(unless I am mistaken and UIBinder is creating pages on the fly) so how to do this?

    ReplyDelete
  10. I'm not familiar with UIBinder, but if it's a GWT thing you'll have to make an authentication call as part of the GWT code that creates the panel.

    ReplyDelete
  11. There is a new (August 2010) post from SpringSource team on how to integrate Spring authentication with GAE:
    http://blog.springsource.com/2010/08/02/spring-security-in-google-app-engine/

    ReplyDelete
  12. A first glance at the article looks like they're using Google Accounts for authentication, which misses the point of using Spring Security for me. Maybe they show how to have site specific logins, too, later.

    ReplyDelete