Home > Software Development > Spring Security: Custom Authenticators

Spring Security: Custom Authenticators

In the Spring tradition, Spring Security 3 is incredibly extensible. In this tutorial I will show you how to create your own authenticators. In particular, I will build on my article “Adding Crowd Authentication to your Application” by adding Crowd support to my web application.

Atlassian used to offer a Spring Security connector for Crowd. They gave up on it when Spring Security moved to 3.0. Fortunately, it is trivial to set up the integration yourself!

Authentication

An Authentication is a central object in Spring Security. For the sake of simplicity, you can consider an authentication as a username and a password (or some other verifying credential like a certificate).

What confuses most new users of Spring Security is that an Authentication object does not necessarily represent an authenticated user. It can also represent an attempt at authentication. Access is granted to secured resources only if the Authentication object’s isAuthenticated method returns true.

AuthenticationProvider

Authentications are authenticated by AuthenticationProviders. An AuthenticationProvider checks the credentials of an authentication to ensure they are valid. Off-the-shelf implementations check database tables, LDAP registries, and other sources. Here is a very stupid example that ensures the user’s username matches his password.

public class StupidAuthenticationProvider implements AuthenticationProvider {
	@Override
	public boolean supports(Class<? extends Object> authentication) {
		return authentication.equals(UsernamePasswordAuthenticationToken.class);
	}

	@Override
	public Authentication authenticate(Authentication authentication) {
		if (authentication.getName().equals(authentication.getCredentials()))
			return new UsernamePasswordAuthentication(authentication.getName(), authentication.getCredentials(), null);
		else
			return null;
	}
}

Registering the AuthenticationProvider

For this to work, we must register the AuthenticationProvider with Spring Security:

<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:security="http://www.springframework.org/schema/security"
	xsi:schemaLocation="http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-3.0.xsd">

	<bean id='stupidAuthenticationProvider' class='StupidAuthenticationProvider'/>

	<security:authentication-manager>
		<security:authentication-provider ref='stupidAuthenticationProvider'/>
	</security:authentication-manager>

</beans>

Crowd Integration

Now, let’s bring this together with my article on integration your application with Crowd. In this example, we check if the username and password represent an account in the Crowd registry. If they do, we grant the user various levels of administrator privileges to our web application.

public class CrowdAuthenticationProvider implements AuthenticationProvider {
	@Autowired
	private CrowdClient crowd;

	@Override
	public boolean supports(Class<? extends Object> authentication) {
		return authentication.equals(UsernamePasswordAuthenticationToken.class);
	}

	@Override
	public Authentication authenticate(Authentication authentication) {
		try {
			User user = crowd.authenticateUser(
				authentication.getName(),
				authentication.getCredentials().toString()
				);

			Administrator administrator = new Administrator();
			administrator.setLastName(user.getLastName());
			administrator.setLastName(user.getFirstName());
			administrator.setEmail(new EmailAddress(user.getEmailAddress()));
			administrator.setUsername(user.getName());

			List<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>();
			authorities.add(new GrantedAuthorityImpl("ROLE_USER"));

			// Optional: Define granted authorities based on
			// groups to which the user is a member.
			List<Group> groups = crowd.getGroupsForUser(user.getName(), 0, -1);
			for (Group group: groups) {
				if (group.getName().equals("..."))
				 	authorities.add(new GrantedAuthorityImpl("ROLE_..."));
			}

			return new UserAuthentication(administrator, authorities);
		}
		catch (UserNotFoundException e) {
			return null;
		}
		catch (InactiveAccountException e) {
			throw new DisabledException(e.getMessage(), e);
		}
		catch (ExpiredCredentialException e) {
			throw new CredentialsExpiredException(e.getMessage(), e);
		}
		catch (InvalidAuthenticationException e) {
			throw new BadCredentialsException(e.getMessage(), e);
		}
		catch (ApplicationPermissionException e) {
			throw new AuthenticationServiceException(e.getMessage(), e);
		}
		catch (OperationFailedException e) {
			throw new AuthenticationServiceException(e.getMessage(), e);
		}
	}
}

Pay attention the exception mapping. This is pretty boilerplate; but it is critical to proper handling of authentication attempts.

Also notice that instead of creating an instance of the CrowdClient, we have Spring inject it? This allows the Crowd configuration to be extracted to a properties file. Here is how we set that up in Spring:

<context:property-placeholder location='classpath:app.properties'/>

<bean id='crowdFactory'
	class='com.atlassian.crowd.integration.rest.service.factory.RestCrowdClientFactory'/>

<bean id='crowd' factory-bean="crowdFactory" factory-method="newInstance">
	<constructor-arg index="0"><value>${crowd.server.url}</value></constructor-arg>
	<constructor-arg index="1"><value>${crowd.application.name}</value></constructor-arg>
	<constructor-arg index="2"><value>${crowd.application.password}</value></constructor-arg>
</bean>

Going Further

In some cases, only certain accounts are managed in Crowd, while others are stored elsewhere. For example, our application may allow any idiot off the street to register, while administrator accounts are managed by Crowd. Multiple AuthenticationProviders can be registered in Spring:

<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:security="http://www.springframework.org/schema/security"
	xsi:schemaLocation="
		http://www.springframework.org/schema/security
		http://www.springframework.org/schema/security/spring-security-3.0.xsd">

	<bean id='crowdAuthenticationProvider' class='CrowdAuthenticationProvider'/>
	<bean id='databaseAuthenticationProvider' class='DatabaseAuthenticationProvider'/>

	<security:authentication-manager>
		<security:authentication-provider ref='crowdAuthenticationProvider'/>
		<security:authentication-provider ref='databaseAuthenticationProvider'/>
	</security:authentication-manager>

</beans>

The AuthenticationProviders will be tried in order. The first provider to return an account or throw an exception is the one that will be used for each login attempt.

  1. June 21, 2011 at 12:50 pm | #1

    Thanks, I tweaked my implementation a bit using your example.

  2. June 21, 2011 at 5:54 pm | #2

    I am glad I was able to help!

  3. Roy Wang
    August 1, 2011 at 12:08 pm | #3

    Are these code available to download?
    In the StupidAuthenticationProvider class, what should authenticate() method to return? I am new to Spring, Thanks for your help

  4. August 1, 2011 at 12:36 pm | #4

    No, I didn’t publish any code for this article. The authenticate method should return an Authentication object and not a boolean like my StupidAuthenticationProvider does (thank you for bringing this mistake to my attention, I will fix the article). Look at line 35 of the CrowdAuthenticationProvider example. You will generally return a new UserAuthentication(, ). I tried to find another example for you on the web; but was unsuccessful. Maybe I should write a more in-depth article on extended Spring Security. In the mean time, if you are new to Spring and Spring Security, you might want to take the time to read Spring in Action 3 ed. It is a fantastic book.

  5. November 17, 2011 at 8:09 am | #5

    very nice article. To read more on spring security, you can refer to these link
    Spring Security 3 – Form Login and Logout Tutorial
    Spring Security 3 – MVC integration Tutorial

  6. November 18, 2011 at 9:03 am | #6

    Thank you for the links! I would also urge anyone using Spring libraries to reference Spring’s documentation. Spring Source does a fantastic job of maintaining the best documentation in the open-source community.

  7. December 7, 2011 at 12:27 pm | #7

    For anyone interested in LDAP integration, which is provided out-of-the-box, check out this article.

  8. Leonty
    March 10, 2012 at 11:19 am | #8

    Hi!
    I was looking for simple tutorial on implementing custom authentication provider with current Spring Security version and yours was the most concise and containing all the info I needed.
    Great job and thanks!

  9. March 11, 2012 at 9:48 am | #9

    Thanks, I am glad I could help.

  10. March 15, 2012 at 9:51 pm | #10

    Did you also integrate SSO?

  11. March 15, 2012 at 10:10 pm | #11

    No. It is possible; but I could not find any documentation and could not justify the reverse engineering time. If you make any head way, let me know and I will update the article. Best of luck.

  1. No trackbacks yet.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.