Google

May 7, 2013

Spring security pre-authentication scenario - Part1

Spring security pre-authentication scenario assumes that a valid authenticated user is available via  either Single Sign On (SSO) applications like Siteminder, Tivoli, etc or a X509 certification based authentication. The Spring security in this scenario will only be used for authorization.

The example shown below retrieves the user name via the HTTP headers.

Step 1:  The dependency jars that are required.

 <dependency>
   <groupId>org.springframework.security</groupId>
   <artifactId>spring-security-core</artifactId>
   <version>3.1.0.RELEASE</version>
  </dependency>

  <dependency>
   <groupId>org.springframework.security</groupId>
   <artifactId>spring-security-web</artifactId>
   <version>3.1.0.RELEASE</version>
  </dependency>

  <dependency>
   <groupId>org.springframework.security</groupId>
   <artifactId>spring-security-acl</artifactId>
   <version>3.1.0.RELEASE</version>
  </dependency>

  <dependency>
   <groupId>org.springframework.security</groupId>
   <artifactId>spring-security-config</artifactId>
   <version>3.1.0.RELEASE</version>
  </dependency>


Step 2: Define the Spring security filter via the web.xml file.

     ....

    <!-- The definition of the Root Spring Container shared by all Servlets and Filters -->
 <context-param>
  <param-name>contextConfigLocation</param-name>
  <param-value>/META-INF/spring/applicationContext.xml</param-value>
 </context-param>
 
 
 
 <!-- Spring Security -->
 <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>/myapp/*</url-pattern>
 </filter-mapping>
 
    ....
 
 


Step 3: The servlet filter configured above will make use of a spring context file like  ssoContext.xml to define the authorization sequences. The ssoContext file can be imported via the applicationContext.xml file bootstrapped via the web.xml file.

<?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:p="http://www.springframework.org/schema/p"
    xmlns:mvc="http://www.springframework.org/schema/mvc"
    xmlns:cache="http://www.springframework.org/schema/cache"
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:aop="http://www.springframework.org/schema/aop" 
    xmlns:jee="http://www.springframework.org/schema/jee" 
    xmlns:tx="http://www.springframework.org/schema/tx"
    xmlns:util="http://www.springframework.org/schema/util"
    xmlns:batch="http://www.springframework.org/schema/batch"
    xsi:schemaLocation="
            http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
            http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-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/aop http://www.springframework.org/schema/aop/spring-aop-3.1.xsd
            http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.1.xsd
            http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-3.1.xsd
            http://www.springframework.org/schema/cache http://www.springframework.org/schema/cache/spring-cache-3.1.xsd">
    
    <!-- Root Context: defines shared resources visible to all other web components -->

    <context:annotation-config />
    
    <import resource="myServerContext.xml" /> 
 <import resource="security/ssoContext.xml" /> 
    
</beans>


Step 4: The ssoContext.xml is defined below showing how the user can be retrieved from HTTP header SM_USER for site minder and passed to your own implementation to retrieve the roles (aka authorities). All the classes configured below are Spring classes except for the UserDetailsServiceImpl, which is used to retrieve the authorities.

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


    <context:component-scan base-package="com.myapp.dao.security" />
    <context:component-scan base-package="com.myapp.model.security" />

 <beans:bean id="springVoter"
  class="org.springframework.security.access.vote.RoleVoter" />
 <beans:bean id="springAccessDecisionManager"
  class="org.springframework.security.access.vote.AffirmativeBased">
  <beans:property name="allowIfAllAbstainDecisions"
   value="false" />
  <beans:property name="decisionVoters">
   <beans:list>
    <beans:ref local="springVoter" />
   </beans:list>
  </beans:property>
 </beans:bean>


    <http auto-config="false"   entry-point-ref="preAuthenticatedProcessingFilterEntryPoint">
      <security:custom-filter position="PRE_AUTH_FILTER" ref="siteminderFilter" />
      <intercept-url pattern="/**/details.csv*" access="ROLE_viewer, ROLE_standard, ROLE_senior" /> 
      <logout logout-url="/j_spring_security_logout" logout-success-url="https://smlogin-dev.myapp.net/siteminderagent/ssologout/Logout.html" invalidate-session="true" /> 
 </http> 
 
 
 <beans:bean id="preAuthenticatedProcessingFilterEntryPoint" class="org.springframework.security.web.authentication.Http403ForbiddenEntryPoint"/>

 <beans:bean id="siteminderFilter"
  class="org.springframework.security.web.authentication.preauth.RequestHeaderAuthenticationFilter">
  <beans:property name="principalRequestHeader" value="SM_USER" />
  <beans:property name="authenticationManager" ref="authenticationManager" />
  <beans:property name="exceptionIfHeaderMissing" value="false" />
 </beans:bean>

 <security:authentication-manager alias="authenticationManager">
  <security:authentication-provider
   ref="preauthAuthProvider" />
 </security:authentication-manager>

 <beans:bean id="preauthAuthProvider"
  class="org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationProvider">
  <beans:property name="preAuthenticatedUserDetailsService">
   <beans:bean id="userDetailsServiceWrapper"
    class="org.springframework.security.core.userdetails.UserDetailsByNameServiceWrapper">
    <beans:property name="userDetailsService" ref="myUserDetailsService" />
   </beans:bean>
  </beans:property>
 </beans:bean>

 <beans:bean id="myUserDetailsService" class="com.myapp.UserDetailsServiceImpl">
  <beans:property name="appCd" value="appName" />
 </beans:bean>
 

</beans:beans>


Step 5: Define the class UserDetailsServiceImpl class that needs to implement the Spring interface UserDetailsService and the required method   "public UserDetails loadUserByUsername(String username)". The returned model object  "UserDetails" is a Spring class as well.

package com.myapp.security;

import java.util.ArrayList;
import java.util.Collection;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Required;
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 UserDetailsServiceImpl implements UserDetailsService 
{
 final static Logger LOG = LoggerFactory.getLogger(UserDetailsServiceImpl.class);
 
 protected String  appCd;
 
 public UserDetailsServiceImpl()  {}

 @Required
 public void setAppCd(String appCd)
 {
  if ( appCd != null && appCd.length() > 0 )
           this.appCd = appCd;
 }
    
    // override
    public UserDetails loadUserByUsername(String username)
            throws UsernameNotFoundException, DataAccessException 
    {
      
         String role = "ROLE_viewer"  ;  //hard coded, in real life retrieved via database or LDAP
         GrantedAuthorityImpl au_impl = new GrantedAuthorityImpl(role);
         Collection<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>();
         authorities.add(au_impl);
         
         User usr = new User(username, "", true, true, true, true, authorities);
         return usr;
         
    }
}



The part-2 will cover annotating your Java methods and the URLs that intercept the calls to verify the roles (or authorities) returned for a given user against the roles allowed for a method or URL.


Labels: , ,

0 Comments:

Post a Comment

Subscribe to Post Comments [Atom]

<< Home