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")]
}
}
}
}
}
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.
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 :-/
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,
Great video, wish it was available when I was trying to figure this stuff out.
Glad you found this code useful.