Dónal
Dónal

Reputation: 187499

customising authentication in Spring Security

In my Grails app I'm using the Spring Security plugin and have defined a custom userDetailsService Spring bean in order to control how user and role data is retrieved, e.g.

class MyUserDetailsService implements GrailsUserDetailsService {

    /**
     * Some Spring Security classes (e.g. RoleHierarchyVoter) expect at least one role, so
     * we give a user with no granted roles this one which gets past that restriction but
     * doesn't grant anything.
     */
    static final List NO_ROLES = [new GrantedAuthorityImpl(SpringSecurityUtils.NO_ROLE)]

    UserDetails loadUserByUsername(String username, boolean loadRoles) {
        return loadUserByUsername(username)
    }

    UserDetails loadUserByUsername(String username) {
        User.withTransaction { status ->

            User user = User.findByUsername(username)
            if (!user) {
                throw new UsernameNotFoundException('User not found', username)
            }

            def authorities = user.authorities.collect {new GrantedAuthorityImpl(it.authority)}
            return new CustomUserDetails(
                    user.username,
                    user.password,
                    user.enabled,
                    !user.accountExpired,
                    !user.passwordExpired,
                    !user.accountLocked,
                    authorities ?: NO_ROLES,
                    user.id,
                    user.name)
        }
    }
}

The CustomUserDetails class referenced above simply extends GrailsUser with a name field:

class CustomUserDetails extends GrailsUser {

    private static final long serialVersionUID = 1;

    final String name

    CustomUserDetails(String username,
                      String password,
                      boolean enabled,
                      boolean accountNonExpired,
                      boolean credentialsNonExpired,
                      boolean accountNonLocked,
                      Collection<GrantedAuthority> authorities,
                      long id,
                      String displayName) {

        super(username, password, enabled, accountNonExpired, credentialsNonExpired, accountNonLocked, authorities, id)
        this.name = displayName
    }
}

In addition to customising how user and role data is retrieved, I also want to control how the user is authenticated. In my case the authentication process is not as simple as the usual "check that the password entered matches the password in the database". The actual details of how the user is authenticated aren't relevant, so for the sake of simplicity let's pretend that if the user's name field matches the entered password he's granted the roles (authorities) encapsulated within CustomUserDetails.

I guess there's a Spring bean somewhere within the plugin that I can override in order to customise the default authentication mechanism, but which one? Because the name field is only defined by CustomUserDetails, I will need access to the instance of this class returned by MyUserDetailsService in order to perform the custom authentication, is this possible?

Upvotes: 2

Views: 661

Answers (1)

Burt Beckwith
Burt Beckwith

Reputation: 75671

I discussed custom logins here and the sample app has code: http://burtbeckwith.com/blog/?p=1090

But if all you want to do is grant different roles to a user than what's in the database, I would do that in MyUserDetailsService. There's no requirement that the roles be in the database - Spring Security just wants a bunch of GrantedAuthorityImpl instances. In one app I worked on I automatically granted ROLE_USER to regular site users during authentication to avoid wasting database space.

Edit:

If you're using DaoAuthenticationProvider you can add additional checks during authentication by customizing the preAuthenticationChecks and/or postAuthenticationChecks beans. Subclass the ones from the plugin and add your own checks, and register them in resources.groovy as

preAuthenticationChecks(MyPreAuthenticationChecks)
postAuthenticationChecks(MyPostAuthenticationChecks)

Upvotes: 3

Related Questions