Richard’s World

Running with Agile (3)

Another day, another run. Today’s run was quite an eventful one for me.. but I’ll get to why after my Agile links. I managed to listen to two short podcasts on today’s run. Both were once again conversations between Bob Payne and George Dinwiddie. The first podcast deals with the topic of self organising teams and the second with the importance of team rooms for agile development. I enjoy the conversations between these two, I like their informal and light-hearted approach to the topics that clearly they both know a lot about. I particularly enjoyed the second podcast dealing with team rooms, the importance of maintaining the flow of information between team members and how ‘cube-less’ working environments can help facilitate this.

Podcast 1: “Self Organising Teams” - available here to download. Running time approx 14 mins.
Podcast 2: “Team Rooms” - available here to download. Running time approx 15 mins.

My run route was another bush trail, 2.8 miles in total. I had intended to go a little further than this, but as I was running down a particularly steep section of the trail I nearly stepped on one of these little blighters:

Red Bellied Black Snake

As I was running I had mistaken it for a twig (there are loads normally lying around on the trail) and only noticed that it was Red-Bellied Black Snake in time to jump over it! Coming from England, where the most exotic wildlife you’ll normally see is maybe a badger, this gave me quite a scare and I decided to cut short my adventure for the day. Later last night we also had a visit from a Funnel Web Spider. It would appear Sydney’s most venomous creatures are making themselves known to me one at a time, what comes next I dread to think…

Configuring Spring Security Form Login with Remember-Me Enabled

I recently had to setup a new JEE project with a security and authentication system. As the project was being built using Spring it made sense to use Spring Security (formally Acegi Security) to achieve this.

So far, in common with most of the Spring framework, I’ve found Spring Security to be well implemented and robust tool. It is well documented and appears to offer a wide variety of different security/authentication tools, such as web form logins, remember-me functionality, OpenId support, LDAP support, annotations support, automatic config and a huge range of other tools. Because it’s open source, it is also of course very easy to extend and override certain areas where you as a developer need to provide custom business logic or rules.

In fact the main problem I had (and this is quite possibly due to my own shortcomings) was that because Spring Security is so customisable I found it could be a little tricky to get it to do everything that you wanted it to. The documentation provides plenty of good examples for single use cases, but it can be fiddly trying to combine more than one use case.

In the end of course (with a little help from the Spring Forums) I did get it all working beautifully. In the spirit of sharing, find below my sample configuration. This sets up form-based authentication for my webapp and utilises remember-me (so that user’s don’t have to login everytime).

Firstly, remember to add the following filter to your web.xml

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

Next step was to define an applicationcontext-security.xml file to store all of my security bean definitions. I’ve tried to comment all of the areas below which I think require attention:


<beans:beans
xmlns="http://www.springframework.org/schema/security"
xmlns:beans="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-2.0.xsd
					http://www.springframework.org/schema/security
					http://www.springframework.org/schema/security/spring-security-2.0.1.xsd">

<http entry-point-ref="authenticationProcessingFilterEntryPoint">

	<!-- Allow access welcome.htm to anyone (even un-authenticated users) -->
	<intercept-url pattern="/welcome.htm" access="IS_AUTHENTICATED_ANONYMOUSLY" />

	<!-- All other resources to require users to have user role USER_ROLE to view -->
	<intercept-url pattern="/*.htm" access="ROLE_USER" />

	<!-- Don't apply any filters to the login form either, we want unauthenticated users to be able to see this -->
	<intercept-url pattern="/login.htm*" filters="none"/>  

	<anonymous />
	<http-basic />

</http>

<authentication-manager alias="authenticationManager" />

<authentication-provider user-service-ref="myUserDetailsService" />

<!-- I've defined a custom UserDetails service to lookup users in my db using me own implementation -->
<beans:bean id="myUserDetailsService" class="com.richardadamdean.myapp.auth.MyUserDetailsService">
	<beans:property name="userServ" ref="UserService"/>
</beans:bean>

<beans:bean id="authenticationProcessingFilterEntryPoint" class="org.springframework.security.ui.webapp.AuthenticationProcessingFilterEntryPoint">
	<beans:property name="loginFormUrl" value="/login.htm" />
	<beans:property name="forceHttps" value="false" />
</beans:bean>

<!-- custom form authentication filter - overridden so I can do some other business logic when a user logs in (update login count etc) -->
<beans:bean id="authenticationFilter" class="com.richardadamdean.myapp.auth.MyAuthenticationProcessingFilter">
	<!-- sets this as the authentication filter over the default -->
	<custom-filter position="AUTHENTICATION_PROCESSING_FILTER"/>
	<beans:property name="authenticationManager" ref="authenticationManager" />
	<beans:property name="authenticationFailureUrl" value="/login.htm?login_error=1" />
	<beans:property name="defaultTargetUrl" value="/" />
	<beans:property name="filterProcessesUrl" value="/j_spring_security_check" />
	<beans:property name="usrServ" ref="UserService"/>
	<beans:property name="continueChainBeforeSuccessfulAuthentication" value="false" />
	<beans:property name="rememberMeServices" ref="rememberMeServices" />
</beans:bean>

<!-- Override RememberMeProcessingFilter to allow application of other business logic (update login count when user returns to the site -->
<beans:bean id="rememberMeProcessingFilter" class="com.richardadamdean.myapp.auth.MyRememberMeProcessingFilter">
  <custom-filter position="REMEMBER_ME_FILTER" />
  <beans:property name="rememberMeServices" ref="rememberMeServices"/>
  <beans:property name="authenticationManager" ref="authenticationManager" />
  <beans:property name="usrServ" ref="UserService"/>
</beans:bean>

<!-- Defines which remember me implementation to use - in this case using a database table to log 'remembered' tokens -->
<beans:bean id="rememberMeServices" class="org.springframework.security.ui.rememberme.PersistentTokenBasedRememberMeServices">
	<beans:property name="tokenRepository" ref="jdbcTokenRepository" />
	<beans:property name="userDetailsService" ref="myUserDetailsService" />
	<beans:property name="key" value="springRocks" />
	<beans:property name="alwaysRemember" value="false" />
</beans:bean>

<!-- Uses a database table to maintain a set of persistent login data -->
<beans:bean id="jdbcTokenRepository" class="org.springframework.security.ui.rememberme.JdbcTokenRepositoryImpl">
	<beans:property name="createTableOnStartup" value="false" />
	<beans:property name="dataSource" ref="dataSource" />
</beans:bean>

<beans:bean id="rememberMeAuthenticationProvider" class="org.springframework.security.providers.rememberme.RememberMeAuthenticationProvider">
  <!-- This ensures that remember-me is added as an authentication provider -->
  <custom-authentication-provider />
  <beans:property name="key" value="springRocks"/>
</beans:bean>

<beans:bean id="logoutFilter" class="org.springframework.security.ui.logout.LogoutFilter">
	<custom-filter position="LOGOUT_FILTER" />
	<beans:constructor-arg value="/" />
	<!-- URL redirected to after logout -->
	<beans:constructor-arg>
		<beans:list>
			<beans:ref bean="rememberMeServices" />
			<!-- I add a bean here to perform some custom tasks when the user logs out -->
			<beans:bean class="com.richardadamdean.myapp.auth.MySecurityContextLogoutHandler"/>
			<!-- class="org.acegisecurity.ui.logout.SecurityContextLogoutHandler"  -->
		</beans:list>
	</beans:constructor-arg>
</beans:bean>

<beans:bean id="loggerListener" class="org.springframework.security.event.authentication.LoggerListener" />

</beans:beans>

I think most of this is probably fairly self-explanatory. Some points that are worth noting:

1. If you’re defining your own filters then remember to include a custom-filter tag to position your filter in the Spring Security filter chain. In the example above you will notice nested within certain bean definitions:

	...<custom-filter position="AUTHENTICATION_PROCESSING_FILTER"/>
	...<custom-filter position="REMEMBER_ME_FILTER" />
	...<custom-filter position="LOGOUT_FILTER" />

These basically tell Spring Security to use the bean they are defined within in that particular position in the filter hierarchy. Without these definitions your custom beans will not be used.

2. When using remember-me, don’t forget to add a

	<custom-authentication-provider />

definition. This makes sure that remember me is actually used as an authentication provider - i.e. when your user returns having previously asked to be remembered, this adds remember me to the list of providers that check if the user is authenticated. (This one actually tripped me up and had me wondering why remember me wasn’t working until I found it!)

3. Extending Spring Security classes to add your own custom functionality / implementations is very easy. I do this in a number of different places (which you maybe already noticed in the above xml config). I’ll give some examples below and provide a quick overview of how and why I do this.

a) Implementing a custom UserDetailsService

I use this to provide a custom authentication service which uses my own user services to lookup and verify that a user exists. For example:

public class MyUserDetailsService implements UserDetailsService
{
	private UserService userServ;

	public UserDetails loadUserByUsername(String username) throws UserNotFoundException
	{
		User user = this.userServ.findUser(username);

		if (user == null)
		{
			throw new UserNotFoundException("User not found");
		}

		Set<UserRole> roles = user.getUserRoles();
		GrantedAuthority[] authorities = new GrantedAuthority[roles.size()];
		int i = 0;
		for (UserRole ur : roles)
		{
			authorities[i] = ur.getRole();
			i = i + 1;
		}

		org.springframework.security.userdetails.User authUser = new org.springframework.security.userdetails.User(user.getUserName(), user
				.getPassword(), user.isAccountActive(), true, true, true, authorities);

		return authUser;
	}
}

b) You can define your own AuthenticationProcessingFilter where you can add any additional business logic you wish to run once a user is successfully authenticated.

public class MyAuthenticationProcessingFilter extends AuthenticationProcessingFilter
{
	private myService;

	@Override
	protected void onSuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response, Authentication authResult) throws IOException
	{
		// perform some custom logic when the user has been authenticated - e.g. update a login count etc
		this.myService.doSomeCustomBusinessLogic(authResult.getName());

		super.onSuccessfulAuthentication(request, response, authResult);
	}
}

c) And very similar to b), we can extend RememberMeProcessingFilter to include any custom business logic you wish to run when a user returns to your site and is ‘remembered’ by the application

public class MyRememberMeProcessingFilter extends RememberMeProcessingFilter
{
	private myService;

	@Override
	protected void onSuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response, Authentication authResult)
	{
		// perform some custom logic when the user has been 'remembered' &amp; authenticated - e.g. update a login count etc
		this.myService.doSomeCustomBusinessLogic(authResult.getName());

		super.onSuccessfulAuthentication(request, response, authResult);
	}
}

So that’s it then. There’s tons more settings and options for Spring Security available on the web site. The Spring Forum section for this can be a little on the quiet side, but there’s probably enough there to help with most initial problems you might come across.

  • Recent Tweets

    Posting tweet...

    Powered by Twitter Tools.

  • Sponsors