Search code examples
tomcatauthenticationgrailsspring-securityx509

Getting Grails , Spring Security Core Plugin, and Tomcat to use X.509 Certificate authentication


I'm creating a simple prototype to use as a model for changing how an existing Grails application that uses Spring Security Core authenticates the end user.

Grails version - 2.3.11 Spring Security Core version - 1.2.7.3

I created my own CA, issued my own server certificate, issued a client certificate, and configured my Tomcat 7 instance and browser to use said certification.

I modified the Tomcat server.xml as follows:

<Connector port="8443" protocol="org.apache.coyote.http11.Http11Protocol"
               maxThreads="150" SSLEnabled="true" scheme="https" secure="true"
               truststoreFile="/Users/mybox/Documents/apache-tomcat-7.0.64/conf/cacerts.jks" truststorePass="password"
               keystoreFile="/Users/mybox/Documents/apache-tomcat-7.0.64/conf/keystore.jks" keystorePass="password"
               clientAuth="true" sslProtocol="TLS" />

In the Grails project itself, I've modified several files:

In Config.groovy I added:

// Added by the Spring Security Core plugin:
grails.plugins.springsecurity.userLookup.userDomainClassName = 'com.pkiprototype.User'
grails.plugins.springsecurity.userLookup.authorityJoinClassName = 'com.pkiprototype.UserRole'
grails.plugins.springsecurity.authority.className = 'com.pkiprototype.Role'
grails.plugin.springsecurity.useX509 = true
grails.plugin.springsecurityx509.continueFilterChainOnUnsuccessfulAuthentication = true
grails.plugin.springsecurity.controllerAnnotations.staticRules = [
    '/':                ['permitAll'],
    '/sample':          ['permitAll'],
    '/index':           ['permitAll'],
    '/index.gsp':       ['permitAll'],
    '/assets/**':       ['permitAll'],
    '/**/js/**':        ['permitAll'],
    '/**/css/**':       ['permitAll'],
    '/**/images/**':    ['permitAll'],
    '/**/favicon.ico':  ['permitAll'],
    '/dbconsole/**':  ['permitAll']
]

In BuildConfig.groovy I added:

grails.tomcat.keystorePath = "${basedir}/grails-app/conf/keystore.jks"
grails.tomcat.keyStorePassword = "password"

I've got the keystore.jks at that location, too.

I've got a super simple Controller:

package com.pkiprototype

class SampleController {

    def springSecurityService

        def User = {
            def currentUser = springSecurityService. getCurrentUser ()
            render "Welcome user:"  +   currentUser. username + "\n Your role is:" + currentUser. getAuthorities ()
        }

    def index() { }
}

I've got a user in my bootstrap.groovy whose username matches the CN of the user in my client certificate, that I've loaded into my browser.

When I hit the page https://localhost:8443/pkiprototype-0.1/sample/User, I am prompted to use my certificate. But then the page throws an error "An error has occurred", and when I check the Tomcat's stacktrace.log I get the following:

2015-09-15 15:27:25,420 [http-bio-8443-exec-7] ERROR StackTrace  - Full Stack Trace:
java.lang.NullPointerException: Cannot get property 'username' on null object
    at com.pkiprototype.SampleController$_closure1.doCall(SampleController.groovy:9)
    at grails.plugin.cache.web.filter.PageFragmentCachingFilter.doFilter(PageFragmentCachingFilter.java:189)
    at grails.plugin.cache.web.filter.AbstractFilter.doFilter(AbstractFilter.java:63)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
    at java.lang.Thread.run(Thread.java:745)

What am I missing? Shouldn't the certificate I supplied to the browser be read and used by Spring Security Core, to find the user and thus and pass it along to the controller action, and the rendered page?


Solution

  • I figured it out!

    The issue was this line:

    grails.plugin.springsecurity.useX509 = true
    

    The correct syntax for VERSION 1 of Spring Security Core plugin references in Config.groovy is grails.plugins

    I changed the line to

    grails.plugins.springsecurity.useX509 = true
    

    And voila! It works.

    Please note, that a VERY valuable resource for help (which was super helpful to me with this issue), is the Grails Community SLACK, http://slack-signup.grails.org/