Customization of Spring Security Authentication

Normally we do application authentication against a database or authenticating through web services or even more complicated a mix of them. With this in mind, I start to investigate how to use spring security to achieve authentication in these scenarios.

We have nice in memory authentication examples from its document, as following:

<authentication-manager>
    <authentication-provider>
      <user-service>
        <user name="jimi" password="jimispassword" authorities="ROLE_USER, ROLE_ADMIN" />
        <user name="bob" password="bobspassword" authorities="ROLE_USER" />
      </user-service>
    </authentication-provider>
  </authentication-manager>

But highly unlikely your system only got a few known hardcoded users, we also have other out of box authentication providers to use, like LDAP or OpenId, which still let me wondering how to have more control on authentication.

There is an out of box authentication against database, but it needs the creation and existence of tables exactly in its desired schema, which is not very useful as well, plus if we want authenticate through web service,we are still stuck.

I know from my experience with other languages that the answer lies in customization of AuthenticationProvider. But I kept getting the suggestions of extension of UserDetailService, and I had a closer look of the UserDetailService interface it only has one function

public UserDetails loadUserByUsername(String username)
            throws UsernameNotFoundException {
    }

This by the looks of it, only loads a user detail through user name, how can we authentication a user with user name and password?

One more frustration is that you can not even find any example out there telling you how to extend AuthenticationProvider, and most of examples of are telling you how to implement UserDetailService, What the hack this has got to do with authentication? Do we need to implement both AuthenticationProvider and UserDetailService to provide a proper authentication mechanism?

Here are the messages that are missing from Spring Security Document and there are paths wandering through AuthenticationProvider and UserDetailService.

You definitely need to customize AuthenticationProvider, which is the base class and starting point of making your own authentication, out of box LDAP and OpenId providers are implementations of it, then why we do not get told how to implement AuthenticationProvider? The answer is you hardly need to go down to that level of class hierarchy, you can get away with UserDetailService, sorry if I have confused you, but am not kidding.

Have a check of AbstractUserDetailsAuthenticationProvider, it is an extension of AuthenticationProvider, and it has an interesting method:

protected abstract UserDetails retrieveUser(java.lang.String username,
                                            UsernamePasswordAuthenticationToken authentication)
                                     throws AuthenticationException

Is not UserDetails what UserDetailService provides? So another option is you can extend AbstractUserDetailsAuthenticationProvider instead of grand dad AuthenticationProvider, it is your choice, but still I have yet shown you why UserDetailService is so popular

You can just implement UserDetailService to customize your authentication process either from a database or from web service, because it is provided by an out of box AuthenticationProvider implementation: DaoAuthenticationProvider which is a conceret class of AbstractUserDetailsAuthenticationProvider and relies on UserDetailService to provide user name and password to compare them with user inputs to achieve authentication.

To put another way, even we only provide a user name to the UserDetailService, that leaves the UserDetailService implementation to return UserDetails that has user password, then DaoAuthenticationProvider will be responsible to check the credentials coming from login form with UserDetails, and take it from there.

To find the right spot of customization is important, and a sign of elegance of software development.

One last piece of puzzle, why from nowhere is DaoAuthenticationProvider mentioned? Or when you first saw its name you reckon it has anything to do with data access like database? All it is because it is the DEFAULT AuthenticationProvider when you config the authentication-provider node in security.xml, if you do not mention any AuthenticationProvider, then Spring Security assuems you use DaoAuthenticationProvider,

So if use:

<authentication-manager>
    <authentication-provider>
      <user-service>
        <user name="jimi" password="jimispassword" authorities="ROLE_USER, ROLE_ADMIN" />
        <user name="bob" password="bobspassword" authorities="ROLE_USER" />
      </user-service>
    </authentication-provider>
  </authentication-manager>

It is using DaoAuthenticationProvider, if you use

<authentication-manager>
		<authentication-provider user-service-ref="cleancodeUserService" >
		</authentication-provider>
	</authentication-manager>
<beans:bean id="cleancodeUserService" class="com.cleancode.springmvc1.users.UserServiceImpl"/>  

It is using DaoAuthenticationProvider too, even it is not in your config, as it is default.

It is time to show you some examples of how all these are done

Example 1 Customization of AuthenticationProvider:

Implementation of AuthenticationProvider, just presented as a prof of concept, not that I encourage you to start from this level.

public class CleanCodeAuthenticationProvider implements AuthenticationProvider{

	public Authentication authenticate(Authentication authentication)
			throws AuthenticationException {
		
		List<GrantedAuthority> AUTHORITIES = new ArrayList<GrantedAuthority>();
		AUTHORITIES.add(new SimpleGrantedAuthority("ROLE_USER"));
		if (authentication.getName().equals(authentication.getCredentials()))
			return new UsernamePasswordAuthenticationToken(authentication.getName(), authentication.getCredentials(), AUTHORITIES);
		else
			return null;

	}

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

	}

}

where authentication.getName().equals(authentication.getCredentials() is just an example, you can go off to somewhere else to get user according to its name and compare the password with authentication.getCredentials().

And security.xml is like

<authentication-manager>
		<authentication-provider ref="customAuthenticationProvider" >
		</authentication-provider>
	</authentication-manager>
	<beans:bean id="customAuthenticationProvider" class="com.cleancode.springmvc1.users.CleanCodeAuthenticationProvider"/>

Example 2: Customization of UserDetailsService

public class UserServiceImpl implements UserDetailsService {

    public UserDetails loadUserByUsername(String username)
            throws UsernameNotFoundException {
    
    	List<GrantedAuthority> AUTHORITIES = new ArrayList<GrantedAuthority>();
    	
    	if(username.equals("rod"))
    	{
    	
    		AUTHORITIES.add(new SimpleGrantedAuthority("supervisor"));
    		AUTHORITIES.add(new SimpleGrantedAuthority("user"));
    		AUTHORITIES.add(new SimpleGrantedAuthority("teller"));
    		return new User("rod",
    				"4efe081594ce25ee4efd9f7067f7f678a347bccf2de201f3adf2a3eb544850b465b4e51cdc3fcdde",
    				 AUTHORITIES);
    		
    	} else if (username.equals("scott")){
    		
    		AUTHORITIES.add(new SimpleGrantedAuthority("user"));
    		return new User("scott",
    				"fb1f9e48058d30dc21c35ab4cf895e2a80f2f03fac549b51be637196dfb6b2b7276a89c65e38b7a1",
    				 AUTHORITIES);
    	}
    	else
    	{
    		throw new UsernameNotFoundException("User not found: " + username);
    	}
        
    }
}  

Here I am using hardcoded user names and passwords as an example, the thing is you can go to a database or web service to retrieve user details(including password) with user name, then pass the user name and password to User constructor, the password will be used later in the chain by DaoAuthenticationProvider

Security.xml

  <beans:bean id="encoder" class="org.springframework.security.crypto.password.StandardPasswordEncoder"/>
   
  <!--
    Usernames/Passwords are
        rod/koala
        scott/wombat
    -->
 	<authentication-manager>
		<authentication-provider user-service-ref="cleancodeUserService" >
			<password-encoder ref="encoder"/>
		</authentication-provider>
	</authentication-manager>
	<beans:bean id="cleancodeUserService" class="com.cleancode.springmvc1.users.UserServiceImpl"/>
     

You can see you do not need to point DaoAuthenticationProvider as AuthenticationProvider, password-encoder is another feature provided by DaoAuthenticationProvider.

http config is not presented here.

Have fun.

Tags:

This entry was posted on Saturday, November 5th, 2011 at 4:38 am and is filed under Java. You can follow any responses to this entry through the RSS 2.0 feed. You can leave a response, or trackback from your own site.

10 Responses to “Customization of Spring Security Authentication”

  1. Reda says:

    very interesting, Thanks a lot.

  2. Loupassakis Christos says:

    I was wondering how to implement UserDetailsService with BCrypt password encryption, it’s all clear now.
    And I confirm, spring security works out of the box when using UserDetailService .
    Thanks a lot for your explanations !

  3. Udana says:

    Great Clarification! I faced the same issues mentioned in your post Your explanations and the sample code helped a lot. Thanks so much!!

  4. Adrien Be says:

    Thanks a lot for the clarification & demystification. The community will give it back to you ;)

  5. Adrien Be says:

    Not too sure how the password encryption works. You obviously found what type of encryption Spring is using, I thought I did too as indicated in Spring API for StandardPasswordEncoder: SHA-256 hashing with 1024 iterations and a random 8-byte random salt value.

    However I am not able to regenerate the values you indicate in your code neither using StandardPasswordEncoder itself via code, it would generate a new password each time I run my test app. Neither online hash calculators.

    I obviously missed something, could you enlighten me? thanks

  6. mf says:

    Thank you so much for this excellent post. The ‘Spring Security 3′ book doesn’t have this information, and leads you down the incorrect paths you mention at the start of your post. This is a lifesaver.

  7. Chhote Lal Prasad Gupta says:

    Hi,

    Thanks for your nice tutorial about Authentication customization.I spent a week for getting the same thing.but not able to do it.It is easy to understand and implement.Thanks a lot for sharing a nice concept for us.

    Regards
    chhote

  8. sohail says:

    It is a nice tutorial. providing basics of spring security userdetailservice customization :)

  9. bk says:

    Thanks a lot for the article. The Spring Security documentation was not clear and your article explains it very well.

  10. Pavel Shchegolevatykh says:

    Thank you so much! I spent the whole searching for any useful information about this.

  11. Ritesh says:

    Thank you very much! You save my life.

Leave a Reply

*