Case Insensitive Usernames using Spring Security Core Plugin for Grails

Given the years I’ve worked with Spring Security, it’s a bit embarrassing how often I forget that, by default, if you’re using the default database-backed implementation, the login is case sensitive. The best place to modify this behavior by overriding it in the UserDetailsService implementation you’ve selected.

Most of my most recent projects have been in Grails1. This is a framework I’ve found to be very flexible and fast for development. And usually the first plugin I install is the Spring Security Core.

Making your login case insensitive (which has always been my choice, since it eliminates irritating user experience issues) turns out to be quite simple. The plugin provides GormUserDetailsService as the default implementation. Override this class and update your resource.groovy to use it instead of the default.

The first example shows how to override if you want to go for simplicity in your code because it’s application specific. Rather than dynamically loading the user domain class, we’ll just use it directly, since we know what it is for this application.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
package com.stevideter
 
import org.codehaus.groovy.grails.plugins.springsecurity.GormUserDetailsService
import org.springframework.security.core.GrantedAuthority
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
 
class CaseInsensitiveUserDetailsService extends GormUserDetailsService {
 
	/**
	 * make username case insensitive. Since we're using in a specific app,
	 * let's directly use the known class and property name
	 */
	UserDetails loadUserByUsername(String username, boolean loadRoles) 
			throws UsernameNotFoundException {
		// in this example, I used Person as my domain class
		Person.withTransaction { status ->
			def user = Person.findByUsernameIlike(username)
			if (!user) {
				log.warn "User not found: $username"
				throw new UsernameNotFoundException('User not found', username)
			}
 
			Collection<GrantedAuthority> authorities = 
				loadAuthorities(user, username, loadRoles)
			createUserDetails user, authorities
		}
	}
}

Then register this bean in resources.groovy, and you’re ready to log in without worrying about case:

1
2
3
beans = {
	userDetailsService(com.stevideter.CaseInsensitiveUserDetailsService)
}

But what if you want to be more generic in your approach? That’s still possible, we just need to remember to inject grailsApplication into our Service class so we can dynamically load the domain class.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
package com.stevideter
 
import org.codehaus.groovy.grails.plugins.springsecurity.GormUserDetailsService
import org.springframework.security.core.GrantedAuthority
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.codehaus.groovy.grails.plugins.springsecurity.SpringSecurityUtils
 
class DynamicCaseInsensitiveUserDetailsService extends GormUserDetailsService {
 
	def grailsApplication
 
	UserDetails loadUserByUsername(String username, boolean loadRoles)
			throws UsernameNotFoundException {
		def conf = SpringSecurityUtils.securityConfig
		String userClassName = conf.userLookup.userDomainClassName
		def dc = grailsApplication.getDomainClass(userClassName)
		if (!dc) {
			throw new RuntimeException("The specified user domain class '$userClassName' is not a domain class")
		}
 
		Class<?> User = dc.clazz
 
		User.withTransaction { status ->
			// this time we'll use where plus a DetachedCriteria closure 
			// with =~ for case insensitivity
			def user = User.where{
				(conf.userLookup.usernamePropertyName) =~ username
			}.find()
			if (!user) {
				log.warn "User not found: $username"
				throw new UsernameNotFoundException('User not found', username)
			}
			log.debug "found user"
 
			Collection<GrantedAuthority> authorities = 
				loadAuthorities(user, username, loadRoles)
			createUserDetails user, authorities
		}
	}
}
1
2
3
4
5
beans = {
	userDetailsService(com.stevideter.DynamicCaseInsensitiveUserDetailsService) {
		grailsApplication = ref('grailsApplication')
	}
}

In the first example, it’s possible to easily use a dynamic finder to create our query, but in the second, I chose to use the where method with a Detached Criteria to find the user.

The UserDetailsService is an important part of Spring Security, as it’s also where you’ll want to make any changes to the UserDetails returned from the authentication process.

[1] Just Grails. See the link in Burt Beckwith’s comment for why.

2 thoughts on “Case Insensitive Usernames using Spring Security Core Plugin for Grails

  1. stevi Post author

    Thanks for the education, Burt. I’ve updated the post accordingly.

    And hooray, I can stop saying GGGGGroovy on GGGGrails in hopes the strong glottal emphasis will help clue people in.

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>