Как предоставить динамические учетные данные (имя пользователя и пароль) для веб-службы с помощью плагина 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)
}
}