Reputation: 187499
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
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