Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Binding a User entity and a GlassFish Principal

I have an entity class User that contains information such as username, first name, last name and a password and I have my GlassFish 3.1 server setup to perform authentication. So far, so good. After the container has authenticated a user, I need some way to bind the principal to the actual User entity. After all, GlassFish is telling me is that user "laurens" has authenticated, it is not giving me the corresponding User entity.

To that end I wrote a JSF managed bean UserController. What I would like to know is if this is the correct way to look the actual entity up and if there are any obvious pitfalls I am not seeing.

UserController features the following fields:

@EJB
private UserFacade userFacade;

private User user;

The userFacade is a stateless session bean to persist and find User instances. The user field is used by the JSF page to get and set properties on the user.

I use the following method to perform the binding accompanied by two helper methods:

@PostConstruct
private void init() {
    try {
        user = userFacade.find(getUserPrincipal().getName());
    } catch (NullPointerException ex) {
        // Intentionally left empty -- User is not logged in.
    }
}

private HttpServletRequest getHttpServletRequest() {
    return (HttpServletRequest) FacesContext.getCurrentInstance().getExternalContext().getRequest();
}

private Principal getUserPrincipal() {
    return getHttpServletRequest().getUserPrincipal();
}

The following methods are used by the JSF page to determine what components to show (if the user is already authenticated then there is no need to show a login form), authenticate the user if the "Login" button is clicked, or register as a new user when the "Register" button is clicked.

public boolean isAuthenticated() {
    return getUserPrincipal() != null;
}

public void authenticate() {
    try {
        getHttpServletRequest().login(user.getEmailAddress(), user.getPassword());
    } catch (Exception ex) {
        // TODO: Handle failed login attempt
    }
}

public void register() {
    userFacade.create(user);
}

Would this the correct way to go about?

Thanks!

Edit:

Thanks for the input both! I thought about it for a bit, and while I think moving the passwords to a different table is a little to much for me to handle at the moment, I do think I can address some of the issues by separating the UserController in a @RequestScoped AuthenticationController and a stripped down @SessionScoped UserController.

The AuthenticationController would have emailAddress and password fields, bound by the web page's emailAddress and password fields. It would additionally contain the public void authenticate() to authenticate the user and discard the credentials afterwards. The @SessionScoped UserController can then bind to the appropriate User entity without ever needing to know a password. In fact I believe I would be able to remove the password field from User altogether.

like image 629
Laurens Avatar asked Jul 25 '11 17:07

Laurens


1 Answers

Your proposed approach has a few rough edges, but for the most part it is quite fine.

If you intend to store a reference to the User entity, then it is preferable to do so in a SessionScoped managed bean. This has it's pros and cons. The obvious advantage is that

  • the User entity is available through out the application flow, across all pages. This would mean that you need to bind the Principal to a User entity only once for a session. And you can re-use the bound value through all pages, if the need arises.

The not-so-obvious disadvantage is that

  • the password field would be stored in memory for quite a long duration. At best, you should attempt to nullify the password field of the entity after an authentication attempt (whether it is unsuccessful or not, irrespective of whether the field contains the password in clear or a hash). Also, it would make a lot of sense to define the password field as lazily fetched (FetchType of LAZY) as opposed to the default of eager fetch (FetchType of EAGER). If you implement this (in particular, nullification of the password field), you'll need to watch out for problems involving merge operations conducted on the User entity; in such an event, it might be better to have a separate entity to store passwords for users (quite unfortunate, but that is the extent to which you must bend your back to protect password and their hashes in certain applications).

Having said that, it is also necessary to ensure the following:

  • The Anonymous user principal should be handled carefully. If you are not writing a filter that enforces an access control mechanism to protect your "private" pages of the application, you should worry a lot more about the way authorization logic in your pages are constructed, not that you do not have to worry at all when you use a filter. The Anonymous principal is just like any other principal, except that it isn't backed by an identity in a realm. If the Principal to User entity binding scheme fails for some reason, you must invalidate the session and redirect the user to the login page again, especially if your pages rely on the User entity instead of the Principal object to enforce access control checks.
  • Ensure that you have a separate login page for the application. This is preferable in most applications that accept user credentials in a form, present in the login page; if the form is in a dialog or in some other contraption, a separate login page is usually not necessary. This is desirable for the simple reason that you would want your login process to implement the POST-REDIRECT-GET pattern - a user on successfully logging into your application must be redirected to the main page of the application. Failing to do so, would result in the scenario where a browser refresh (performed by any person having access to the terminal) would re-submit the credentials; quite obviously applications that use a modal dialog or something similar are less susceptible to this problem.

Update

This is based on the edited question. If you implement the authenticate method as proposed, you can implement your scheme of binding the User entity to the Principal only upon successful authentication.

The following is a reproduction of a similar implementation from my application:

public String authenticate()
{
    String result = null;
    ExternalContext externalContext = FacesContext.getCurrentInstance().getExternalContext();
    HttpServletRequest request = (HttpServletRequest) externalContext.getRequest();
    try
    {
        request.login(userId, password);
        result = "/private/MainPage.xhtml?faces-redirect=true";
    }
    catch (ServletException ex)
    {
        logger.error("Failed to authenticate user.", ex);
        FacesMessage facesMessage = new FacesMessage(FacesMessage.SEVERITY_ERROR, Messages.getString("Login.InvalidIdOrPasswordMessage"), null);
        FacesContext.getCurrentInstance().addMessage(null, facesMessage);
    }
    return result;
}

which is invoked from the facelet:

<h:form id="LoginForm" acceptcharset="UTF-8">
    <p>
        <h:outputLabel for="userid" value="#{msg['Login.userid.label']}" />
        <h:inputText id="userid" value="#{loginBean.userId}" />
    </p>
    <p>
        <h:outputLabel for="password" value="#{msg['Login.password.label']}" />
        <h:inputSecret id="password" value="#{loginBean.password}" />
    </p>
        <h:commandButton id="submit" value="#{msg['Login.submit.label']}"
            action="#{loginBean.authenticate}" />
</h:form>

Note the use of the POST-REDIRECT-GET pattern for the case where the authentication is successful. I've left a bit of code that relates to invalidation of the current session before redirection, to prevent session fixation attacks. The binding of the User entity to the Principal would be done in new session, as long as it is done in a session scoped bean.

like image 200
Vineet Reynolds Avatar answered Sep 27 '22 17:09

Vineet Reynolds