Introduction
Spring Security is a Java/J2EE framework that provides advanced security features for the enterprise application. This framework was started as an "Acegi Security Framework", later adopted by Spring as its subproject "Spring Security". Spring Security targets two areas namely, Authentication and Authorization. This article explains the concepts of Spring Security and how it is modeled in the framework. This article focuses only on namespace configuration introduced in Spring 2.0. This article assumes that the reader knows the basics of Java and Spring. (This article does not cover all the implementations of the supporting classes, it covers only default implementations that are used by namespace configuration. It also does not cover all the authentication models such as LDAP, CAS. It only covers HTTP Form Based Authentication. In Authorization, it does not cover authorizing access to individual domain object instances such as ACL.)
Spring Security Concepts
Spring Security works around two core areas of security, Authentication and Authorization.
"Authentication" is the assurance that the user is actually the user he is claiming to be, for example, when the user logs into any application and gives his credentials, he authenticates himself. At the authentication level, spring supports various authentication models such as Http Basic authentication, Form Based authentication.
"Authorization" is the assurance that the user is allowed to access only those resources that he is authorized to use. For example, in a corporate application, there are some parts of an application where only admin have access and to some parts all the employees have access. These access rules are determined by the access rights given to each user of the system. At the authorization level, spring targets three main areas: authorizing web request, authorizing whether methods can be invoked and authorizing access to individual domain object instances.
Getting Started
To get started with the implementation, following jars need to be present in the class path of the project.
- Core - spring-security-core.jar
- Web - spring-security-web.jar
- Config - spring-security-config.jar
Namespace Configuration
The namespace configuration of the spring provides lot of shortcuts that hides much of the complexity of the framework. To start with this configuration, define a security filter in web.xml as shown below:
<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>
In the above configuration, DelegatingFilterProxy
delegates the control to a filter implementation which is defined as a bean named springSecurityFilterChain
. This bean is an infrastructure internal bean to handle namespace configurations. Once this configuration is done, all the incoming requests enter the spring framework for security checks.
Security Configuration
The security configuration is done in XML file and can have any name such as applicationContext-security.xml. This file needs to be loaded explicitly from web.xml. This is done by adding ContextLoadListener
. The following lines needs to be added before security filter definition in web.xml.
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>WEB-INF/applicationContext-security.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.
ContextLoaderListener</listener-class>
</listener>
applicationContext-security.xml
When the namespace configuration is used, spring-config.jar needs to be present in the classpath. The first line in this XML file is the schema definition as shown below:
<beans xmlns="http://www.springframework.org/schema/security"
xmlns:bean="http://www.springframework.org/schema/beans"
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.0.xsd
http://www.springframework.org/schema/security
http://www.springframework.org/schema/security/spring-security-3.0.3.xsd">
...
</beans>
The minimal namespace configuration looks like:
<http auto-config='true'>
<intercept-url pattern="/**" access="ROLE_USER" />
</http>
<authentication-manager>
<authentication-provider>
<user-service>
<user name="testadmin" password="testadminpassword"
authorities="ROLE_USER, ROLE_ADMIN" />
<user name="testuser" password="testuserpassword" authorities="ROLE_USER" />
</user-service>
</authentication-provider>
</authentication-manager>
The above configuration declares that all the urls in the application will be intercepted for the security checks and the urls can only be accessed by the user with role ROLE-USER
. The attribute "auto-config=true
" defines three elements <form-login/>
, <http-basic/>
and <logout>
.The default configuration always chooses http-basic authentication model. If the model needs to be changed to the form-login model, then the following configuration is needed.
<http auto-config='true'>
<intercept-url pattern="/login.jsp*" access="IS_AUTHENTICATED_ANONYMOUSLY"/>
<intercept-url pattern="/**" access="ROLE_USER" />
<form-login login-page='/login.jsp'/></http>
This configuration is done to enable form-login authentication model where the login page is login.jsp. Note that in the intercept tag, pattern for login.jsp is given and access rule is defined as IS_AUTHENTICATED_ANONYMOUSLY
. That means login.jsp is not checked for security, which makes sense as login.jsp is the starting point from where the user is authenticated.
The tag <authentication-manager>
processes the authentication information; <authentication-provider>
defines the credential information and the roles given to each user (authentication information).
The above configuration defines the very minimalistic approach for security. But, when it needs customization according to business requirements, it is very important to understand what happens internally. Spring Security framework is a chain of filters, with each filter having certain responsibility. The next section opens the namespace configuration to bean configuration to understand the flow and responsibility of each filter.
Namespace Configuration to Bean configuration
The <http>
block in namespace configuration invokes the chain of filters. DelegatingFilterProxy
that is defined in web.xml invokes the FilterChainProxy
class which in turn invokes the chain of filters defined for each URL pattern.
The chain of filters that FilterChainProxy
calls are shown below:
To define the FilterChainProxy
in the applicationContext-security.xml, a bean needs to be defined. This bean can have any name; the only thing to remember is that it should be an alias for bean springSecurityFilterChain
defined in web.xml. Change the schema definition to make bean as default.
<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"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/security
http://www.springframework.org/schema/security/spring-security-3.0.3.xsd">
...
</beans>
To define the alias:
<alias name="filterChainProxy" alias="springSecurityFilterChain"/>
The definition of filter chain proxy:
<bean id="filterChainProxy"
class="org.springframework.security.web.FilterChainProxy">
<security:filter-chain-map path-type="ant">
<security:filter-chain pattern="/login.jsp*" filters="none"/>
<security:filter-chain pattern="/**" filters="
securityContextFilter, logoutFilter, formLoginFilter, requestCacheFilter,
servletApiFilter, anonFilter, sessionMgmtFilter,
exceptionTranslator, filterSecurityInterceptor" />
</security:filter-chain-map> </bean>
The above configuration declares that for URL pattern login.jsp, no filters should be applied and, for all other URL patterns, the filters as shown above should be applied. All these beans should be defined as a bean in the configuration file. All the filters defined in the above configuration are invoked in the same order in which they are defined.
SecurityContextPersistenceFilter (org.springframework.security.web.context)
This filter will be executed only once per request. This filter loads the existing context from the SecurityContextRepository
before the request goes to the authentication process; if the context does not exist then it will create a new context. Context
is an instance of SecurityContext
class which stores the details of the authentication such as credential information, authority assigned to the user. All this information is wrapped in an Authentication
object. SecurityContextRepository
is a class that holds the context, this information can be stored in the session or in the database or anywhere according to the requirements. The default configuration is HTTP session using HttpSessionSecurityContextRepository
. If it needs any customization, then a custom repository can be written by implementing SecurityContextRepository
. Once the request is completely processed, filter saves the context information again to the repository. The class diagram looks like:
The configuration of SecurityContextPersistenceFilter
is shown below:
<bean id="securityContextFilter"
class="org.springframework.security.web.context.SecurityContextPersistenceFilter">
<property name="securityContextRepository" ref=" securityContextRepository "/>
</bean>
<bean id="securityContextRepository"
class="org.springframework.security.web.context.
HttpSessionSecurityContextRepository" />
LogoutFilter (org.springframework.security.web.authentication.logout)
This filter is responsible for logging out the user. By default, it invokes only one LogoutHandler
i.e., SecurityContextLogoutHandler
which clears out the SecurityContext
and also invalidates the session if this operation is allowed. Custom handlers can be written by implementing LogoutHandler
interface.
The class diagram looks like:
The configuration of LogoutFilter
is shown below:
<bean id="logoutFilter" class="org.springframework.
security.web.authentication.logout.LogoutFilter">
<constructor-arg value="/logged_out.htm"/>
<constructor-arg> <list>
<bean class="org.springframework.security.web.
authentication.logout.SecurityContextLogoutHandler"/></list>
</constructor-arg>
</bean>
UsernamePasswordAuthenticationFilter (org.springframework.security.web.authentication)
This filter performs the authentication process. In the namespace configuration when the form-based authentication is configured, UsernamePasswordAuthenticationFilter
is called by default. The name of the parameters it requires and the URL it listens to are all configurable by setting the properties of this class. This filter also requires the AuthenticationManager
to be set for doing the authentication process. The possible outcomes of the authentication process are either the user is successfully authenticated or not.
On successful authentication, SavedRequestAwareAuthenticationSuccessHandler
is called by default. This handler first checks the original request from the client that is stored in RequestCache
and redirects the request to that URI. If "alwaysUseDefaultTargetUrl
" property is set, then on successful authentication it is redirected to the default URL. For setting customized URL "targetUrlParameter
" property needs to be set. Custom success handler can be written by implementing AuthenticationSuccessHandler
interface.
On authentication failure, SimpleUrlAuthenticationFailureHandler
is called by default. This handler will check if the property "defaultFailureUrl
" is set, if it is set then it will redirect to that URL otherwise it will send 401 response to the client. 401 stands for unauthorized request. There is also a subclass of this handler ExceptionMappingAuthenticationFailureHandler
, by using it the exception type is mapped to the URL it should be redirected to. For this, exceptionMapping
properties file should be present in a classpath.
The class diagram looks like:
The configuration of UsernamePasswordAuthenticationFilter
is shown below:
<bean id="formLoginFilter"
class="com.tcs.integrated.internals.security.UsernamePasswordAuthenticationFilter">
<property name="authenticationManager" ref="authenticationManager"/>
<property name="filterProcessesUrl" value="/j_spring_security_check"/>
<property name="usernameParameter" value="username "/>
<property name="passwordParameter" value="password"/>
<property name="authenticationSuccessHandler">
<bean class="
org.springframework.security.web.authentication.
SavedRequestAwareAuthenticationSuccessHandler ">
<property name="alwaysUseDefaultTargetUrl" value="true"/>
<property name="defaultTargetUrl" value="/success.jsp"/>
</bean>
</property>
<property name="authenticationFailureHandler">
<bean class=" org.springframework.security.web.
authentication.SimpleUrlAuthenticationFailureHandler "/>
</property>
</bean>
In the above configuration, UsernamePasswordAuthenticationFilter
is used. This filter will listen to j_spring_security_check
for authentication. On successful authentication, SavedRequestAwareAuthenticationSuccessHandler
is called which will redirect the user to success.jsp.
AuthenticationManager
AuthenticationManager
processes the authentication request. It has various implementations, default is ProviderManager
. ProviderManager
iterates through a list of AuthenticationProviders
. If any of these providers returns a non-null
response, user is authenticated successfully. If none of the providers returns non-null
response then ProviderNotFoundException
is thrown. If all or last provider throw AuthenticationException
, then user is not successfully authenticated and gets 401 HTTP status code. There are various implementations of AuthenticationProviders
such as DAOAuthenticationProvider
. DAOAuthenticationProvider
leverages UserDetailsService
to look up username, password and GrantedAuthority
for a given username from an Authentication
request. Some of the implementation of UserDetailsService
is JDBCDaoImpl
and In-Memory authentication. In-memory authentication is the simplest to configure where the credential information is given in the config file itself as shown below. In JDBCDaoImpl
, the information is fetched from the database.
<bean id="authenticationManager"
class="org.springframework.security.authentication.ProviderManager">
<property name="providers">
<list>
<ref local="daoAuthenticationProvider"/>
<ref local="anonProvider" />
</list>
</property>
</bean>
<bean id="daoAuthenticationProvider"
class="org.springframework.security.authentication.dao.DaoAuthenticationProvider">
<property name="userDetailsService" ref="inMemoryDaoImpl"/>
</bean>
<bean id="inMemoryDaoImpl"
class="org.springframework.security.core.userdetails.memory.InMemoryDaoImpl">
<property name="userMap">
<value>test=testpassword,enabled,ROLE_USER</value>
</property>
</bean>
<bean id="anonProvider"
class="org.springframework.security.providers.anonymous.
AnonymousAuthenticationProvider">
<property name="key" value="myapp" />
</bean>
The above configuration defines an AuthenticationManager
implementation ProviderManager
which uses two providers DaoAuthenticationProvider
and AnonymousProvider
. DaoAuthenticationProvider
uses InMemoryDaoImpl
to fetch the user credential information for a given Authentication
request.
RequestCacheAwareFilter (org.springframework.security.web.savedrequest)
This filter fetches the request from the RequestCache
, if the current request is found in the cache, then it forwards the wrapped request to the next filter, otherwise the original request is forwarded to the next filter.
The class diagram looks like:
The configuration of RequestCacheAwareFilter
is shown below:
<bean id="requestCacheFilter" class="org.springframework.security.
web.savedrequest.RequestCacheAwareFilter" />
SecurityContextHolderAwareRequestFilter(org.springframework.security.web.servletapi)
This filter wraps the ServletRequest
with the wrapper that implements security specific method. This is required to access the Authentication details from the request object. When the request is forwarded from a security framework to a Spring MVC framework, this wrapper request object is required to check authorization details. This wrapper implements the following methods:
getRemoteUser()
: To get the principal's name getUserPrincipal()
: To get the Authentication
object isUserInRole()
: Returns true
if user has assigned some role, otherwise false
The class diagram looks like:
The configuration of SecurityContextHolderAwareRequestFilter
is shown below:
<bean id="servletApiFilter" class="org.springframework.security.web.
servletapi.SecurityContextHolderAwareRequestFilter"/>
AnonymousAuthenticationFilter(org.springframework.security.web.authentication)
This filter populates the authentication object in security context if it is null
. This situation comes when for some of the URL the user is not required to get authenticated such as for login.jsp. For such URIs, an anonymous user is defined and an anonymous role is given to it. The definition looks like:
<bean id="anonFilter"
class="org.springframework.security.web.authentication.
AnonymousAuthenticationFilter" >
<property name="key" value="SomeUniqueKeyForThisApplication" />
<property name="userAttribute" value="anonymousUser,ROLE_ANONYMOUS" />
</bean>
The above configuration declares the anonFilter
bean, there are two parameters, key
can be anything specific to the application, and this key
will be used by AnonymousAuthenticationProvider
when processing the authentication. In userAttributes
the principle and the authorities granted for this principle is defined. The configuration for AnonymousAuthenticationProvider
:
<bean id="anonymousAuthenticationProvider"
class="org.springframework.security.authentication.AnonymousAuthenticationProvider">
<property name="key" value="foobar"/>
</bean>
SessionManagementFilter(org.springframework.security.web.session)
This filter is applied only once per request. With the help of this filter, various session related activity can be performed. Such as, to guard the application from session fixation the sessionID
associated with the request can be changed after every successful authentication. SessionFixationProtectionStrategy
is a class that performs this change. Or, if application supports concurrent user per session, then ConcurrentSessionControlStrategy
is a class that performs the check related to concurrent users. If the number of users allowed per session exceeded then it will throw SessionAuthenticationException
, otherwise allows the user to proceed. It needs a SecurityContextRepository
to check if the user is authenticated or not. The definition of the bean looks like:
The class diagram looks like:
The configuration of SessionManagementFilter
is shown below:
<bean id="sessionMgmtFilter"
class="org.springframework.security.web.session.SessionManagementFilter">
<constructor-arg ref="customSecurityContextRepository"/>
</bean>
ExceptionTranslationFilter (org.springframework.security.web.access)
This filter handles AuthenticationException
and AccessDeniedException
that is thrown from a filter chain. If an AuthenticationException
is thrown, it launches the entryPoint
defined by its property "authenticationEntryPoint
". If AccessDeniedException
is thrown and user is an anonymous user then the authenticationEntryPoint
is launched. Otherwise, 403 (Access Forbidden) status code is returned by AccessDeniedHandlerImpl
.
The class diagram looks like:
The configuration of ExceptionTranslationFilter
is shown below:
<bean id="exceptionTranslator"
class="org.springframework.security.web.access.ExceptionTranslationFilter">
<property name="requestCache" ref="myRequestCache"/>
<property name="authenticationEntryPoint">
<bean class="org.springframework.security.web.authentication.
LoginUrlAuthenticationEntryPoint">
<property name="loginFormUrl" value="/login.jsp"/>
</bean>
</property>
</bean>
<bean id ="myRequestCache"
class="org.springframework.security.web.savedrequest.HttpSessionRequestCache">
</bean>
FilterSecurityInterceptor(org.springframework.security.web.access.intercept)
This interceptor performs the security authorization checks on the request according to the configured parameters. The meta data that is set in this interceptor as shown below declares the URL patterns and the authorization role it needs to access the resources.
The class diagram looks like:
The configuration of FilterSecurityInterceptor
is shown below:
<bean id="filterSecurityInterceptor"
class="org.springframework.security.web.access.intercept.FilterSecurityInterceptor">
<property name="securityMetadataSource">
<security:filter-security-metadata-source>
<security:intercept-url pattern="/user/**" access="ROLE_USER,ROLE_ADMIN"/>
<security:intercept-url pattern="/login.jsp*" access="ROLE_ANONYMOUS" />
</security:filter-security-metadata-source>
</property>
<property name="accessDecisionManager" ref="accessDecisionManager" />
</bean>
<bean id="accessDecisionManager"
class="org.springframework.security.access.vote.AffirmativeBased">
<property name="decisionVoters">
<list>
<bean class="org.springframework.security.access.vote.RoleVoter"/>
<bean class="org.springframework.security.access.vote.AuthenticatedVoter"/>
</list>
</property>
</bean>
The above configuration declares that the request matching the pattern /user/**
should be authenticated for roles ROLE_USER
or ROLE_ADMIN
. Any other URL can access the resource without going through security checks. Note that in filterChainProxy
we have defined that for login.jsp filters are bypassed completely. Setting any access value for this login.jsp will have no effect as this interceptor will never be called. Authorization is done with the help of various implementations of AccessDecisionManager
. The default is AffirmativeBased
that grants access if any AccessDecisionVoter
returns an affirmative response. AccessDecisionVoter
is having various implementations that are responsible for voting on authorizing decisions. Default is RoleVoter
. RoleVoter
looks into the meta-data defined in FilterSecurityInterceptor
. It first checks that the access defined starts with ROLE_
prefix. If not, then it returns ACCESS_ABSTAIN
. Otherwise, it checks that the role defined is same as present in the Authentication
object. If it matches, then it returns ACCESS_GRANTED
, otherwise, ACCESS_DENIED
. Note that all the comparisons are case sensitive.
Conclusion
Spring security framework is a robust and a well defined framework for implementing security requirements. Because of its interface based design, it is highly customizable. This article only touches the default configuration of the security framework. Default configurations help to understand the basics which are very crucial to customize this framework according to specific requirements.
History
- 12th September, 2011: Initial version