Implementing Spring Security Remember Me with JSF 2.0

Posted on Updated on

Today we will add remember-me functionality to our project.

We will enhance our code from earlier revision, namely:

  1. Integrating Spring Security 3.1 and JSF 2.0
  2. Spring Security 3.1 and JSF 2.0 Custom Form
  3. Adding Spring Security Logout Functionality to JSF 2.0

Remember me functionality adds a expriry timeout to a cookie which is set on the client machine, when client tries to login, cookie is checked, if it has expired client is requested to login or else client is automatically logged in.

Let’s start adding this functionality.

First update our jsfspring-sec-bean-config.xml and add rememberMe services:

jsfspring-sec-bean-config.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:beans="http://www.springframework.org/schema/beans"
	xmlns:sec="http://www.springframework.org/schema/security"
	xsi:schemaLocation="
	   http://www.springframework.org/schema/beans
	   http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
       http://www.springframework.org/schema/security
	   http://www.springframework.org/schema/security/spring-security-3.1.xsd"
	   >

	<beans:bean id="navigator" name="navigator" class="com.mumz.jsfspringsec.beans.Navigator" scope="session">
	</beans:bean>

	<beans:bean id="loginBean" name="loginBean" class="com.mumz.jsfspringsec.beans.LoginBean" scope="prototype">
		<beans:property name="authenticationManager" ref="authenticationManager"></beans:property>
		<beans:property name="rememberMeServices" ref="rememberMeServices"></beans:property>
	</beans:bean>

	<beans:bean id="rememberMeServices"
		class="org.springframework.security.web.authentication.rememberme.TokenBasedRememberMeServices">
		<beans:property name="key" value="jsfspring-sec" />
		<beans:property	name="userDetailsService" ref="userDetailsService" />
		<beans:property	name="alwaysRemember" value="true" />
		<beans:property	name="tokenValiditySeconds" value="60" />
	</beans:bean>

	<beans:bean id="rememberMeAuthenticationProvider"
		class="org.springframework.security.authentication.RememberMeAuthenticationProvider">
  		<beans:property name="key" value="jsfspring-sec"/>
	</beans:bean>

	<beans:bean id="rememberMeFilter"
		class="org.springframework.security.web.authentication.rememberme.RememberMeAuthenticationFilter">
  		<beans:property name="rememberMeServices" ref="rememberMeServices"/>
  		<beans:property name="authenticationManager" ref="authenticationManager" />
	</beans:bean>
</beans:beans>

Second we will update our jsfspring-sec-security-config.xml and add authentication-provider.

jsfspring-sec-security-config.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans:beans
	xmlns:sec="http://www.springframework.org/schema/security"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:beans="http://www.springframework.org/schema/beans"
	xsi:schemaLocation="
	   http://www.springframework.org/schema/beans
	   http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
	   http://www.springframework.org/schema/security
	   http://www.springframework.org/schema/security/spring-security-3.1.xsd">

	 <sec:http auto-config="true" use-expressions="true">
		<sec:intercept-url pattern="/pages/secure/**" access="hasRole('ROLE_USER')" />
		<sec:intercept-url pattern="/pages/unsecure/**" access="permitAll"/>
		<sec:intercept-url pattern="/pages/common/**" access="permitAll"/>
		<sec:intercept-url pattern="/**" access="permitAll"/>
		<sec:form-login login-page="/pages/common/login.jsf"/>
		<sec:remember-me key="jsfspring-sec" services-ref="rememberMeServices"/>
		<sec:logout
			invalidate-session="true"
			delete-cookies="JSESSIONID,SPRING_SECURITY_REMEMBER_ME_COOKIE"
			logout-success-url="/pages/common/login.jsf"></sec:logout>
	</sec:http>

	<sec:authentication-manager alias="authenticationManager">
		<sec:authentication-provider ref="rememberMeAuthenticationProvider"></sec:authentication-provider>
		<sec:authentication-provider >
			<sec:user-service id="userDetailsService">
				<sec:user authorities="ROLE_USER" name="admin" password="admin" />
			</sec:user-service>
		</sec:authentication-provider>
	</sec:authentication-manager>

</beans:beans>

Third we need to update our login.xhtml to add remember me.

login.xhtml

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
	xmlns:h="http://java.sun.com/jsf/html"
	xmlns:f="http://java.sun.com/jsf/core"
	xmlns:ui="http://java.sun.com/jsf/facelets">
<h:head>
</h:head>
<h:body>
	<div align="right" style="">
		<h:form	id="loginFormId" prependId="false">
				<div id="loginFieldsPnlId">
					<div id="loginFieldUsrContId">
						<h:outputText id="outTxtUserNameId" value="Username:" name="outTxtUserNameNm"></h:outputText>
						<h:inputText id="userName" required="true" value="#{loginMgmtBean.userName}" requiredMessage="Please enter username"></h:inputText>
						<h:outputLabel id="outLblUserNameId" for="userName" name="outLblUserNameNm"></h:outputLabel>
					</div>
					<div id="loginFieldPassContId">
						<h:outputText id="outTxtPasswordId" value="Password:" name="outTxtPasswordNm"></h:outputText>
						<h:inputSecret id="password"  required="true" value="#{loginMgmtBean.password}" requiredMessage="Please enter password"></h:inputSecret>
						<h:outputLabel id="outLblPasswordId" for="password" name="outLblPasswordNm"></h:outputLabel>
					</div>
					<div id="loginFieldPassContId">
						<h:selectBooleanCheckbox id="rememberMe" value="#{loginMgmtBean.rememberMe}" label="Remember Me">Remember Me</h:selectBooleanCheckbox>
					</div>
				</div>
				<div id="loginBtnPanelId">
					<h:commandButton id="btnLoginId" value="Login" action="#{loginMgmtBean.login}" styleClass="loginPanelBtn"></h:commandButton>
					<h:commandButton id="btnCancelId" value="Cancel" action="#{loginMgmtBean.cancel}" styleClass="loginPanelBtn" immediate="true" update="loginFormId"></h:commandButton>
				</div>
		</h:form>
	</div>
	<div>
		<h:messages></h:messages>
	</div>
</h:body>
</html>

Fourth is to update our LoginBean.java to handle remember me during login and logout.

LoginBean.java

package com.mumz.jsfspringsec.beans;

import javax.faces.bean.ManagedBean;
import javax.faces.bean.ManagedProperty;
import javax.faces.bean.RequestScoped;
import javax.faces.context.FacesContext;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.RememberMeAuthenticationToken;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.web.authentication.RememberMeServices;

/**
 * The Class LoginBean.
 */
@ManagedBean(name = "loginMgmtBean")
@RequestScoped
public class LoginBean {

	/** The user name. */
	private String userName = null;

	/** The password. */
	private String password = null;

	/** The remember me. */
	private String rememberMe = null;

	/** The authentication manager. */
	@ManagedProperty(value = "#{authenticationManager}")
	private AuthenticationManager authenticationManager = null;

	/** The remember me services. */
	@ManagedProperty(value = "#{rememberMeServices}")
	private RememberMeServices rememberMeServices = null;

	@ManagedProperty(value="#{userDetailsService}")
	private UserDetailsService userDetailsService = null;
	/**
	 * Login.
	 *
	 * @return the string
	 */
	public String login() {
		try {
			Authentication result = null;
			if ("TRUE".equalsIgnoreCase(this.getRememberMe())) {
				UserDetails userDetails = userDetailsService.loadUserByUsername(getUserName());
				RememberMeAuthenticationToken rememberMeAuthenticationToken = new RememberMeAuthenticationToken(
						"jsfspring-sec", userDetails,
						userDetails.getAuthorities());
				HttpServletRequest httpServletRequest = (HttpServletRequest)FacesContext.getCurrentInstance().getExternalContext().getRequest();
				HttpServletResponse httpServletResponse = (HttpServletResponse) FacesContext.getCurrentInstance().getExternalContext().getResponse();
				rememberMeServices.loginSuccess(httpServletRequest, httpServletResponse, rememberMeAuthenticationToken);
				result = rememberMeAuthenticationToken;
			} else {
				Authentication request = new UsernamePasswordAuthenticationToken(
						this.getUserName(), this.getPassword());
				result = authenticationManager.authenticate(request);
			}
			SecurityContextHolder.getContext().setAuthentication(result);
		} catch (AuthenticationException e) {
			e.printStackTrace();
		}
		return "Secured";
	}

	/**
	 * Cancel.
	 *
	 * @return the string
	 */
	public String cancel() {
		return null;
	}

	/**
	 * Logout.
	 *
	 * @return the string
	 */
	public String logout() {
		SecurityContextHolder.clearContext();
		/**
		 * Delete Cookies
		 */
		HttpServletRequest httpServletRequest = (HttpServletRequest)FacesContext.getCurrentInstance().getExternalContext().getRequest();
		HttpServletResponse httpServletResponse = (HttpServletResponse) FacesContext.getCurrentInstance().getExternalContext().getResponse();
		Cookie cookie = new Cookie("SPRING_SECURITY_REMEMBER_ME_COOKIE", null);
		cookie.setMaxAge(0);
		cookie.setPath(httpServletRequest.getContextPath().length() > 0 ? httpServletRequest.getContextPath() : "/");
		httpServletResponse.addCookie(cookie);
		return "loggedout";
	}

	/**
	 * Gets the user name.
	 *
	 * @return the user name
	 */
	public String getUserName() {
		return userName;
	}

	/**
	 * Sets the user name.
	 *
	 * @param userName
	 *            the new user name
	 */
	public void setUserName(String userName) {
		this.userName = userName;
	}

	/**
	 * Gets the password.
	 *
	 * @return the password
	 */
	public String getPassword() {
		return password;
	}

	/**
	 * Sets the password.
	 *
	 * @param password
	 *            the new password
	 */
	public void setPassword(String password) {
		this.password = password;
	}

	/**
	 * Gets the remember me.
	 *
	 * @return the remember me
	 */
	public String getRememberMe() {
		return rememberMe;
	}

	/**
	 * Sets the remember me.
	 *
	 * @param rememberMe
	 *            the new remember me
	 */
	public void setRememberMe(String rememberMe) {
		this.rememberMe = rememberMe;
	}

	/**
	 * Gets the authentication manager.
	 *
	 * @return the authentication manager
	 */
	public AuthenticationManager getAuthenticationManager() {
		return authenticationManager;
	}

	/**
	 * Sets the authentication manager.
	 *
	 * @param authenticationManager
	 *            the new authentication manager
	 */
	public void setAuthenticationManager(
			AuthenticationManager authenticationManager) {
		this.authenticationManager = authenticationManager;
	}

	/**
	 * Gets the remember me services.
	 *
	 * @return the remember me services
	 */
	public RememberMeServices getRememberMeServices() {
		return rememberMeServices;
	}

	/**
	 * Sets the remember me services.
	 *
	 * @param rememberMeServices
	 *            the new remember me services
	 */
	public void setRememberMeServices(RememberMeServices rememberMeServices) {
		this.rememberMeServices = rememberMeServices;
	}

	public UserDetailsService getUserDetailsService() {
		return userDetailsService;
	}

	public void setUserDetailsService(UserDetailsService userDetailsService) {
		this.userDetailsService = userDetailsService;
	}
}

That’s it our remember me functionality is now implemented. Deploy, Run and let me know your comments.

16 thoughts on “Implementing Spring Security Remember Me with JSF 2.0

    Allan Wood said:
    October 4, 2012 at 5:17 PM

    I implemented my own userDetailService, where in your code should I put the reference to my userdetail service, I usually put it in the tag. But since you put a ref attribute into the tag, I couldn’t put my user-service-ref. What do I do?

    Thanks in advance.

    Victor said:
    November 16, 2012 at 9:36 PM

    I have next error after timeout “viewId:/pages/common/login.jsf – No se pudo restablecer la vista /pages/common/login.jsf.”. what’s the problem

    Tiago Peres said:
    June 11, 2013 at 2:51 AM

    I don’t get it, why there’s no password validation when the “Remember Me” check-box is checked?

      hcubyc said:
      October 12, 2013 at 5:54 AM

      There’s a password validation. Method login() catches
      AuthenticationException, but it doesn’t do much when it happens. If
      you want, you can return something in catch clause for instance
      “error” or “loginFailed” or do anything what do you really
      want.

      Spyros Non Serviam said:
      December 13, 2013 at 6:12 PM

      I have noticed this also but I found a solution. I moved
      the else part of the LoginBean before the if and it seems to be
      working properly.

    Rachid said:
    February 28, 2014 at 7:33 AM

    Thanks for this very nice topic,
    I used your code to create my own login, every thing is just right unless Remeber Me.
    It doesn’t work, i mean when i re-open the browser, i have to re login.

    Any ideas please ?

    Ramz said:
    March 30, 2014 at 11:55 PM

    I am getting a null pointer exception as there is no UserDetailsService refrence in bean configuration for the LoginBean. When i placed the below code in bean configuration, it worked. May be you should add it.

    However it is there under “rememberMeServices” bean id. Not sure if it is getting initialized.

      dbarrios83 said:
      April 3, 2014 at 12:22 AM

      I have the same problem? have you found any solution?

      sreejith said:
      June 19, 2014 at 2:43 PM

      Hi Ramz ,

      Please let me know how you fixed this null pointer exception in UserDetailsService reference .
      I am struck with this , getting this exception .

      Please help .

      Emil said:
      October 18, 2014 at 9:28 PM

      Hi, did you managed to resolve this issue, I’m stuck.

    Amy said:
    May 28, 2014 at 6:02 PM

    I tried to integrate the above code in my project and it does work. I am getting an error

    “Context initialization failed: org.springframework.beans.factory.BeanCreationException: Error creating bean with name ‘rememberMeServices’ defined in ServletContext resource [/WEB-INF/applicationContext.xml]: Cannot resolve reference to bean ‘userDetailsService’ while setting bean property ‘userDetailsService’; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named ‘userDetailsService’ is defined”

    Please let me know how to fix it. userDetailsService is a reference to an interface and it does not allow to create a bean for the same.

      Emil said:
      October 18, 2014 at 9:27 PM

      Hi, did you managed to resolve this issue (userDetailsService NPE), I’m stuck.

    Alex said:
    November 21, 2014 at 11:12 PM

    I don’t think this works with Spring 4.0.1.

    Cannot resolve reference to bean ‘userDetailsService’ while setting bean property ‘userDetailsService’; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named ‘userDetailsService’ is defined

    I use Spring 4.0.1, NetBeans 8.0.1 and deploy to WildFly 8.1.0

      Prabhat Jha responded:
      November 23, 2014 at 12:53 AM

      Yeah may be, I am working on getting all these samples ported to Spring 4 and that too with complete Maven Project. Should be there soon before Christmas I would say

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s