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: security, Spring, Spring security
0 Comments:
Post a Comment
Subscribe to Post Comments [Atom]
<< Home