Tuesday, September 1, 2009

Spring Security 2 Explained. Part 1. The filters.

The following article will try to explain Spring security with detail.

The vast majority of Spring Security articles and books, explain the framework making emphasis in the easy of configuration and use. Which is great for almost everything. However to really understand what is going on, i need to go a little deeper and study the elements tha actually participate on the framework.

I will make a very simple application and try to explain what happens in Spring Security through the different paths of executions i do.

The first thing to do is to add the security filter and context configuration to your web.xml file.

This is my web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">

Here, the Delegating Filter Proxy is the entry point into Spring Security. It's actually not a Spring Security Filter, but a Spring Web Filter, is just that in this case we are using it to delegate to the security filters we will define in the Spring Application Context “security.xml”

Defining the simplest Spring Security Configuration File:


<beans:beans xmlns="http://www.springframework.org/schema/security"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-2.0.4.xsd">
<http auto-config='true'>
<intercept-url pattern="/**" access="ROLE_USER" />

<user name="carlo" password="carlo" authorities="ROLE_USER" />


This file will include Spring security in our Application.

Let's explain what is on this file, by accessing our application. Pointing to the base path of our web application automatically shows us a login form. How does this happen?.

The XML element http with auto-config= “true” defines the following:

First of all Spring will automatically register the following servlet filters:


These filters execute from top to bottom and carry the following resposibilities:

HttpSessionContextIntegrationFilter job is to create a default SecurityContext. It tries to read the context from the Session, and if there isn't any, it creates a new Instance of SecurityContextImpl and put it in the ThreadLocal SecurityContextHolder. The it passes Control to the next filter in the Chain.
After returning from the chain, this filter consults the SecurityContextHolder for the SecurityContext in it to see if it has changed during request processing.
I SecurityContext has changed and authentication is not anonymous, then the new SecurityContext is stored in session.

LogoutFilter. This filter consults the URL invoked to see if it is a URL that requires logout. The default Logout Requring URL is j_spring_security_logout. So if you invoke this URL in your application you are explicitly asking to logout from the application. If the URL is for logut, the This filter delegates in handlers the actual logout process. By default it delegates to SecurityContextLogoutHandler and TokenBasedRememberMeServices. The first Handler invalidates the Session, and the second handler, deletes the remember me cookie (set age to 0)

. This is one of the most important filters. Its job is the following. First it it checks if the Invocation URL is for a login petition, it does this by consulting if the URL matches the default /j_spring_security_check URL.
If the previous step requirement is met, then this filter looks in the request parameters for a Username and a password (in the request parameters j_username and j_password respectively). Then it creates an UsernamePasswordAuthenticationToken which is an Implementation of an Authentication Class, so it set the authentication Principal to the username and the Authentication credentials to the passoword provided.
Then it puts the username in the session under the name SPRING_SECURITY_LAST_USERNAME. The next important Step is to call the AuthenticationManager's authenticate method with our newly created Authentication Object.
From the preceding paragraph we can see that the first time we access the application home, the filter will stop it's processing in the point where it checks if the invocation URL is an authentication petition. The home page is not the URL for authentication (which we now know is j_spring_security_check) so this filter won't do anything (right now) but to continue to the next filter.

. This Filter is almost explained by its name. It's function is to generate a default login page when required. With "when required" I mean when the URL invoked corresponds to the default URL for login page which is /spring_security_login. When this filter detects that this URL is being invoked it renders a default login page. When we first invoke the application home we are not invoking this URL. So why does it show the login Page. We'll see why soon enough.

BasicProcessingFilter. This filter's job is to check if the request has standard http authentication headers. If it does, it creates a new Authentication (UsernamePasswordAuthenticationToken) object from it, and like the AuthenticationProcessingFilter, it delegates to the AuthenticationManager's authenticate method the authentication job. If authentication fails, the filters sets the SecurityContext to null.

SecurityContextHolderAwareRequestFilter. This filter wraps the Servlet request in a SavedRequestAwareWrapper. This wrapper somehow maintains a saved request, but I don't know what this wrapper exactly brings to the table.

RememberMeProcessingFilter. This filter's job is to query the security context to see if there is an Authentication Object on it, if not, it will create (or try to) a new one, by calling the autoLogin method of AbstractRememberMeServices which will retrieve a Remember Me cookie and try to authenticate using it.
The cookie's default name is SPRING_SECURITY_REMEMBER_ME_COOKIE. If the filter finds this cookie, it decodes it, make some validations and call the userDetailsService with the cookie information. If the userDetails returned is valid, The RememberMeServices creates a new RememberMeAuthenticationToken that return to the filter and the filter saves it into the SecurityContext.

. This simple filter, detects if there already is an authentication object in the security context. if there is not any, it creates a new one (AnonymousAuthenticationToken) with the username "guest" and the rol "ROLE_ANONYMOUS" (this names can be overriden).

ExceptionTranslationFilter. This filter is in charge of Capturing and initiating the handling of any security exceptions that may come from the application (AccessDeniedException,AuthenticationException). The filter serves as a translator between the Java Exceptions and the Http Errors. If the filter detects an authentication Exception (Basically No Authentication Object) it initiates the authentication mechanism by invoking the authentication entry point to show the login page. If the exception is a AccessDeniedException, the filter first checks if the authenticated user is Anonymous. If it is, it follows the same path as if there wasn't any authentication at all. If it isn't anonymous, it basically means that a USer with some privileges is trying to access resources that he can't. So an access denied exception is returned to the browser

SessionFixationProtectionFilter. This filter checks if there is a user authenticated but there is not a Session attribute associated with the securityContext. This can happen if the user is not authenticated from the start of the request but is authenticated during the duration of the request thread. If this is the case, the filter creates a new session with the information required.

FilterSecurityInterceptor. This is the Filter version of the AbstractSecurityInterceptor. it delegates to it's super class (the mentioned AbstractSecurityInterceptor) the authorization to access the required resource. If it can not access the resource the AbstractSecurityInterceptor will throw an accessDeniedException. We'll cover the AbstractSecurityInterceptor in detail in the next par of this Article.

No comments: