Introduction

In the tutorial below we will look at a basic integration of a Java web application with Spring Security 3. In this particular scenario we’ll be integrating Spring Security with existing web application that uses Struts & Spring. However, similar steps can be applied to any other web framework you may be using. We will also look at one of the core concepts Spring Security – AccessDecisionManager.

Basic integration

We will start with an application that has no security implemented. I’ve used this setup as my base. It already uses maven, so the first thing we do is to add a new repository:

<repository>
			<id>org.springframework.security</id>
			<name>Spring Security</name>
			<url>http://maven.springframework.org/snapshot</url>
		</repository>

and bunch of new JARs:

<dependency>
			<groupId>org.springframework.security</groupId>
			<artifactId>spring-security-core</artifactId>
			<version>3.0.6.CI-SNAPSHOT</version>
		</dependency>
		<dependency>
			<groupId>org.springframework.security</groupId>
			<artifactId>spring-security-config</artifactId>
			<version>3.0.6.CI-SNAPSHOT</version>
		</dependency>
		<dependency>
			<groupId>org.springframework.security</groupId>
			<artifactId>spring-security-web</artifactId>
			<version>3.0.6.CI-SNAPSHOT</version>
		</dependency>

Spring Security is implemented as a filter – it can be plugged into the application without any changes. A request will be intercepted by DelegatingFilterProxy and authentication will take place, as defined in Spring security XML file. We will add filter to the top of web.xml and trigger it for all URLs (url pattern set to /*).

<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>

For the very basic integration, Spring Security will require only little configuration that we’ll put into security.xml file.

<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/security"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:beans="http://www.springframework.org/schema/beans"
	xsi:schemaLocation="
		http://www.springframework.org/schema/beans 
		http://www.springframework.org/schema/beans/spring-beans.xsd
		http://www.springframework.org/schema/security 
		http://www.springframework.org/schema/security/spring-security-3.0.xsd
	">
	<http auto-config="true">
		<intercept-url pattern="/*" access="ROLE_USER" />
	</http>
	<authentication-manager alias="authenticationManager">
		<authentication-provider>
			<user-service>
				<user authorities="ROLE_USER" name="guest" password="test" />
			</user-service>
		</authentication-provider>
	</authentication-manager>
</beans:beans>

We are defining here that access to any resource (intercept-url pattern=”/*”) should be allowed only for users that have authority called ROLE_USER. Normally user credentials will need to be stored in a central place – typically a database or LDAP server. Above we are hard-coding an authentication-provider with one user (guest/test) that has authority ROLE_USER.

Finally, we need to tell Spring about security.xml. Since our application already uses Spring, we have defined in web.xml contextConfigLocation parameter already – it points to our beans definition (applicationContext.xml). We will add security.xml in the same place:

<context-param>
		<param-name>contextConfigLocation</param-name>
		<param-value>
		/WEB-INF/resources/applicationContext.xml
		/WEB-INF/resources/security.xml
		</param-value>
	</context-param>

When we build the application now, any URL should automatically generate following login page:

Login_page

Access decision manager

Spring Security deals (or helps you deal with) two security concepts: first authentication and then authorization. Authentication filters are run first – they will make sure that a user is who he says he is. This is usually done by checking user credentials, e.g., username and password). Let’s skip authentication step for now and talk about authorization. Authorization takes care of deciding if authenticated user (meaning that authentication must precede authorization) should be allowed to access a resource.

Spring Security supports authorization with the help of AccessDecisionManager interface. AccessDecisionManagers in turn will use one or more AccessDecisionVoters. Each voter will make the decision about granting or denying the access for given user to given resource. Access decision manager will aggregate the votes and issue final decision. Voters can return: ACCESS_GRANTED,ACCESS_DENIED but also ACCESS_ABSTAIN if a voter can’t or doesn’t want to make a decision. Managers must make the final decision – user is either granted access or not. The “not granting the access” part is done by throwing an exception – usually AccessDeniedException. In the default configuration that we’ve created, Spring uses implementation of AccessDecisionManager called AffirmativeBased. It will grant the access if any voter returns ACCESS_GRANTED. The default voters are

  • RoleVoter – checks GrantedAuthority (remember user tag with authorities=”ROLE_USER”) against ConfigAttribute (that comes from intercept-url pattern=”/*” access=”ROLE_USER”)
  • AuthenticatedVoter – can check if user IS_AUTHENTICATED_FULLY, IS_AUTHENTICATED_REMEMBERED or IS_AUTHENTICATED_ANONYMOUSLY. This is not used in our setup

We can see authorization in working when we enable higher logging level – simply add to log4j properties:

log4j.logger.org.springframework.security=DEBUG, console

When we try to access protected page as anonymous user, among other messages we can see following entries, with AccessDeniedException thrown at the end. RoleVoter returns ACCESS_DENIED and AuthenticatedVoter returns ACCESS_ABSTAIN.

 DEBUG | 2011-07-10 12:40:30,473 | AffirmativeBased.java | decide | 53 | Voter: org.springframework.security.access.vote.RoleVoter@1ae73783, returned: -1
 DEBUG | 2011-07-10 12:40:30,474 | AffirmativeBased.java | decide | 53 | Voter: org.springframework.security.access.vote.AuthenticatedVoter@41ed8741, returned: 0
 DEBUG | 2011-07-10 12:40:30,478 | ExceptionTranslationFilter.java | handleException | 153 | Access is denied (user is anonymous); redirecting to authentication entry point
org.springframework.security.access.AccessDeniedException: Access is denied

Compare it with the log after we provide correct credentials. This time RoleVoter returns ACCESS_GRANTED and as soon as it happens, AffirmativeBased access decision manager stops querying voters and allows for access:

  DEBUG | 2011-07-10 12:43:33,468 | AffirmativeBased.java | decide | 53 | Voter: org.springframework.security.access.vote.RoleVoter@1ae73783, returned: 1
 DEBUG | 2011-07-10 12:43:33,469 | AbstractSecurityInterceptor.java | beforeInvocation | 213 | Authorization successful

There are other implementations of AccessDecisionManagers and AccessDecisionVoters but I think we will leave them for another time. Stay tuned!

Here is a full source code for the example above. Run it with:

mvn jetty:run