Home > grails, groovy, programming > Adventures in Grails – WS-Security Part 1

Adventures in Grails – WS-Security Part 1

The next step in rewriting the application was to secure the web services with WS-Security. In this post I get a grails version of the xfire wss example of User Token Authentication up and running. To do this I use (of course) the grails xfire plugin.

After creating a grails project and installing the xfire plugin, the first thing to do is to configure the inHandlers:

to do this add the following to the doWithSpring closure in XfireGrailsPlugin.groovy


"xfire.passHandler"(org.codehaus.xfire.demo.PasswordHandler) { bean ->
        }

"xfire.DOMhandler"(org.codehaus.xfire.util.dom.DOMInHandler) { bean ->
        }

"xfire.WSS4JHandler"(org.codehaus.xfire.security.wss4j.WSS4JInHandler) {
    properties = ["passwordCallbackRef":ref("xfire.passHandler"),
                  "action":"UsernameToken"]
        }
"xfire.ValidateUserTokenHandler"(org.codehaus.xfire.demo.ValidateUserTokenHandler) {
        }

ValidateUserTokenHandler and PasswordHandler are part of the example code distributed with xfire. I just copied them into the correct package in src/java/ in this simple grails app. And then add the inHandlers to the org.grails.xfire.ServiceBean


inHandlers = [ref("xfire.DOMhandler"),
              ref("xfire.WSS4JHandler"),
              ref("xfire.ValidateUserTokenHandler")]

See the complete listing for doWithSpring at the end of this post. Now any service you expose with xfire will require (and print) a username and password in a WSS UsernameToken header. The simple service I used to test this is:


class TestService {

    static expose=['xfire']

    boolean transactional = true

    String serviceMethod() {
       return "You did it!!!"
    }
}

I use soapUI to test, here is the request it generated:


      foo
      bar
      2008-03-01T19:49:03.627Z

This is obviously not a perfect solution. You may not want to secure all the web services in your project or at least not all in the same way. After I finish with this project I will have a more general solution to contribute to the grails xfire plugin.But first, I need to do something with the user credentials I am now receiving. Next up, integrating with acegi through the grails acegi plugin

Full doWithSpring listing:


def doWithSpring = {

        "xfire.serviceRegistry"(org.codehaus.xfire.service.DefaultServiceRegistry) { bean->
            bean.getBeanDefinition().setSingleton(true)
        }

        "xfire.transportManager"(org.codehaus.xfire.transport.DefaultTransportManager){ bean->
            bean.getBeanDefinition().setSingleton(true)
            bean.getBeanDefinition().setInitMethodName("initialize")
            bean.getBeanDefinition().setDestroyMethodName("dispose")
        }

        "xfire"(org.codehaus.xfire.DefaultXFire,
                 ref("xfire.serviceRegistry"),
                 ref("xfire.transportManager")) { bean ->
            bean.getBeanDefinition().setSingleton(true)
        }

        "xfire.typeMappingRegistry"(org.codehaus.xfire.aegis.type.DefaultTypeMappingRegistry){ bean ->
            bean.getBeanDefinition().setSingleton(true)
            bean.getBeanDefinition().setInitMethodName("createDefaultMappings");
        }

        "xfire.aegisBindingProvider"(org.codehaus.xfire.aegis.AegisBindingProvider,
            ref("xfire.typeMappingRegistry")) { bean ->
            bean.getBeanDefinition().setSingleton(true)
        }

        "xfire.serviceFactory"(org.codehaus.xfire.service.binding.ObjectServiceFactory,
            ref("xfire.transportManager"), ref("xfire.aegisBindingProvider")) { bean ->
            bean.getBeanDefinition().setSingleton(true)
        }

        "xfire.servletController"(org.codehaus.xfire.transport.http.XFireServletController,
            ref("xfire")) { bean ->
            bean.getBeanDefinition().setSingleton(true)
        }

        "grails.xfire"(org.grails.xfire.ServiceFactoryBean, "grails.xfire") { bean ->
            bean.getBeanDefinition().setInitMethodName("initialize")
            transportManager = ref("xfire.transportManager")
            grailsApplication = ref("grailsApplication", true)
        }

        "xfire.passHandler"(org.codehaus.xfire.demo.PasswordHandler) { bean ->
        }

        "xfire.DOMhandler"(org.codehaus.xfire.util.dom.DOMInHandler) { bean ->
        }

        "xfire.WSS4JHandler"(org.codehaus.xfire.security.wss4j.WSS4JInHandler) {
            properties = ["passwordCallbackRef":ref("xfire.passHandler"),
                          "action":"UsernameToken Timestamp"]
        }

        "xfire.ValidateUserTokenHandler"(org.codehaus.xfire.demo.ValidateUserTokenHandler) {}

        if(application.serviceClasses) {
            application.serviceClasses.each { service ->
                def serviceClass = service.getClazz()
                def exposeList = GrailsClassUtils.getStaticPropertyValue(serviceClass, 'expose')
                if(exposeList!=null && exposeList.contains('xfire')) {
                    def sName = service.propertyName.replaceFirst("Service","XFire")
                    //
                    "${sName}"(org.grails.xfire.ServiceBean){
                        //
                        xfire = ref("xfire")
                        //
                        serviceBean = ref("${service.propertyName}")
                        //
                        serviceClass = service.getClazz()
                        //
                        serviceFactory = ref("grails.xfire")

                        inHandlers = [ref("xfire.DOMhandler"),
                                ref("xfire.WSS4JHandler"),
                                ref("xfire.ValidateUserTokenHandler")]
                    }
                }
            }
        }
    }
  1. Keith Thomas
    May 21, 2009 at 4:19 am | #1

    Thanks for the great article. I’ve tried to follow the instructions above to use the sample code:

    ValidateUserTokenHandler and PasswordHandler

    However, using both the soapUi and an Axis2 client I see the same error in the Grails log,

    ERROR wss4j.WSS4JInHandler – WSS4JInHandler: Request does not contain required Security header

    Any thoughts anyone has would be great.

  2. Keith Thomas
    May 21, 2009 at 10:35 pm | #2

    Solved my own problem. As soon as I decided access my web services via ssl,
    grails run-app -https
    and use the soapui client with an https url on port 8443 then it worked great!

    I knew that my final solution would include both ssl and user token authentication but it didn’t cross my mind that one was dependent on the other. Silly me :-/

  3. Keith Thomas
    May 22, 2009 at 9:56 pm | #3

    As a newbie I found the configuration of the SoapUI a little unexpected so I made this short video to cover how to do it,

    • Andrew Hahn
      May 25, 2009 at 4:40 pm | #4

      Great video, wish it was available when I was trying to figure this stuff out.
      Glad you found this code useful.

  1. March 12, 2008 at 7:17 pm | #1