JSF Login System

Created: 13.10.2013

A login system enables user to authenticate to a web application with their credentials. First of all we need a user object that contains some basic information and a password (get/set methods omitted for the sake of brevity).

@Entity
public class User implements Serializable{
	private static final long serialVersionUID = 1L;
	@Id
	@GeneratedValue(strategy = GenerationType.IDENTITY)
	private int userId;
	
	@Column(unique=true)
	private String username;
	private String password;
}

Next, we need a handler which backs a login form. The login form might look like this:

<h:form>
		<h:panelGrid columns="2">
			<h:outputLabel for="username">Username:</h:outputLabel>
			<h:inputText id="username" value="#{loginHandler.username}" />
			<h:outputLabel for="password">Password:</h:outputLabel>
			<h:inputSecret id="password" value="#{loginHandler.password}" />
		</h:panelGrid>
		<h:commandButton value="Login" action="#{loginHandler.login}" />
	</h:form>

The handler has attributes for the user credentials and a user that stores successfully logged in users for each session. The user object can be accessed by other handlers, if they need the currently logged in user. To make sure that such handlers refer to the right user object, every user needs its own handler for the web application. That’s why we create this handler with session scope.

@ManagedBean
@SessionScoped
public class LoginHandler {
	@PersistenceContext
	private EntityManager em;
	@Resource
	private UserTransaction utx;

	private String username = "";
	private String password = "";
	private User currentUser = null;
	
	public String login(){
		TypedQuery<User> query = em.createQuery("select u from User u where u.username=:username, User.class);
        query.setParameter("username", username);
		try{
			User result = query.getSingleResult();
			
			String hashedPassword = SecurityUtils.hashPasswordAndSalt(password + currentUser.getSalt());
			if(currentUser.getPassword().equals(hashedPassword)){
				currentUser = result;
				return "/welcome.xhtml?faces-redirect=true";
			} else {
				return "/login.xhtml?faces-redirect=true";
			}
			
		} catch (NoResultException e){
			return "/login.xhtml?faces-redirect=true";
        } 
	}
	
	public String logout() {
		 currentUser = null;
		 username = "";
		 password = "";
		 
		 FacesContext.getCurrentInstance().getExternalContext().invalidateSession();
		 return "/index.xhtml?faces-redirect=true";
	}
}

The LoginHandler checks the credentials submitted by the user via the login form. If you want to secure the transmission of the credentials from to users client to your web server, you should enable SSL/TLS.
For secure password storage, you need to hash a concatenation of password and a random salt. The salt will be stored as an attribute in the database.
For more information please check my article about secure passwords. If the submitted password is equal to the password in the database, the user can be successfully logged in by setting the currentUser attribute to the user just retrieved from the database. If there is no user with that username or the passwords don’t match you should provide an error message that treats both cases equally (otherwise username farming attacks will be possible).

The logout() method logs out a user by setting the currentUser object null and invalidating its session. Destroying a session also destroys all related session scoped handlers which is usually desired in most applications.

Other handlers might access the active user by injecting the LoginHandler and call its getCurrentUser() method.

@ManagedBean
@RequestScoped
public class ExampleHandler {
	@ManagedProperty(value="#{loginHandler}")
	private LoginHandler loginHandler;
}

Protecting Views by Checking for Logged in Users

One of the main purposes of a login system is to protect views which are reserved for authenticated users. There are two ways to force a check for logged in users.

Solution 1: PreRenderEvents Integrated in Templates

You might include a PreRenderEvent in a special template that is exclusively designed for authenticated users. Such a user template is called from all sites that require a valid login. This event will be thrown before the page is rendered.

<h:head>
<f:event listener="#{loginHandler.checkLoggedIn}" type="preRenderView" />
</h:head>

The LoginHandler is appropriate to listen for such events. We will add a method with the required signature to process the event: checkLoggedIn(ComponentSystemEvent cse). The JSF framework calls this method before the view is rendered.

public void checkLoggedIn(ComponentSystemEvent cse) {
	FacesContext context = FacesContext.getCurrentInstance();
	if( currentUser == null){
		context.getApplication().getNavigationHandler().handleNavigation(context, null, "/login.xhtml?faces-redirect=true");
	}
}

If the currentUser attribute is not set, the user is not authenticated and thus needs to be redirected. If the currentUser attribute is set, nothing needs to be done and the page can be rendered.

Solution 2: Filter to Protect Directories

Another way to protect pages is a filter. A filter doesn't need to be registered in the web.xml if you annotate it with @WebFilter. In addition, you can specify the folders the filter is responsible for in this annotation.

@WebFilter("/protected/*")
  public class UserFilter implements Filter{
  	@Override
  	public void doFilter(ServletRequest request, ServletResponse response,
  	FilterChain chain) throws IOException, ServletException {
  		HttpServletRequest req = (HttpServletRequest) request;
  		LoginHandler loginHandler = (LoginHandler) req.getSession().getAttribute("loginHandler");
  
  		if(loginHandler.isLoggedIn()){
  			chain.doFilter(request, response);
  		} else {
  			HttpServletResponse res = (HttpServletResponse) response;
  			res.sendRedirect(req.getContextPath() + "/login.xhtml");
  		}

 	 }
  }

For our purposes we set a directory called "protected" to indicate that the files inside are login protected. Every time a user tries to access a file inside this directory, the method doFilter() is called. It will call the checkedLogin() method from the LoginHandler and force redirection, if the user is not authenticated.

Dealing with User Time outs

Users can be logged out by calling the logout() method in the LoginHandler. However due to the limitations of the HTTP protocol you might also want to consider users, whose session times out. You can extend the LoginHandler to listen for this situation. The web container will notify the LoginHandler every time a session times out.

@PreDestroy
  public void sessionDestroyed() {
  	if(currentUser != null){
  		currentUser = null;
  		username = "";
  		password = "";
  	}
  }

In this example there is no real use case for this situation, but in larger applications you might want to notify components with application scope.