如何使用Grails-cxf插件为Web服务提供动态凭据(用户名和密码)

时间:2012-12-04 11:22:37

标签: grails groovy cxf credentials wss4j

我正在使用这个非常棒的插件http://grails.org/plugin/cxf-client来使用具有安全性的契约优先Web服务。

所以我的配置中已经有类似的东西了:

 cxf {
   client {
    cybersourceClient {           
        clientInterface = com.webhost.soapProcessor
        serviceEndpointAddress = "https://webhost/soapProcessor"
        wsdl = "https://webhost/consumeMe.wsdl"
        secured = true
        username = "myUname"
        password = "myPwd"
    }   
}

这非常有效,但我现在要做的是为我的用户提供输入用户名和密码的权限,以便他们输入用户名和密码来使用该服务。有谁知道怎么做?

我怀疑它在演示项目中使用了Custom In Interceptor:

package com.cxf.demo.security

import com.grails.cxf.client.CxfClientInterceptor

import org.apache.cxf.ws.security.wss4j.WSS4JOutInterceptor
import org.apache.ws.security.WSPasswordCallback
import org.apache.ws.security.handler.WSHandlerConstants

import javax.security.auth.callback.Callback
import javax.security.auth.callback.CallbackHandler
import javax.security.auth.callback.UnsupportedCallbackException


class CustomSecurityInterceptor implements CxfClientInterceptor {

String pass
String user


   WSS4JOutInterceptor create() {
    Map<String, Object> outProps = [:]
    outProps.put(WSHandlerConstants.ACTION, org.apache.ws.security.handler.WSHandlerConstants.USERNAME_TOKEN)
    outProps.put(WSHandlerConstants.USER, user)
    outProps.put(WSHandlerConstants.PASSWORD_TYPE, org.apache.ws.security.WSConstants.PW_TEXT)
    outProps.put(WSHandlerConstants.PW_CALLBACK_REF, new CallbackHandler() {

        void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException {
            WSPasswordCallback pc = (WSPasswordCallback) callbacks[0]
            pc.password = pass
            pc.identifier = user
        }
    })

    new WSS4JOutInterceptor(outProps)
}
}

但由于我没有实例化这个拦截器,或者理解它是如何实例化的,我不知道如何获得拦截器中使用的用户凭据。

有谁知道怎么做/有任何示例代码?

谢谢!

2 个答案:

答案 0 :(得分:0)

假设您正在使用Spring Security插件,并且您要使用的WS凭据是User域对象的属性,那么这样的东西应该可以工作(未经测试):

<强>的src /常规/ COM / CXF /演示/安全/ CustomSecurityInterceptor.groovy

package com.cxf.demo.security

import com.grails.cxf.client.CxfClientInterceptor

import org.apache.cxf.ws.security.wss4j.WSS4JOutInterceptor
import org.apache.ws.security.WSPasswordCallback
import org.apache.ws.security.handler.WSHandlerConstants

import javax.security.auth.callback.Callback
import javax.security.auth.callback.CallbackHandler
import javax.security.auth.callback.UnsupportedCallbackException


class CustomSecurityInterceptor implements CxfClientInterceptor {

   def springSecurityService
   def grailsApplication

   WSS4JOutInterceptor create() {
    Map<String, Object> outProps = [:]
    outProps.put(WSHandlerConstants.ACTION, org.apache.ws.security.handler.WSHandlerConstants.USERNAME_TOKEN)
    // take default username from config
    outProps.put(WSHandlerConstants.USER, grailsApplication.config.cxf.client.cybersourceClient.username)
    outProps.put(WSHandlerConstants.PASSWORD_TYPE, org.apache.ws.security.WSConstants.PW_TEXT)
    outProps.put(WSHandlerConstants.PW_CALLBACK_REF, new CallbackHandler() {

        void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException {
            WSPasswordCallback pc = (WSPasswordCallback) callbacks[0]
            // take password from current user, fall back to config if no
            // user currently logged in/not in a request thread, etc.
            pc.password = (springSecurityService.currentUser?.wsPassword
               ?: grailsApplication.config.cxf.client.cybersourceClient.password)
        }
    })

    new CustomWSS4JOutInterceptor(springSecurityService, outProps)
  }
}

class CustomWSS4JOutInterceptor extends WSS4JOutInterceptor {
  def springSecurityService

  CustomWSS4JOutInterceptor(springSecurityService, outProps) {
    super(outProps)
    this.springSecurityService = springSecurityService
  }

  // overridden to fetch username dynamically from logged in user
  // but fall back on config if no user/not on a request hander thread
  public Object getOption(String key) {
    if(key == WSHandlerConstants.USER && springSecurityService.currentUser?.wsUser) {
      return springSecurityService.currentUser?.wsUser
    } else return super.getOption(key)
  }
}

<强>的grails-app / CONF /弹簧/ resources.groovy

import com.cxf.demo.security.CustomSecurityInterceptor
beans = {
  customSecurityInterceptor(CustomSecurityInterceptor) {
    springSecurityService = ref('springSecurityService')
    grailsApplication = ref('grailsApplication')
  }
}

并在配置中,将secured = true替换为securityInterceptor = 'customSecurityInterceptor'

如果您不使用Spring Security,则相同的模式将起作用。关键位是回调处理程序

            pc.password = (springSecurityService.currentUser?.wsPassword
               ?: grailsApplication.config.cxf.client.cybersourceClient.password)

getOption

中的用户名逻辑
    if(key == WSHandlerConstants.USER && springSecurityService.currentUser?.wsUser) {
      return springSecurityService.currentUser?.wsUser

例如,如果用户名和密码存储在HTTP会话中,那么您可以使用Spring RequestContextHolder而不是springSecurityService,其静态getRequestAttributes()方法返回正在处理的GrailsWebRequest当前线程,如果当前线程没有处理请求,则为null(例如,如果它是后台作业)。

RequestContextHolder.requestAttributes?.session?.wsUser

或者,如果他们是请求属性(即您在控制器中说过request.wsUser = 'realUsername'),则可以使用RequestContextHolder.requestAttributes?.currentRequest?.wsUser

答案 1 :(得分:0)

以下是其他任何人的通用答案:

<强> 1。 Config.groovy中

cxf {
    client {

        nameOfClient {
            clientInterface = com.webhost.soapProcessor
            serviceEndpointAddress = "https://webhost/soapProcessor"
            wsdl = "https://webhost/soapProcessorconsumeMe.wsdl"
            secured = true
            securityInterceptor = "nameOfSecurityInterceptorBean"
        }
    }
}

<强> 2。 Resources.groovy

import com.company.package.MySecurityInterceptor

beans = {

nameOfSecurityInterceptorBean(MySecurityInterceptor) {
}

}

第3。在com.company.package

下创建一个MySecurityInterceptor
package com.company.package;

import com.grails.cxf.client.CxfClientInterceptor

import org.apache.cxf.ws.security.wss4j.WSS4JOutInterceptor
import org.apache.ws.security.WSPasswordCallback
import org.apache.ws.security.handler.WSHandlerConstants

import javax.security.auth.callback.Callback
import javax.security.auth.callback.CallbackHandler
import javax.security.auth.callback.UnsupportedCallbackException

import org.springframework.web.context.request.RequestContextHolder

class MySecurityInterceptor implements CxfClientInterceptor {


WSS4JOutInterceptor create() {
    Map<String, Object> outProps = [:]
    outProps.put(WSHandlerConstants.ACTION, org.apache.ws.security.handler.WSHandlerConstants.USERNAME_TOKEN)
    outProps.put(WSHandlerConstants.USER, user)
    outProps.put(WSHandlerConstants.PASSWORD_TYPE, org.apache.ws.security.WSConstants.PW_TEXT)
    outProps.put(WSHandlerConstants.PW_CALLBACK_REF, new CallbackHandler() {

                void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException {
                    WSPasswordCallback pc = (WSPasswordCallback) callbacks[0]
                    def requestObj = RequestContextHolder.requestAttributes?.currentRequest
                    pc.password = requestObj.soapPassword
                    pc.identifier = requestObj.soapIdentifier
                }
            })

    new WSS4JOutInterceptor(outProps)
}
}

<强> 4。现在我们需要将用户名和密码放入请求(线程安全)中,以便拦截器取出:

    import com.company.package.MySecurityInterceptor
    class MySoapSendingController {

    SoapProcessor nameOfClient


    def index() {

    request['soapIdentifier'] = "usernameToUse"
    request['soapPassword'] = "passwordToUse"


                 ...

        ReplyMessage replyMsg = nameOfClient.makeSOAPRequest(request)
       }
    }