Working with Rails

Recommend Brian on Working With Rails


Powered

Authentication with Seam

Posted by Joseph Nusairat Wed, 16 Jul 2008 04:00:00 GMT

One characteristics of modern web frameworks is to provide lots of functionality out of the box. This functionality ranges from authentication, to batch processing, to validation. For those of you who haven't used Seam's authentication framework; rest assured it is extremely easy to use. However, if you want to add some custom messaging things get a little more complex. I will show you a work around for the custom messaging issue but first let's discuss how to deal wih Seam authentication in general.

The main component to remember is the Identity class. This class stores the user, password, and provides the methods for login in and out. This class is provided by Seam out of the box. In the first part we will create the login itself.

The login can be fairly simple and will use the Identity object. Here is a simple login form.

Partial of login.xhtml

<h:form id="login">
<h:inputText id="username" size="20" value="#{identity.username}" required="true"/>
<h:inputSecret id="password" size="22" value="#{identity.password}" required="true"/>
<h:selectBooleanCheckbox id="rememberMe" value="#{identity.rememberMe}"/>
<h:commandButton value="Login" action="#{identity.login}"/>

In your components.xml we need to tell Seam what class/method to use for authentication by defining an authentication method and class to call when #{identity.login} is called.

components.xml

<security:identity 
    authenticate-method="#{authenticator.authenticate}"/>

Then you define a Seam component named "authenticator" that provides an "authenticate" method. By default Seam allows you to inject the Identity object (a Seam Session scoped component). A base skeleton of this code is here:

Authenticator.java

@Name("authenticator")
public class Authenticator {
    @In
    Identity identity;

    public boolean authenticate() {
        // Do your database, ldap, etc lookup
        // then return true/false based on if it was successful
    }
}

It's that simple however, there is one IMPORTANT thing to remember when using Seam Security. And that is Authentication != Login. This distinction will for the most part not affect you unless you want to start dealing with error messages.

By default there are two messages, a failure or successful login that are displayed by Seam automatically when an authentication passes/fails. Both of these are defined in the message.resources file as :

message_en.properties

org.jboss.seam.loginFailed=Login failed!
org.jboss.seam.loginSuccessful=Welcome

You can change the messages there. However, what if your requirements are more complex? What if you want to display a message that specifically informs that the username or password is incorrect, or if you are using something like LDAP, and you want to report that their servers are down. Simple enough right? Just do a FacesMessage context look up and add to it? Right? Wrong! What's the problem with this? You run a good chance that the error message will show up twice on the screen. While this isn't the worse thing to happen, it is a bit annoying. So why does this happen? The authenticator provides just that; "authentication" and is not actually performing the login logic. Meaning that the authenticator is actually called multiple times depending on the scenario and you do not necessarily have control over when and how often the authenticator is called.

To fix that we start by not write messages directly inside the authenticator. Instead, we will write login messages where they should be written, at login. So how do we do this while still using Seam's login? One way is to use a combination of the Seam Events/Observers and to write a "custom login".

The purpose of the Events/Observer, is actually quite interesting. You can write an event in one area of the code, and the Observer can pick it up based on a keyword in a separate class and process it. So in the authenticator you can add this line of code whenever you want to raise a specific message.

Events.instance().raiseEvent("invalidLogin", "Invalid password!!");

The first part of the method contains the key "invalidLogin", the second part displays whatever string we want to display to the front end. Please note, the actual method takes an array of Objects for it's second parameter, so you could pass as many strings, or whatever objects you want.

We have now saved this event, now comes the part of retrieving it and storing it to the FacesMessages, this part is a BIT trickier. We need to create our own Identity class. The Identity, as I explained is the component that actually performs the logging in. But wait just earlier i said that Identity is a Seam level component. What we are going to do is basically extend the Identity class and then replace the component with ours. Lets take a look at our custom identity component.

CustomIdentity.java

@Name("org.jboss.seam.security.identity")
@Scope(ScopeType.SESSION)
@Install(precedence = Install.APPLICATION)
@BypassInterceptors
@Startup
public class CustomIdentity extends Identity {
    private static final long serialVersionUID = -9154737979944336061L;

    @Override
    public String login() { 
        String retVal = "";
    try {
            // clear out the login message in case it was triggered
            // by an authenticate occurring outside the login
            loginErrorMessage = null;           
            retVal = super.login();         
        } finally {         
            // check if any error messages were registered from
            // this logging., if they are write them out            
            if (StringUtils.isNotBlank(loginErrorMessage)) {
                FacesMessages.instance().add(loginErrorMessage);
            }
        }       
        return retVal;
    }

    private String loginErrorMessage;

    /**
     * This is used to save off an error message in case of a login.
     * @param message
     */
    @Observer("invalidLogin")
    public void writeInvalidLogin(String message) {         
          loginErrorMessage = message;      
    }
}

This code is a bit of a mouthful, but really not that hard to implement. The class level annotations are similar to the ones by default with Identity except we change the precedence to a later time. By default Identity has a BUILT_IN precedence. By changing it to APPLICATION we guarantee that our custom component will be the one used by Seam instead of Seam's own Identity component.

The last method in the code defines the Observer which is called when the event is raised, we are going to set it to a class level string. Finally in the login method we are going to call the parent's login method (which is Seam's Identity login method) but after the login call we will check to see if any messages were saved by our Events/Observer pattern and if they are we will add them to the FacesMessages.

With those changes in place you will now be able to display login errors without showing them displaying multiple times on a page.

Posted in , , ,  | no comments | no trackbacks

JBoss Seam 1.1.6 & Security

Posted by Joseph Nusairat Tue, 20 Feb 2007 05:00:00 GMT

JBoss Seam
A few days ago, I blogged complaining how in Seam 1.1.5 in order to create your authentication class you had to create a method with 3 parameters: username, password, and a mutable set of roles. Well, this weekend I downloaded Seam 1.1.6 and much to my surprise they've changed it. In 1.1.6 you inject an Identity object into your POJO instead. This identity object contains the username, password, and set of roles. I'd like to think my blog had something to do with that, but I highly doubt it ;-) I just wanted to add this correction and send a kudos to the Seam team for changing it. Of course your authenticate method still has to return a boolean ... but that is much more acceptable behaviour.

Posted in , ,  | no comments | no trackbacks

Abusing POJOs? - JBoss Seam Security

Posted by Joseph Nusairat Mon, 05 Feb 2007 05:00:00 GMT

JBoss Seam

I've recently downloaded the latest Seam distribution (1.1.5). 1.1.5 is when Seam adds Security, and I wanted to update the source code for my book, Beginning JBoss Seam, accordingly.

For the most part I’ve been happy with their security implementation; however, there is one thing that really bugged me. In previous “development” releases they had the security class extend an interface. However, that went against the Seam paradigm of using POJOs everywhere. So now they made the security is implemented as a POJO. Now that in itself isn’t bad, but in my opinion their implementation is questionable.

The way Seam implemented it is there is a specific class for login in and out, passing in username and passwords etc. In order to authenticate using it, you are required to create a POJO has a method like boolean (String username, String password, Set roles)

You can name it anything you want. But the parameter and return has to be in that format. Furthermore the method takes in a username and password, and then sets the roles that are assigned to it. So now you are forcing the parameter “roles” to be a mutable object, something I try to avoid at all costs.

Fair enough they had a hard time implementing a solution. But if you are going to the extent to make the method EXTREMELY specific go ahead and just have it implement an interface. Thats what they are there for. A POJO while a great concept, is not a 100% solution. And in general POJOs are great for business services or presentation tier services that could be accessed in a non-specific manner. An authentication mechanism is not that way. Authentications almost always are very specific. you have a username, password and then are going to return authentication credentials. I understand what they were trying to accomplish but sometimes I think people are soo desperate to make everything a POJO, even items that shouldn’t be.

Final thought, I started this blog "Abusing POJOs?" with a question mark, the reason being to ask yourself is this really abuse or is this the way it should be.

Posted in , ,  | no comments | no trackbacks

Categories

Archives

Syndicate