Spring Security Siteminder Pre-authentication Example

So far we have learned about securing spring application using login form based security, custom user details security and many more such security related concepts. In this post, I am giving an example of scenario where use is already authenticated via any third party application or tool e.g. site minder which is very common interface between multiple applications in a group.

In this scenario, user has been pre-authenticated in any other application and get into your web application using site minder. Site minder sends a request header about pre-authenticated user which you can utilize to further authorize the user inside your application. You do not need to further authenticate the user, just verify user roles from database and provide appropriate access inside application.

Please keep in mind that site minder is only for example, in fact you can use any third party
application to get pre-authenticated user. Only request header will change in each case.

Lets follow the tutorial step by step.

Step 1) Maven dependency

I am using maven for runtime dependencies so giving pom.xml. If you are using ANT then download respective JARs and add them in class path.

<properties>
	<spring.version>3.0.5.RELEASE</spring.version>
	<jackson-mapper-asl.version>1.9.9</jackson-mapper-asl.version>
    <jaxb-api.version>2.2.7</jaxb-api.version>
  </properties>
  <dependencies>
    <!-- Spring 3 dependencies -->
	<dependency>
		<groupId>org.springframework</groupId>
		<artifactId>spring-core</artifactId>
		<version>${spring.version}</version>
		<scope>runtime</scope>
	</dependency>
	<dependency>
		<groupId>org.springframework</groupId>
		<artifactId>spring-web</artifactId>
		<version>${spring.version}</version>
		<scope>runtime</scope>
	</dependency>
	<dependency>
		<groupId>org.springframework</groupId>
		<artifactId>spring-webmvc</artifactId>
		<version>${spring.version}</version>
		<scope>runtime</scope>
	</dependency>
	<!-- Spring Security -->
	<dependency>
	    <groupId>org.springframework.security</groupId>
	    <artifactId>spring-security-core</artifactId>
	    <version>${spring.version}</version>
	    <type>jar</type>
	    <scope>runtime</scope>
	</dependency>
	<dependency>
	    <groupId>org.springframework.security</groupId>
	    <artifactId>spring-security-web</artifactId>
	    <version>${spring.version}</version>
	    <type>jar</type>
	    <scope>runtime</scope>
	</dependency>
	<dependency>
	    <groupId>org.springframework.security</groupId>
	    <artifactId>spring-security-config</artifactId>
	    <version>${spring.version}</version>
	    <type>jar</type>
	    <scope>runtime</scope>
	</dependency>
	<dependency>
	    <groupId>org.springframework.security</groupId>
	    <artifactId>spring-security-taglibs</artifactId>
	    <version>${spring.version}</version>
	    <type>jar</type>
	    <scope>runtime</scope>
	</dependency>
  </dependencies>

Step 2) Update web.xml file

There is nothing much in web.xml file. Just add context config location and spring security related filter mappings.

<web-app>
  <display-name>www.howtodoinjava.com</display-name>
  
  	<servlet>
		<servlet-name>spring-mvc</servlet-name>
		<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
		<load-on-startup>1</load-on-startup>
	</servlet>
 
	<servlet-mapping>
		<servlet-name>spring-mvc</servlet-name>
		<url-pattern>/*</url-pattern>
	</servlet-mapping>
 
	<context-param>
		<param-name>contextConfigLocation</param-name>
		<param-value>/WEB-INF/spring-mvc-servlet.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>
 
	<listener>
		<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
	</listener>
</web-app>

Step 3) Spring security configuration

This is most important step because here we will configure the pre authentication security related mappings. Lets look at the file:

<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:security="http://www.springframework.org/schema/security"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
    xmlns:mvc="http://www.springframework.org/schema/mvc"
    xmlns:aop="http://www.springframework.org/schema/aop"
    xsi:schemaLocation="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd
        http://www.springframework.org/schema/beans/ http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
        http://www.springframework.org/schema/context/ http://www.springframework.org/schema/context/spring-context-2.5.xsd
        http://www.springframework.org/schema/aop/ http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
        http://www.springframework.org/schema/security/ http://www.springframework.org/schema/security/spring-security-3.0.3.xsd">
     
     <!-- Annotation are configuring the application -->
     <mvc:annotation-driven/>
	
     <!-- Scan this package for all config annotations -->
	<context:component-scan base-package="com.howtodoinjava.web" />
	
	<security:http use-expressions="true" auto-config="false" entry-point-ref="http403EntryPoint">
    	<!-- Additional http configuration omitted -->
    	<security:intercept-url pattern="/**" access="hasRole('ROLE_USER')" />
    	<security:custom-filter position="PRE_AUTH_FILTER" ref="siteminderFilter" />
  	</security:http>

    <bean id="siteminderFilter" class="org.springframework.security.web.authentication.preauth.RequestHeaderAuthenticationFilter">
	    <property name="principalRequestHeader" value="SM_USER"/>
	    <property name="authenticationManager" ref="authenticationManager" />
	</bean>
	
  	<bean id="preauthAuthProvider" class="org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationProvider">
	    <property name="preAuthenticatedUserDetailsService">
	      	<bean id="userDetailsServiceWrapper"  class="org.springframework.security.core.userdetails.UserDetailsByNameServiceWrapper">
	        	<property name="userDetailsService" ref="customUserDetailsService"/>
	      	</bean>
	    </property>
    </bean>

    <security:authentication-manager alias="authenticationManager">
      	<security:authentication-provider ref="preauthAuthProvider" />
    </security:authentication-manager>	
    
    <bean id="customUserDetailsService" class="com.howtodoinjava.security.CustomUserDetailsService"></bean>
    <bean id="http403EntryPoint" class="org.springframework.security.web.authentication.Http403ForbiddenEntryPoint"></bean>
    
</beans>

Lets understand this configuration:

  1. mvc:annotation-driven” is used to tell spring that it needs to scan the annotations in base package specified in “context:component-scan” for searching the resource mappings.
  2. security:http” configuration specifies security related configuration and options. “use-expressions” tells that while matching the “access” property in “security:intercept-url”, expressions are allowed and should be parsed.
  3. security:custom-filter” specifies definition of a custom filter which will be invoked to verify user’s validity.
  4. PRE_AUTH_FILTER assures that this filter will be invoked before other authentication/authorization handling. I have defined a siteminder filter for this. You can name it to another name.
  5. principalRequestHeader” is important because it is the request header attribute which will be checked once user come to application from another application. So, ask this header from third party provider to integrate here.
  6. authenticationManager” is ultimately using “customUserDetailsService” which I have written in “com.howtodoinjava.security.CustomUserDetailsService” class. This class implements UserDetailsService interface and have one method loadUserByUsername(). This method must return a authenticated user interface of type “org.springframework.security.core.userdetails.UserDetails“. This object will have other authorization details such as user role, which will be used in further security.

Step 4) Write custom UserDetailsService class

This class will get the username passed from third party application and username is passed as request header e.g. in our case “SM_USER“.

package com.howtodoinjava.security;

import org.springframework.dao.DataAccessException;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.GrantedAuthorityImpl;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;

public class CustomUserDetailsService implements UserDetailsService
{
	public UserDetails loadUserByUsername(String username)
	        throws UsernameNotFoundException, DataAccessException
    {
		System.out.println("username recieved :: " + username);
		@SuppressWarnings("deprecation")
		
		//Get this user details from database and set its roles also here
		
		UserDetails user = new User(username, "password", true, true, true, true,
				new GrantedAuthority[]{ new GrantedAuthorityImpl("ROLE_USER") });
		return user;
    }
}

Step 5) Write secured resources to verify

I ave written two very basic classes for sake of simplicity. I will try to access them with and without request header “SM_USER“.

DemoController.java

package com.howtodoinjava.web;

@Controller
@RequestMapping("/users")
public class DemoController 
{
	@RequestMapping(method = RequestMethod.GET, value="/{id}", headers="Accept=application/xml")
	public @ResponseBody User getUserById(@PathVariable String id) 
	{
		User user = new User();
		user.setFirstName("john");
		user.setLastName("adward");
		return user;
	}
	
	@RequestMapping(method = RequestMethod.GET,  headers="Accept=application/xml")
	public @ResponseBody Users getAllUsers() 
	{
		User user1 = new User();
		user1.setFirstName("john");
		user1.setLastName("adward");
		
		User user2 = new User();
		user2.setFirstName("tom");
		user2.setLastName("hanks");
		
		Users users = new Users();
		users.setUsers(new ArrayList<User>());
		users.getUsers().add(user1);
		users.getUsers().add(user2);
		
		return users;
	}
}

Users.java

@XmlRootElement(name="users")
@XmlAccessorType(XmlAccessType.NONE)
public class Users 
{
	@XmlElement(name="user")
	private Collection<User> users;

	public Collection<User> getUsers() {
		return users;
	}

	public void setUsers(Collection<User> users) {
		this.users = users;
	}
}

User.java

@XmlRootElement(name="user")
@XmlAccessorType(XmlAccessType.NONE)
public class User {
	
	@XmlElement(name="first-name")
	private String firstName;
	
	@XmlElement(name="last-name")
	private String lastName;
	
	public String getFirstName() {
		return firstName;
	}
	public void setFirstName(String firstName) {
		this.firstName = firstName;
	}
	public String getLastName() {
		return lastName;
	}
	public void setLastName(String lastName) {
		this.lastName = lastName;
	}
}

Step 6) Demo

Lets deploy the application in tomcat server and test it.

Case 1 :: Without “SM_USER” request header

This will throw following exception:

org.springframework.security.web.authentication.preauth.PreAuthenticatedCredentialsNotFoundException: SM_USER header not found in request.
	at org.springframework.security.web.authentication.preauth.RequestHeaderAuthenticationFilter.getPreAuthenticatedPrincipal(RequestHeaderAuthenticationFilter.java:43)
	at org.springframework.security.web.authentication.preauth.AbstractPreAuthenticatedProcessingFilter.doAuthenticate(AbstractPreAuthenticatedProcessingFilter.java:98)
	at org.springframework.security.web.authentication.preauth.AbstractPreAuthenticatedProcessingFilter.doFilter(AbstractPreAuthenticatedProcessingFilter.java:86)
	at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:380)
	at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:105)
	at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:380)
	at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:79)
	at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:380)
	at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:169)
	at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:237)
Spring security pre-authentication error
Spring security pre-authentication error

Case 2 :: With “SM_USER” request header

This time user will be able to access the resource.

Spring security pre-authentication success
Spring security pre-authentication success

To download the sourcecode of above tutorial, please follow below download link.

Sourcecode Download[/download]

Happy Learning !!

Comments

Subscribe
Notify of
guest
42 Comments
Most Voted
Newest Oldest
Inline Feedbacks
View all comments

About Us

HowToDoInJava provides tutorials and how-to guides on Java and related technologies.

It also shares the best practices, algorithms & solutions and frequently asked interview questions.

Our Blogs

REST API Tutorial

Dark Mode

Dark Mode