Archive for December 9th, 2011

“Hacking the Grails Spring Security Plugin” at Groovy & Grails Exchange

Friday, December 09th, 2011

I gave a talk at the Groovy & Grails Exchange in London called “Hacking the Grails Spring Security Plugin”. I didn’t want to spend a lot of time discussing the sample app code since there was a lot of material to cover, so I’m making the code available here with a brief discussion of the implementation.


To support a custom login where the user’s organization must be specified in addition to the standard username and password, there’s a custom AuthenticationProvider (hacking.extralogin.auth.OrganizationAuthenticationProvider) which processes a subclass of UsernamePasswordAuthenticationToken (hacking.extralogin.OrganizationAuthentication) that adds an organizationName property, and a filter (hacking.extralogin.ui.OrganizationFilter) that creates the authentication from the request and initiates authentication.

In this example all authentication uses this approach, so the filter replaces the standard "authenticationProcessingFilter" bean (and subclasses the plugin’s RequestHolderAuthenticationFilter to maintain its functionality) and the provider replaces the "daoAuthenticationProvider" bean. The provider directly implements the AuthenticationProvider interface since using GORM directly is simple enough to not need to delegate to a UserDetailsService or other helper classes.

You can see the bean registrations for the filter and provider in grails-app/conf/spring/resources.groovy.

Note that the auth provider and filter are in separate packages to reinforce the idea that auth providers shouldn’t be aware of the UI. The filters that call the auth providers create an Authentication instance with all of the information that’s needed to authenticate, getting most or all of the data from the HTTP request. This keeps the auth providers modular and reusable outside of a web application.

No changes are required for the generated User, Role, or UserRole classes, but a new domain class Organization is needed to store the organization names, and OrgUser is needed to provide a link between users and organizations. auth.gsp has an extra input, a <select> box with all available Organization names.

If you request http://localhost:8080/hacking_london/secure you should see the text “not secured” since the index action is not guarded. But navigating to http://localhost:8080/hacking_london/secure/admin requires a user with ROLE_ADMIN, http://localhost:8080/hacking_london/secure/user requires a user with ROLE_USER, and http://localhost:8080/hacking_london/secure/adminOrUser requires a user with either ROLE_ADMIN or ROLE_USER. You can use one of the users created in BootStrap.groovy:

username Organization name password Role
admin Org1 password ROLE_ADMIN
user Org2 password ROLE_USER
disabled Org1 password ROLE_USER

There’s an extra user (“disabled”) with a disabled account to test that login fails with a correct username, org name, and password.

We also need to tweak an error message. The plugin’s i18n message bundle will display the error “Sorry, we were not able to find a user with that username and password” if the username, password, or organization are wrong. But we should include the organization in the message to indicate that it might have been wrong. To fix this, add this line to your application’s grails-app/i18n/messages.properties: springSecurity.errors.login.fail=Sorry, we were not able to find a user with that username, organization, and password.

To test this, log in as user ‘user’ with password ‘password’ but leave the organization name selected as ‘Org1’.

Note that since we’re not using the plugin’s UserDetailsService or Spring Security’s DaoAuthenticationProvider we don’t need the grails.plugins.springsecurity.userLookup.userDomainClassName, grails.plugins.springsecurity.userLookup.authorityJoinClassName, or
grails.plugins.springsecurity.authority.className properties added to Config.groovy by the s2-quickstart script. They’re commented out so you can switch back to the standard authentication approach by removing or commenting out the bean overrides in resources.groovy and the organization select box in auth.gsp.


The other significant customization I discussed was doing a custom post-logout redirect. It is possible to specify a spring-security-redirect request parameter when logging out, but this is too coarse an approach in general. If you need to use logic specific to the user, or something about the current authentication state, you need more control. So the sample application subclasses the default implementation of LogoutSuccessHandler, SimpleUrlLogoutSuccessHandler with hacking.logout.CustomLogoutSuccessHandler and registers it as the logoutSuccessHandler bean in resources.groovy.

The logic is contrived; if you’re in Organization ‘Org1’ you’re redirected to ‘http://yahoo.com’ and if you’re in Organization ‘Org2’ you’re redirected to ‘http://google.com’. Otherwise you’re redirected to the default location (‘/’ unless you’ve customized it with the grails.plugins.springsecurity.successHandler.defaultTargetUrl config attribute). But it shows an example of how you could use your own business logic to make a similar decision.

There’s one wrinkle here though; the only parameters for the overridden determineTargetUrl method are the HttpServletRequest and HttpServletResponse, but not the Authentication. And since this is the last step of the logout process, the user has already been logged out and the Authentication isn’t available from the request, springSecurityService, SecurityContextHolder, etc. But the public method (onLogoutSuccess) that calls this method has a parameter for the Authentication, so we save it in a ThreadLocal so it’s available for our override.


You can get the PDF of the presentation here, and the zip of the sample project here.

Creative Commons License
This work is licensed under a Creative Commons Attribution 3.0 License.