Last updated by burtbeckwith 4 years ago
Hopefully the standard configuraton, plus the configurability exposed in DefaultSecurityConfig.groovy and SecurityConfig.groovy, enable most customization needs for your applications. However security is a large topic and there are many possible ways to secure an application.


When authenticating users from a database using DaoAuthenticationProvider (the default mode in the Spring Securiy plugin if you haven't enabled OpenID, LDAP, etc.), an implementation of UserDetailsService is required. This class is responsible for returning a concrete implementation of UserDetails. The Spring Security plugin uses GrailsDaoImpl as its UserDetailsService implementation and GrailsUserImpl as its UserDetails implementation. GrailsUserImpl actually implements GrailsUser which extends UserDetails to add a getDomainClass() method. This way GrailsDaoImpl returns a UserDetails instance that satisfies the Spring Security contract and also contains the domain User instance.

You can extend or replace GrailsDaoImpl with your own class by defining a bean in resources.groovy (or resources.xml) with the same bean name, 'userDetailsService'. This is because application beans are configured after plugin beans and there can only be one bean for each name.

You just have to make sure that your implementation returns a class implementing GrailsUser to avoid missing method exceptions. If you don't have a need to attach the database User instance to the GrailsUser instance (e.g. you don't want to store the User in the session along with the GrailsUser) you can just return null from getDomainClass().

Here's an example UserDetails (GrailsUser) and UserDetailsService implementation that maintains the primary key (id) and the full name of the user in addition to the standard information. If you extract extra data from your domain class, you'll be less likely to need to reload the user from the database - most of your common data can be kept along with your security credentials.

In this example I'm adding in a 'fullName' field and an 'id' field. Keeping the full name cached avoids hitting the database just for that lookup, but by also caching the id we can do a more efficient database load of the user. If all you have is the username, then you need to call "User.findByUsername(principal.username)", but if you have the id you can call "User.get(principal.id)". Even if you have a unique index on the 'username' database column, loading by primary key will usually be more efficient since it can take advantage of Hibernate's first-level and second-level caches.

There's really not much to implement other than your application-specific lookup code:

package com.foo.bar;

import org.codehaus.groovy.grails.plugins.springsecurity.GrailsUser; import org.springframework.security.GrantedAuthority; import org.springframework.security.userdetails.User;

public class MyUserDetails extends User implements GrailsUser {

private final long _id; private final String _fullName;

public MyUserDetails( String username, String password, boolean enabled, boolean accountNonExpired, boolean credentialsNonExpired, boolean accountNonLocked, GrantedAuthority[] authorities, long id, String fullName) {

super(username, password, enabled, accountNonExpired, credentialsNonExpired, accountNonLocked, authorities);

_id = id; _fullName = fullName; }

public Object getDomainClass() { return null; }

public long getId() { return _id; }

public String getFullName() { return _fullName; } }

package com.foo.bar;

import org.springframework.security.userdetails.UserDetails; import org.springframework.security.userdetails.UserDetailsService;

public class MyUserDetailsService implements UserDetailsService {

public UserDetails loadUserByUsername(final String username) throws UsernameNotFoundException, DataAccessException {

// lookup user and data

return new MyUserDetails(username, password, enabled, accountNonExpired, credentialsNonExpired, accountNonLocked, authorities, id, fullName); } }

and to use it, register it in resources.groovy using

userDetailsService(com.foo.bar.MyUserDetailsService)

Another option if you want to load users and roles from the database is to subclass GrailsDaoImpl - the methods are protected so you can override whatever you want.

Also note that this approach works with all beans defined in AcegiGrailsPlugin.doWithSpring() - you can replace or subclass any of the Spring beans to provide your own functionality when the standard extension mechanisms aren't sufficient.