Wednesday, May 12, 2010

Interceptors in Spring MVC

These are different ways to configure Interceptors for Spring MVC based controllers.

1. If you have the URL / Controller mapping in xml you can define the interceptors as

<bean id="myService" class="com.test.services.MyService"/>

<bean id="myInterceptorOne" class="com.test.interceptors.FirstInterceptor">
<property name="myService" ref="myService" />
</bean>

<bean id="myInterceptorTwo" class="com.test.interceptors.SecondInterceptor" />

<bean id="defaultHandlerMapping" class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"/>

<bean name="/home.action" class="com.test.controller.FirstController"/>

<bean id="someId" class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
<property name="interceptors">
<list>
<ref bean="myInterceptorOne"/>
<ref bean="myInterceptorTwo"/>
</list>
</property>
<property name="urlMap">
<map>
<entry key="/index.action" value-ref="index"/>
</map>
</property>
</bean>

<bean id="index" class="com.test.controller.SecondController" />

I have defined 2 controllers. Only /index.action is intercepted by both the interceptors while /home.action is not.

2.Annotation-based controllers – you define the interceptors as –

<bean id="annotationMapper" class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping">
<property name="interceptors">
<list>
<ref bean="myInterceptorOne"/>
<ref bean="myInterceptorTwo"/>
</list>
</property>
</bean>

The interceptors are applied to all the configured controllers.

3. While using annotation based controllers, we can use "mvc:interceptors" tag which can be used to define interceptors and control how they are applied.

Like

<mvc:interceptor>
<mvc:mapping path="/index.action"/>
<mvc:mapping path="/home.action"/>
<bean class="com.test.interceptors.ThirdInterceptor" />
</mvc:interceptor>

Here ThirdInterceptor would be applied to /index.action and /home.action

<mvc:interceptor>
<mvc:mapping path="/index.action"/>
<bean class="com.test.interceptors.ThirdInterceptor" />
</mvc:interceptor>

<mvc:interceptor>
<mvc:mapping path="/index.action"/>
<bean class="com.test.interceptors.FirstInterceptor" />
</mvc:interceptor>

Here ThirdInterceptor and FirstInterceptor (in the defined order) would be applied to /index.action.

Saturday, January 9, 2010

Developing Modular Web Application using Slices on Spring DM 2 RC1

Configurations and Installations
1. Download Spring DM 2 RC1

2. Download Slices bundles from http://www.springsource.com/download/community?project=SpringSource%20Slices&nightly=yes

3. Copy the bundles and the plan file in the dist directory of the zip to dm Server's repository/usr directory.

4. Edit DM Server's config/com.springsource.kernel.userregion.properties file to add slice’s plan under initialArtifacts –
initialArtifacts=repository:plan/com.springsource.kernel.userregion.springdm, repository:plan/com.springsource.server.web, repository:plan/com.springsource.osgi.slices

5. Restart DM server and we are ready to deploy web application using slices.

Sample web application
With Slices, we can develop web application using multiple OSGi bundles; each bundle provides content for a distinct sub-portion of the application's URL space. Slices applications are arranged in a parent/child structure, with each application having one parent called “host” and zero or more children called “slices”.


The advantage using Slices is that any section (slice) of the web application can be hot deployed without restarting the complete web application.


Let us consider a Travel Agent web application that provides reservations for flight and hotel. So, the structure looks like –

The “Flight Reservation” slice can be accessed using URL “/flights” and the “Hotel Reservation” slice can be accessed using URL “/hotels”.

Now let us look into web.xml and MANIFEST.MF for host and slice bundles.

The host web bundle (TravelAgent) has the following web.xml which is similar to any other web application except that com.springsource.osgi.slices.core.SliceHostFilter is configured to route request to its slices.

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

<filter>
<filter-name>host-filter</filter-name>
<filter-class>com.springsource.osgi.slices.core.SliceHostFilter</filter-class>
</filter>

<filter-mapping>
<filter-name>host-filter</filter-name>
<url-pattern>/*</url-pattern>
<dispatcher>REQUEST</dispatcher>
<dispatcher>FORWARD</dispatcher>
<dispatcher>INCLUDE</dispatcher>
</filter-mapping>

<servlet>
<servlet-name>agent</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>

<servlet-mapping>
<servlet-name>agent</servlet-name>
<url-pattern>*.action</url-pattern>
</servlet-mapping>

</web-app>


Host web bundle (TravelAgent) has the following MANIFEST.MF

Manifest-Version: 1.0
Import-Bundle: org.springframework.context,org.springframework.context.support
Bundle-Version: 1.0.0
Bundle-Name: TravelAgent
Bundle-ManifestVersion: 2
Bundle-SymbolicName: TravelAgent
Web-ContextPath: /
Import-Package: javax.servlet.http;version="[2.5, 3.0)",org.springfram
ework.beans.factory.xml;version="[2.5.6.A,3.1)",org.springframework.w
eb.servlet;version="[2.5.6.A,3.1)",org.springframework.web.servlet.ha
ndler;version="[2.5.6.A,3.1)",org.springframework.web.servlet.mvc;ver
sion="[2.5.6.A,3.1)",org.springframework.web.servlet.view;version="[2
.5.6.A,3.1)",com.springsource.osgi.slices.core,org.springframework.st
ereotype,org.springframework.web.bind.annotation


Slices (FlightReservation and HotelReservation) has the following web.xml similar to any other web application

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

<servlet>
<servlet-name>servlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>

<servlet-mapping>
<servlet-name>servlet</servlet-name>
<url-pattern>*.action</url-pattern>
</servlet-mapping>
</web-app>

Slice web bundle (FlightReservation) has the following MANIFEST.MF

Manifest-Version: 1.0
Bundle-Version: 1.0.0
Bundle-Name: FlightReservation
Bundle-ManifestVersion: 2
Bundle-SymbolicName: FlightReservation
Slice-ContextPath: /flights
Slice-Host: TravelAgent;version="[1.0.0, 2.0.0)"
Import-Bundle: org.springframework.context,org.springframework.context.support
Import-Package: javax.servlet.http;version="[2.5, 3.0)",org.springfram
ework.beans.factory.xml;version="[2.5.6.A,3.1)",org.springframework.w
eb.servlet;version="[2.5.6.A,3.1)",org.springframework.web.servlet.ha
ndler;version="[2.5.6.A,3.1)",org.springframework.web.servlet.mvc;ver
sion="[2.5.6.A,3.1)",org.springframework.web.servlet.view;version="[2
.5.6.A,3.1)",com.springsource.osgi.slices.core,org.springframework.st
ereotype,org.springframework.web.bind.annotation

Slice web bundle (HotelReservation) has the following MANIFEST.MF

Manifest-Version: 1.0
Bundle-Version: 1.0.0
Bundle-Name: HotelReservation
Bundle-ManifestVersion: 2
Bundle-SymbolicName: HotelReservation
Slice-ContextPath: /hotels
Slice-Host: TravelAgent;version="[1.0.0, 2.0.0)"
Import-Bundle: org.springframework.context,org.springframework.context.support
Import-Package: javax.servlet.http;version="[2.5, 3.0)",org.springfram
ework.beans.factory.xml;version="[2.5.6.A,3.1)",org.springframework.w
eb.servlet;version="[2.5.6.A,3.1)",org.springframework.web.servlet.ha
ndler;version="[2.5.6.A,3.1)",org.springframework.web.servlet.mvc;ver
sion="[2.5.6.A,3.1)",org.springframework.web.servlet.view;version="[2
.5.6.A,3.1)",com.springsource.osgi.slices.core,org.springframework.st
ereotype,org.springframework.web.bind.annotation

A slice uses “Slice-Host” manifest header to define host to which it attach to. URL portion that the slice handles is defined using “Slice-ContextPath” manifest header.

Here I have not covered any configurations / code related to Spring MVC, we will cover Spring MVC in later posts.

Sunday, January 3, 2010

Spring Security – Part 3 - Role based Method Invocation

In the previous part we looked how to secure the URL calls. Now let us look into how to secure the method invocation. Let us again have a properties file (methods.properties) that contains the method names and the roles that can invoke the methods. For example,

com.test.service.MyService.delete*=ROLE_USER,ROLE_MANAGER
com.test.service.MyService.createAccount=ROLE_ADMIN

You can also have this mapping defined in database or LDAP server. For simplicity we will have the mapping defined in a properties file.

Let us now define interface (IMyService) and implementation (MyService)

public interface IMyService {
public void deleteUserPreferences (String userName);
public void deleteUserLogs(String userName);
public void createAccount(String userName);
}

Now based on the above configurations ROLE_USER, ROLE_MANAGER would only be able to invoke the methods deleteUserPreferences and deleteUserLogs. ROLE_ADMIN would only be able to invoke method createAccount.

Now you can write MyService implementing the above interface.

Method security in enforced using a MethodSecurityInterceptor, which secures
MethodInvocations. The interceptor uses a MethodDefinitionSource instance to obtain the configuration attributes that apply to a particular method invocation. Since we would be reading the methods names and the corresponding roles from properties file, we would either –

1. Define our own implementation of MethodSecurityMetadataSource and implement all methods.

2. Or we can write a class extending MapBasedMethodSecurityMetadataSource. MapBasedMethodDefinitionSource is used to store configuration attributes keyed by method names (which can be wildcarded) and will be used internally when the attributes are defined in the application context using the <intercept-methods> or <protect-point> elements.

We will go ahead with point 2 as we just need to read the properties file, create Map<String, List<ConfigAttribute>> and call MapBasedMethodDefinitionSource’s constructor passing this map.

So, let us first define a bean PropertyHolder that would read the properties file and create Map<String, List<ConfigAttribute>>.

<bean id="propHolder" class="com.test.common.PropertyHolder">
<property name="methodProperties">
<util:properties location="classpath:methods.properties" />
</property>
</bean>

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Properties;

import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.access.SecurityConfig;
import org.springframework.util.StringUtils;

public class PropertyHolder {
private Properties methodProperties;

public void setMethodProperties(Properties methodProperties) {
this.methodProperties = methodProperties;
}

public Properties getMethodProperties() {
return methodProperties;
}

public Map<String, List<ConfigAttribute>> getMethodMap() {
Map<String, List<ConfigAttribute>> methodMap = new HashMap<String, List<ConfigAttribute>>();
for (Iterator iter = methodProperties.keySet().iterator(); iter
.hasNext();) {
String name = (String) iter.next();
String value = methodProperties.getProperty(name);

String[] tokens = StringUtils
.commaDelimitedListToStringArray(value);
List<ConfigAttribute> attributes = new ArrayList<ConfigAttribute>(
tokens.length);

for (String token : tokens) {
attributes.add(new SecurityConfig(token));
}

methodMap.put(name, attributes);
}

return methodMap;
}
}

Now let us configure a MethodSecurityIterceptor in our application context

<bean id="methodSecurityInterceptor" class="org.springframework.security.access.intercept.aopalliance.MethodSecurityInterceptor">
<property name="authenticationManager" ref="authenticationManager"/>
<property name="accessDecisionManager" ref="accessDecisionManager"/>
<property name="securityMetadataSource" ref="mySecurityMetadataSource"/>
</bean>

We have already configured authenticationManager and accessDecisionManager in previous parts.

Let us now configure mySecurityMetadataSource.

<bean id=" mySecurityMetadataSource" class="com.test.common.MySecurityMetadataSource">
<constructor-arg name="propHolder" ref="propHolder"/>
</bean>


Now let us write the bean MySecurityMetadataSource that extends MapBasedMethodSecurityMetadataSource.

Import org.springframework.security.access.method.MapBasedMethodSecurityMetadataSource;

public class MySecurityMetadataSource extends
MapBasedMethodSecurityMetadataSource {

public MyMethodSecurityFilterNew(PropertyHolder propHolder) {
super(propHolder.getMethodMap());
}
}

Now define the beans MyService in application context that we want to secure and define another bean org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator to create a proxy for MyService object so that an authorization check may be applied for every invocation on the object.

<bean id=”myService” class=”com.test.service.MyService” />

<bean id="autoProxyCreator" class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
<property name="interceptorNames">
<list>
<value>methodSecurityInterceptor</value>
</list>
</property>
<property name="beanNames">
<list>
<value> myService</value>
</list>
</property>
</bean>

Spring Security – Part 2 – Custom securityMetadataSource

Now let us look into filterSecurityInterceptor. This filter authorizes web requests based on URL patterns. Instead of having the URLs configured in application context, we will have URLs in the properties file. We can even have these URLs configured in database or LDAP server. Here let us have properties file having URLs and the corresponding roles. The properties file looks like –

/admin/**=ROLE_ADMIN,ROLE_MANAGER
/admin/userPreference/**=ROLE_MANAGER
/admin/userPreference/updatePreference.action=ROLE_USER,ROLE_ASSOCIATE
/admin/userPreference/deletePreference.action=ROLE_USER

Here ROLE_ADMIN, ROLE_MANAGER are given complete access to all admin URLs, ROLE_MANAGER is given access to all URLs having /admin/userPreference/ and ROLE_USER, ROLE_ASSOCIATE are given page / action level access.

Now let us define filterSecurityInterceptor.

<bean id="filterSecurityInterceptor"
class="org.springframework.security.web.access.intercept.FilterSecurityInterceptor">
<property name="authenticationManager" ref="authenticationManager"/>
<property name="accessDecisionManager" ref="accessDecisionManager"/>
<property name="securityMetadataSource" ref="mySecureResourceFilter"/>
</bean>

We have already configured authenticationManager in the previous part. Let us now define accessDecisionManager.

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

We will look into it in more detail in next part.

Now let us write custom securityMetadataSource – mySecureResourceFilter

Let us define the bean first -


<bean id="mySecureResourceFilter" class="com.test.common.MySecureResourceFilter">
<property name="urlProperties">
<util:properties location="classpath:urls.properties" />
</property>
</bean>

Given the requested URL, we have to find all the roles authorized to access it. Like if requested URL is /admin/changePreference/deletePreference.action, we will get the roles from the properties file which can access the following URLs –

/admin/changePreference/deletePreference.action
/admin/changePreference/**
/admin/**

All this is implemented in getAttributes (..) method defined below which returns the name of the Authorities (or Roles) that are allowed to access requested URL.

import java.util.Collection;
import java.util.Properties;

import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.access.SecurityConfig;

import org.springframework.security.web.FilterInvocation;
import org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource;

public class MySecureResourceFilter implements
FilterInvocationSecurityMetadataSource {

private Properties urlProperties;

public Collection<ConfigAttribute> getAllConfigAttributes() {
return null;
}

public Collection<ConfigAttribute> getAttributes(Object filter)
throws IllegalArgumentException {
FilterInvocation filterInvocation = (FilterInvocation) filter;
String url = filterInvocation.getRequestUrl();

//get the roles for requested page from the property file
String urlPropsValue = urlProperties.getProperty(url);
StringBuilder rolesStringBuilder = new StringBuilder();
if(urlPropsValue != null) {
rolesStringBuilder.append(urlPropsValue).append(",");
}

if(!url.endsWith("/")) {
int lastSlashIndex = url.lastIndexOf("/");
url = url.substring(0, lastSlashIndex + 1);
}


String [] urlParts = url.split("/");

StringBuilder urlBuilder = new StringBuilder();
for (String urlPart : urlParts) {
if(urlPart.trim().length() == 0) {
continue;
}
urlBuilder.append("/").append(urlPart);
urlPropsValue = urlProperties.getProperty(urlBuilder.toString() + "/**");

if(urlPropsValue != null) {
rolesStringBuilder.append(urlPropsValue).append(",");
}
}

if(rolesStringBuilder.toString().endsWith(",")) {
rolesStringBuilder.deleteCharAt(rolesStringBuilder.length()-1);
}


if(rolesStringBuilder.length() == 0) {
return null;
}

return SecurityConfig.createListFromCommaDelimitedString(rolesStringBuilder.toString());
}

public boolean supports(Class<?> arg0) {
return true;
}

public void setUrlProperties(Properties urlProperties) {
this.urlProperties = urlProperties;
}

public Properties getUrlProperties() {
return urlProperties;
}

}

That’s it. In next part we will look into role based Method Invocation.

Saturday, January 2, 2010

Spring Security – Part 1 – Necessary Configuration and Custom UserDetailsService

Here we will look into –

1. The necessary configurations required
2. Write custom userDetailsService reading user name, password and roles from properties file

We will not use default security namespace configuration instead we will define beans and configure our own FilterChainProxy.

Add the schema declaration to the application context file –
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
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.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd"
xmlns:sec="http://www.springframework.org/schema/security"
xmlns:util="http://www.springframework.org/schema/util">

. . . . . .

</beans>

web.xml configuration
The first thing we need to do is add the following filter declaration to web.xml file:
<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>

Now let us define and configure springSecurityFilterChain. It delegates request to a chain of Spring-managed filters. Since we would be customizing the Spring Security’s behavior we would be manually configuring the filters instead of using the default configuration.

For a web application we need to configure the following filters in the mentioned order –

1. HttpSessionContextIntegrationFilter – It queries HTTPSession to retrieve SecurityContext and populates SecurityContextHolder for the duration of web request. At the end of the web request, any updates made to the SecurityContextHolder will be persisted back to the HttpSession by this filter.

2. LogoutFilter – It clears SecurityContextHolder when logout is requested.

3. AuthenticationProcessingFilter – It puts Authentication into the SecurityContext on login request.

4. ExceptionTranslationFilter – It converts SpringSecurity exceptions into HTTP response or HTTP redirect.

5. FilterSecurityInterceptor – It authorizes web requests based on URL patterns.

Now let us define springSecurityFilterChain in application context file.
<bean id="springSecurityFilterChain" class="org.springframework.security.web.FilterChainProxy">
<sec:filter-chain-map path-type="ant">
<sec:filter-chain pattern="/**" filters=" httpSessionContextFilter,
logoutFilter,
authenticationProcessingFilter,
exceptionTranslationFilter,
filterSecurityInterceptor" />
</sec:filter-chain-map>
</bean>


Let us now configure the filters –

1. httpSessionContextFilter
Let us use the default configuration for httpSessionContextFilter.

<bean id="httpSessionContextFilter" class="org.springframework.security.web.context.HttpSessionContextIntegrationFilter"/>

2. exceptionTranslationFilter
It handles any AccessDeniedException and AuthenticationException thrown within the filter chain.
If an AuthenticationException is detected, the filter will launch the authenticationEntryPoint. If an AccessDeniedException is detected, the filter will determine whether or not the user is an anonymous user. If they are an anonymous user, the authenticationEntryPoint will be launched. If they are not an anonymous user, the filter will delegate to the AccessDeniedHandler.
Let us define the beans now. Login form url is set as “/login.jsp” for authenticationEntryPoint and error page is set as “/error/jsp” for access denied handler.

<bean id="exceptionTranslationFilter" class="org.springframework.security.web.access.ExceptionTranslationFilter">
<property name="authenticationEntryPoint" ref="authenticationEntryPoint"/>
<property name="accessDeniedHandler" ref="accessDeniedHandler" />
</bean>


<bean id="authenticationEntryPoint" class="org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint" >
<property name="loginFormUrl" value="/login.jsp" />
</bean>

<bean id="accessDeniedHandler" class="org.springframework.security.web.access.AccessDeniedHandlerImpl">
<property name="errorPage" value="/error401.jsp"/>
</bean>


3. authenticationProcessingFilter –
User information can be stored in database, in LDAP server or in properties file. Let us have user name, password and roles defined in a properties file (user.properties). We will write custom implementation of Spring Security's UserDetailsService - MyUserDetailService

user.properties has the following entries –
pranav=123123,ROLE_ADMIN
pranjal=321321,ROLE_MANAGER
sudheer=abcabc,ROLE_USER

Now let us write MyUserDetailService that implements UserDetailsService implementing method loadUserByUsername. This would load the user information from the user.properties creating UserDetails.

import java.util.Properties;
import org.springframework.dao.DataAccessException;
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;
import org.springframework.security.core.userdetails.memory.UserAttribute;
import org.springframework.security.core.userdetails.memory.UserAttributeEditor;

public class MyUserDetailService implements UserDetailsService {
private Properties userProperties;

public UserDetails loadUserByUsername(String username)
throws UsernameNotFoundException, DataAccessException {

String userPropsValue = userProperties.getProperty(username);
if (userPropsValue == null) {
throw new UsernameNotFoundException(username
+ "User does not exist");
}

UserAttributeEditor configAttribEd = new UserAttributeEditor();
configAttribEd.setAsText(userPropsValue);

UserAttribute userAttributes = (UserAttribute) configAttribEd
.getValue();

return new User(username, userAttributes.getPassword(), userAttributes
.isEnabled(), true, true, true, userAttributes.getAuthorities());
}

public void setUserProperties(Properties userProperties) {
this.userProperties = userProperties;
}

public Properties getUserProperties() {
return userProperties;
}

}

Now let us define the beans. Post successful authentication user would be shown the requested URL or the default target url “/home.action” set to authenticationSuccessHandler.
<bean id="authenticationProcessingFilter" class="org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter">
<property name="authenticationManager" ref="authenticationManager" />
<property name="authenticationSuccessHandler" ref="authenticationSuccessHandler"/>
</bean>

<bean id="authenticationManager" class="org.springframework.security.authentication.ProviderManager">
<property name="providers">
<list>
<ref bean="authenticationProvider"/>
</list>
</property>
</bean>

<bean id="authenticationProvider" class="org.springframework.security.authentication.dao.DaoAuthenticationProvider">
<property name="userDetailsService" ref="userDetailService"/>
</bean>

<bean id="userDetailService" class="com.test.common.MyUserDetailService">
<property name="userProperties">
<util:properties location="classpath:users.properties" />
</property>
</bean>

<bean id="authenticationSuccessHandler" class="org.springframework.security.web.authentication.SimpleUrlAuthenticationSuccessHandler">
<property name="defaultTargetUrl" value="/home.action"/>
</bean>


We will look into filterSecurityInterceptor and logoutFilter in next part.