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

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.

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

  1. […] “Hacking the Grails Spring Security Plugin” at Groovy & Grails Exchange, by Burt Beckwith […]

  2. RJ says:

    Always a pleasure to see different ways to hack into SpringSecurity!
    There is a lot of noise out there regarding SpringSecurity w/ CAS, and for obvious reasons.
    1. Acegi -> SpringSecurity upgrade
    2. SpringSecurity 2.x -> SpringSecurity 3.x upgrade.
    3. If that wasn’t enough to create confusion – SpringSecurity-Cas plugin upgrade itself.

    So “updated” articles like these are good sources to look (especially) into configuration and design issues.

    Thanks Burt!

  3. Ken R says:

    This is a great post it had some great descriptions and even better all the code. From this I was able to override the Spring Security Core plugin and authenticate against an existing database. All of this is great but I also wanted to have my authentication as a plugin because I will be re-using this in many applications. The issue is I can’t seem to put the overrides/connections that were originally in the spring/resources.groovy – here is the overrides that I have:

    import edu.gvsu.security.GvAuthenticationProvider
    import edu.gvsu.security.ui.GvFilter

    import org.codehaus.groovy.grails.plugins.springsecurity.SpringSecurityUtils

    beans = {
    def conf = SpringSecurityUtils.securityConfig

    // custom authentication
    authenticationProcessingFilter(GvFilter) {
    authenticationManager = ref(‘authenticationManager’)
    sessionAuthenticationStrategy = ref(‘sessionAuthenticationStrategy’)
    authenticationSuccessHandler = ref(‘authenticationSuccessHandler’)
    authenticationFailureHandler = ref(‘authenticationFailureHandler’)
    rememberMeServices = ref(‘rememberMeServices’)
    authenticationDetailsSource = ref(‘authenticationDetailsSource’)
    filterProcessesUrl = conf.apf.filterProcessesUrl
    usernameParameter = conf.apf.usernameParameter
    passwordParameter = conf.apf.passwordParameter
    continueChainBeforeSuccessfulAuthentication = conf.apf.continueChainBeforeSuccessfulAuthentication
    allowSessionCreation = conf.apf.allowSessionCreation
    postOnly = conf.apf.postOnly

    // custom authentication
    daoAuthenticationProvider(GvAuthenticationProvider) {
    passwordEncoder = ref(‘passwordEncoder’)
    saltSource = ref(‘saltSource’)
    preAuthenticationChecks = ref(‘preAuthenticationChecks’)
    postAuthenticationChecks = ref(‘postAuthenticationChecks’)
    dataSource = ref(‘dataSource’)

    I put this code into the plugin class and the doWithSpring block but when I get to the authentication the overrides don’t seem to be there since it errors on the person class which should be part of the override. Also, if I put the override into the spring/resources.groovy of the application using the plugin that works too, however, it seems like I should be able to have the override in the plugin and not have to re-do it every time I use the plugin. So, I would appreciate if someone could help me with this.

    Thanks, Ken

  4. navin says:

    Hi Burt,

    That was useful. I tried to run this on grails 2.2 and got issues.

    Do we have an oauth2 *provider* example with grails.

  5. Erlinis says:

    Hi Burt,
    Great post, it helped me to do an authentication against a web service without involve Grails’ domains.

    Thanks for sharing the code, it was really helpfull as reference.

    Greetings from Colombia.

  6. Francisco says:

    Thanks Burt,
    sill works like charm,
    I am using grails 2.3.3, spring-security-core:2.0-RC2, spring-security-ui:1.0-RC1

    So a couple of things changed a little bit I am going to list to save a couple of minutes to anyone who needs them.

    1.SpringSecurityUtils package changed

    2. In resources.groovy when defining the bean authenticationProcessingFilter

    add a line to set a new required parameter, I did it after this line:
    postOnly = conf.apf.postOnly
    storeLastUsername=false //New Line

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