Как предоставить динамические учетные данные (имя пользователя и пароль) для веб-службы с помощью плагина Grails-cxf

Я использую этот удивительный плагин, http://grails.org/plugin/cxf-client, для использования веб-службы первого контракта с безопасностью.

Так что у меня уже есть что-то вроде этого в моей конфигурации:

 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 ответа

Решение

Предполагая, что вы используете плагин Spring Security, и учетные данные WS, которые вы хотите использовать, являются свойствами вашего User объект предметной области, тогда что-то вроде этого должно работать (не проверено):

SRC / заводной / ком / 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-приложение / 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, то вместо springSecurityService вы можете использовать Spring RequestContextHolder, чей статический getRequestAttributes() Метод возвращает GrailsWebRequest, обрабатываемый текущим потоком, или ноль, если текущий поток не обрабатывает запрос (например, если это фоновое задание).

RequestContextHolder.requestAttributes?.session?.wsUser

Или, если это атрибуты запроса (т.е. вы сказали request.wsUser = 'realUsername' в контроллере) вы могли бы использовать RequestContextHolder.requestAttributes?.currentRequest?.wsUser,

Вот общий ответ для всех остальных:

1. Config.groovy

cxf {
    client {

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

2. Ресурсы.гроовы

import com.company.package.MySecurityInterceptor

beans = {

nameOfSecurityInterceptorBean(MySecurityInterceptor) {
}

}

3. Создайте MySecurityInterceptor в com.company.package

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)
       }
    }
Другие вопросы по тегам