These scenarios are referred to as "pre-authenticated" as the user has been reliably authenticated prior to accessing the application. Spring Security provides a number of classes to support pre-authentication such as the PreAuthenticatedAuthenticationProvider class. This post demonstrates how to convert a web application based on Spring Security authentication to one that uses Container based authentication.
<sec:authentication-manager alias="authenticationManager">
<sec:authentication-provider ref='preAuthenticatedAuthenticationProvider'/>
</sec:authentication-manager>
<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"/>
As Spring Security will no longer be authenticating the user, we need to modify our login page to target the standard j_security_check URI instead of the Spring j_spring_security_check.
We also need to specify the security constraints within the web.xml such as form-based login and the security role names.
Note that although we will be using Spring Security for specifying our access control rules, we still need to specify some access control within the web.xml so that the JEE container knows when to request authentication from the user.
<filter>
<!-- <filter-name>springSecurityFilterChain</filter-name> -->
<filter-name>filterChainProxy</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
<!-- <filter-name>springSecurityFilterChain</filter-name> -->
<filter-name>filterChainProxy</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<!-- for container based authentication -->
<login-config>
<auth-method>FORM</auth-method>
<realm-name>file</realm-name>
<form-login-config>
<form-login-page>/login</form-login-page>
<form-error-page>/login</form-error-page>
</form-login-config>
</login-config>
<security-role>
<role-name>ROLE_CONSUMER_SUPPORT</role-name>
</security-role>
<security-role>
<role-name>ROLE_CONSUMER_SUPPORT_READ_ONLY</role-name>
</security-role>
<security-constraint>
<web-resource-collection>
<web-resource-name>All areas</web-resource-name>
<url-pattern>/users/*</url-pattern>
</web-resource-collection>
<auth-constraint>
<role-name>ROLE_CONSUMER_SUPPORT_READ_ONLY</role-name>
<role-name>ROLE_CONSUMER_SUPPORT</role-name>
</auth-constraint>
</security-constraint>
See the source code of Spring Security Config's
HttpSecurityBeanDefinitionParser#registerFilterChainProxyIfNecessary
method to understand how springSecurityFilterChain is used as the default bean id for the FilterChainProxy
when using the http security namespace.So which filters are required to configure the FilterChainProxy for this container based pre-authenticated scenario?
<bean id="filterChainProxy" class="org.springframework.security.web.FilterChainProxy">
The above shows five filters that have been specified in the Spring Security pre-auth sample. Lets see what each one is responsible for.
<sec:filter-chain-map path-type="ant">
<sec:filter-chain pattern="/**" filters="sif,j2eePreAuthFilter,logoutFilter,etf,fsi"/>
</sec:filter-chain-map>
</bean>
<bean id="sif" class="org.springframework.security.web.context.SecurityContextPersistenceFilter"/>
<bean id="j2eePreAuthFilter" class="org.springframework.security.web.authentication.preauth.j2ee.J2eePreAuthenticatedProcessingFilter">
<property name="authenticationManager" ref="authenticationManager"/>
<property name="authenticationDetailsSource">
<bean class="org.springframework.security.web.authentication.preauth.j2ee.J2eeBasedPreAuthenticatedWebAuthenticationDetailsSource">
<property name="mappableRolesRetriever">
<bean class="org.springframework.security.web.authentication.preauth.j2ee.WebXmlMappableAttributesRetriever" />
</property>
<property name="userRoles2GrantedAuthoritiesMapper">
<bean class="org.springframework.security.core.authority.mapping.SimpleAttributes2GrantedAuthoritiesMapper">
<property name="convertAttributeToUpperCase" value="true"/>
</bean>
</property>
</bean>
</property>
</bean>
SecurityContextPersistenceFilter
is responsible for populating the SecurityContextHolder
and is required to be the first filter to execute.The J2eePreAuthFilter is what retrieves the JEE user principle name and associated roles for the pre-authenticated principle. As the container has performed the authentication, the user principle is available with associated roles and these are mapped to Spring's
GrantedAuthority
instances.
<bean id="logoutFilter" class="org.springframework.security.web.authentication.logout.LogoutFilter">
The
<constructor-arg value="/"/>
<constructor-arg>
<list>
<bean class="org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler"/>
</list>
</constructor-arg>
</bean>
LogoutFilter
will invoke the configured list of LogoutHandler
implementations and then direct the user to the url specified in the first argument. It is also possible to provide a LogoutSuccessHandler
to implement any custom logic after a successful logout. The LogoutHandler
's are responsible for implementing the actual logout behaviour such as invalidating the session.
<bean id="preAuthenticatedProcessingFilterEntryPoint" class="org.springframework.security.web.authentication.Http403ForbiddenEntryPoint"/>
The
<bean id="etf" class="org.springframework.security.web.access.ExceptionTranslationFilter">
<property name="authenticationEntryPoint" ref="preAuthenticatedProcessingFilterEntryPoint"/>
</bean>
ExceptionTranslationFilter
is responsible for handling any AccessDeniedException
and AuthenticationException
's thrown within the filter chain and mapping them to HTTP responses. It will delegate to the authenticationEntryPoint on an AuthenticationException
. In the pre-authenticated scenario, the authenticationEntryPoint will simply return the HTTP error code 403 rather than commence the re-authentication process.
<bean id="roleVoter" class="org.springframework.security.access.vote.RoleVoter"/>
The final filter in the chain is the
<bean id="httpRequestAccessDecisionManager" class="org.springframework.security.access.vote.AffirmativeBased">
<property name="allowIfAllAbstainDecisions" value="false"/>
<property name="decisionVoters">
<list>
<ref bean="roleVoter"/>
</list>
</property>
</bean>
<bean id="fsi" class="org.springframework.security.web.access.intercept.FilterSecurityInterceptor">
<property name="authenticationManager" ref="authenticationManager"/>
<property name="accessDecisionManager" ref="httpRequestAccessDecisionManager"/>
<property name="securityMetadataSource">
<sec:filter-invocation-definition-source>
<sec:intercept-url pattern="/secure/extreme/**" access="ROLE_SUPERVISOR"/>
<sec:intercept-url pattern="/secure/**" access="ROLE_USER"/>
<sec:intercept-url pattern="/**" access="ROLE_USER"/>
</sec:filter-invocation-definition-source>
</property>
</bean> FilterSecurityInterceptor
that implements the access control rules for HTTP resources similar to the ones defined in the HTTP security namespace. The FilterSecurityInterceptor will first check if authentication is required, and then ask the accessDecisionManager to decide whether to grant access to a request resource. The AccessDecisionManager collaborates with the configured list of AccessDecisionVoters to resolve the authorization request.These filters and some changes to the web.xml and login, logout forms enable transitioning a web application based on Spring Security authentication to one that uses container based authentication.
Finally, depending on the application server used, there will be some configuration required for mapping the JEE security roles.
<?xml version="1.0" encoding="UTF-8"?>
glassfish-web.xml
<!DOCTYPE glassfish-web-app PUBLIC "-//GlassFish.org//DTD GlassFish Application Server 3.1 Servlet 3.0//EN" "http://glassfish.org/dtds/glassfish-web-app_3_0-1.dtd">
<glassfish-web-app>
<context-root>preauth</context-root>
<security-role-mapping>
<role-name>ROLE_USER</role-name>
<group-name>users</group-name>
</security-role-mapping>
<security-role-mapping>
<role-name>ROLE_SUPERVISOR</role-name>
<group-name>managers</group-name>
</security-role-mapping>
</glassfish-web-app>
No comments:
Post a Comment