Reusing Persistent Token Mechanism of Spring Security

Spring Security Framework exists in my enterprise application development tool suite for ages. Over years it has evolved a lot and become a much more reusable and extendable framework for various security needs.

Recently, one of my clients came up with an interesting requirement. They are developing a mobile client for their enterprise web application, and want to make their client to communicate with server side via REST based web service calls. However, after login requests, they don’t want to continue with providing credentials information for every subsequent request. Instead they asked if a token could be used to indicate client is already authenticated. That token would be generated during authentication process, returned to the client, and client will place it into further requests and so on. In order to improve security, they also asked about changing token for each request, so that in case another user obtains the token during the client-server interaction, system should detect theft attempts immediately. Tokens will be valid for several weeks and after that period, they will expire, and user will be made to login again.

After listening and discussing about those requirements, instead of creating a brand new solution, I thought about employing Spring Security Framework’s remember-me authentication. Remember-me authentication basicly does same token management task as well. During form login process, user tells remember-me module to remember himself when browser is closed and reopened. That way, user won’t be asked credentials again and again. Spring Security handles this by using cookies. Remember me authentication information is sent to client after successful authentication, and stored as a cookie in the client. When user opens his browser and types in the application’s one of secure urls in the location bar, Spring Security won’t redirect him to login page, but let the user to access the requested page according to his granted authorities of course. Spring Security’s remember me service has two forms; one is “TokenBasedRememberMeServices” and the second is “PersistentTokenBasedRememberMeServices“. Both use cookie mechanism, but second one is more secure as it never sends username information to client side and token is regenerated for each subsequent request.

The main difference in our implementation was that we carried credentials and token information in http request and response headers. Spring Security provides convenient methods in order to extract such information from request, and let us to set token into response again instead of storing it into cookies.

public class TokenBasedAuthenticationFilter extends
	UsernamePasswordAuthenticationFilter {

	protected String obtainPassword(HttpServletRequest request) {
		return request.getHeader(SPRING_SECURITY_FORM_PASSWORD_KEY);
	}

	protected String obtainUsername(HttpServletRequest request) {
		return request.getHeader(SPRING_SECURITY_FORM_USERNAME_KEY);
	}
}

Our TokenBasedAuthenticationFilter class extends from UsernamePasswordAuthenticationFilter and looks for credentials in http request headers.

<bean id="authenticationProcessingFilter"   class="org.speedyframework.security.web.TokenBasedAuthenticationFilter">
    <property name="authenticationManager" ref="authenticationManager"/>
    <property name="rememberMeServices" ref="rememberMeServices"/>
    <property name="authenticationSuccessHandler" ref="authenticationSuccessHandler"/>
    <property name="authenticationFailureHandler" ref="authenticationFailureHandler"/>
    <property name="postOnly" value="false"/>
    <property name="allowSessionCreation" value="false"/>
</bean>

<bean id="authenticationSuccessHandler" class="org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler">
    <property name="defaultTargetUrl">
        <value>${defaultTargetUrl}</value>
    </property>
    <property name="alwaysUseDefaultTargetUrl" value="true"/>
</bean>

<bean id="authenticationFailureHandler" class="org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler">
    <property name="defaultFailureUrl">
        <value>${authenticationFailureUrl}</value>
    </property>
</bean>

As you see from the bean definition, we don’t allow session creation, and also accept GET methods for http requests. Therefore, we are able to handle following call made using RestTemplate.

RestTemplate restTemplate = new RestTemplate();
HttpHeaders requestHeaders = new HttpHeaders();
requestHeaders.set(UsernamePasswordAuthenticationFilter.SPRING_SECURITY_FORM_USERNAME_KEY, "user3");
requestHeaders.set(UsernamePasswordAuthenticationFilter.SPRING_SECURITY_FORM_PASSWORD_KEY, "user3");
requestHeaders.set(AbstractRememberMeServices.DEFAULT_PARAMETER, "true");

HttpEntity<?> requestEntity = new HttpEntity(requestHeaders);

HttpEntity response = restTemplate.exchange("http://localhost:8080/test-token/j_spring_security_check",
HttpMethod.GET, requestEntity, String.class);

On the server side, when authentication is performed, and when subsequent requests with remember-me cookie are received, our RememberMeServices generates token, puts into http response, and extracts it from request etc, but instead of creating and processing cookie, it uses headers in the process. Thanks to improvements in Spring Security 3, it was very easy to just override some specific methods in RememberMeServices classes and perform necessary actions.

public class RequestHeaderCheckingPersistentTokenBasedRememberMeServices extends PersistentTokenBasedRememberMeServices {
	public RequestHeaderCheckingPersistentTokenBasedRememberMeServices() throws Exception {
	super();
	}

	protected boolean rememberMeRequested(HttpServletRequest request, String parameter) {
		String value = request.getHeader(DEFAULT_PARAMETER);
		return value != null && Boolean.parseBoolean(value)?Boolean.parseBoolean(value):super.rememberMeRequested(request, parameter);
	}

	protected String extractRememberMeCookie(HttpServletRequest request) {
		String cookieValue = request.getHeader(SPRING_SECURITY_REMEMBER_ME_COOKIE_KEY);
		return cookieValue;
	}

	protected void setCookie(String[] tokens, int maxAge, HttpServletRequest request, HttpServletResponse response) {
		String cookieValue = encodeCookie(tokens);
		response.setHeader(SPRING_SECURITY_REMEMBER_ME_COOKIE_KEY, cookieValue);
	}
}

<bean id="rememberMeServices" class="org.speedyframework.security.web.RequestHeaderCheckingPersistentTokenBasedRememberMeServices">
    <property name="key" value="${tokenBasedRememberMeServicesKey}"/>
    <property name="userDetailsService" ref="userDetailsService"/>
    <property name="tokenRepository" ref="tokenRepository"/>
</bean>

<bean id="tokenRepository" class="org.springframework.security.web.authentication.rememberme.JdbcTokenRepositoryImpl">
    <property name="createTableonstartup" value="false"/>
    <property name="dataSource" ref="dataSource"/>
</bean>

 

And we perform subsequent http requests for secured url by placing the token into http request headers as follows;

String token = response.getHeaders().getFirst(AbstractRememberMeServices.SPRING_SECURITY_REMEMBER_ME_COOKIE_KEY);

requestHeaders = new HttpHeaders();
requestHeaders.set(AbstractRememberMeServices.SPRING_SECURITY_REMEMBER_ME_COOKIE_KEY, token);
requestEntity = new HttpEntity(requestHeaders);

response = restTemplate.exchange("http://localhost:8080/test-token/mvc/secure-url",
HttpMethod.GET, requestEntity, String.class);

I hope, that example gives you a general idea about how Spring Security Framework is flexible and extendable to handle such changes in enterprise security requirements. In our public and private trainings, we teach everything about Spring Security Framework and discuss about enterprise web application security requirements in detail. You can also contact with us if you have hard to solve security problems either in general or specific to Spring Security Framework.