Spring-Security3 PreAuthentication + JSF2

Spring Security (formerly Acegi Security) is for sure one of the most flexible framework to address security+authentication for a real-life web application. Its pluggable mechanism offers an easy path to achieve security in a large number of contexts.

Recently I decided to use it for a webapp that will be deployed and used by an organization which use a custom SSO strategy.

From the webapp perspective this means that we don’t have to authenticate users, this was already done by the SSO framework. What the webapp need to know is basically who is the user and which role he play in the webapp domain.

The “which” part is essentially a mapping of the organization-wide user grants to the webapp specific (three) roles.

Spring Security offers a rich set of ready-made tools for the “pre-authenticated” scenario (link). Unfortunately I didn’t found any real life example showing how to use these tools. So here comes this article.

First of all the web.xml…

...
<context-param>
  <param-name>contextConfigLocation</param-name>
  <param-value>classpath*:applicationContext.xml, 
               classpath*:applicationContext-security.xml</param-value>
</context-param>

<filter>
  <filter-name>springSecurityFilterChain</filter-name>
  <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>

<filter-mapping>
  <filter-name>springSecurityFilterChain</filter-name>
  <url-pattern>/*</url-pattern>
</filter-mapping>
...

Here you have to customize two things:

  • the contextConfigLocation value to meet your configuration setup (in the example above there are two separate files, one for the generic Spring beans configuration, another for the security configuration only)
  • the url-pattern for the filter mapping that need to address your specific security requirement. (in the example all pages will be “secured”)

Now, let’s see the applicationContext-security.xml….

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xmlns:context="http://www.springframework.org/schema/context"
  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/context http://www.springframework.org/schema/context/spring-context-3.1.xsd
    http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-3.1.xsd">

<!-- <sec:debug/> --> <!-- Uncomment this to have verbose debug informations -->

  <sec:http realm="My Realm" auto-config='true' create-session="ifRequired" disable-url-rewriting="true">
    <sec:intercept-url pattern="/**" access="ROLE_USER"/>    
    <sec:custom-filter ref="myPreAuthFilter" position="PRE_AUTH_FILTER"/>    
    <sec:session-management session-fixation-protection="newSession"/>
  </sec:http>

  <sec:authentication-manager alias="authenticationManager">
    <sec:authentication-provider ref='preAuthenticatedAuthenticationProvider'/>
  </sec:authentication-manager>

  <bean id="myPreAuthFilter" class="mypackage.MyPreAuthenticatedProcessingFilter">
    <property name="authenticationManager" ref="authenticationManager"/>
    <property name="authenticationDetailsSource" ref="authenticationDetailsSource"/>
    <property name="continueFilterChainOnUnsuccessfulAuthentication" value="false"/>
  </bean>

  <bean id="authenticationDetailsSource" class="mypackage.MyAuthenticationDetailsSource" />

  <bean id="authenticationManager" class="org.springframework.security.authentication.ProviderManager">
    <constructor-arg>
      <list>
        <ref bean="preAuthenticatedAuthenticationProvider"/>
      </list>
    </constructor-arg>
  </bean>

  <bean id="preAuthenticatedAuthenticationProvider" class="org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationProvider">
    <property name="preAuthenticatedUserDetailsService" ref="preAuthenticatedUserDetailsService"/>
  </bean>
  <bean id="preAuthenticatedUserDetailsService" class="org.springframework.security.web.authentication.preauth.PreAuthenticatedGrantedAuthoritiesUserDetailsService"/>
</beans>

Let’s see the most important things …

  • the sec:http part configure the relevant things in our secure  realm, inside this we are saying that
  • we want to intercept access to every page, including sub-folders (/**), and make them accessible to users with role “ROLE_USER” (which in our scenario means to every SSO authenticated user) and
  • we want to place our pre-authenticated filter in the Spring-Security filters-chain in the appropriate position (more here)

The rest of the file says that we want to use a certain authenticationManager, a custom authenticationFilter and a custom authenticationDetailSource.

The easier way to understand what those filter and detail source do it’s to look to their source:

public class MyPreAuthenticatedProcessingFilter extends AbstractPreAuthenticatedProcessingFilter{

  @Override
  protected Object getPreAuthenticatedPrincipal(HttpServletRequest request) {
    MyUser user = null;
      try {       
        // Here you have to extract the user from the request.
        // This is SSO framework dependant.
        user = customMethodToGetTheUserFromTheRequest(request);
      } catch (MyException e) {
        throw new AuthenticationServiceException("Error....", e);
      }

    return user; //User must implement java.security.Principal
  }

  @Override
  protected Object getPreAuthenticatedCredentials(HttpServletRequest request) {
    //Normally this should return the password or any other credential
    return "N/A";
  }
}
public class MyAuthenticationDetailsSource implements AuthenticationDetailsSource {

	@Override
	public GrantedAuthoritiesContainer buildDetails(HttpServletRequest request) {
		MyUser user = customMethodToGetTheUserFromTheRequest(request);

		List gal = new ArrayList();
		try{
			GrantedAuthority ga = null;
			if (user.isMemeberOfTheOrganization()){
				ga = new SimpleGrantedAuthority(Roles.ROLE_USER);
				gal.add(ga);
			}
			if (user.isMemberOfTheATeam()){
				ga = new SimpleGrantedAuthority(Roles.ROLE_MASTER);
				gal.add(ga);
			}
			if (user.isMemberOfTheBTeam()){
				ga = new SimpleGrantedAuthority(Roles.ROLE_ADMIN);
				gal.add(ga);
			}
		} catch (MyException e) {
			throw new AuthenticationServiceException("Error..", e);
		}

		return new PreAuthenticatedGrantedAuthoritiesWebAuthenticationDetails(request, gal);
	}
}

The two classes are almost self-explicative. The first one retrieve/build the webapp user reading the SSO data from the request.
The second maps the organizational user roles to the webapp user roles.

That’s all 🙂