Цель
Управление заголовком JAXWS на стороне сервера, например чтение токена имени пользователя WSS, запись сообщения saop и публикация определенного заголовка.
Вступление
В телекоммуникационной ИТ-среде и, в частности, в промежуточном программном обеспечении, веб-службы интенсивно используют связи между решениями. Цель этого руководства — представить использование обработчика на стороне сервера путем публикации определенного заголовка, чтения WSS UserToken или регистрации сообщения мыла на консоли.
Этот учебник основан на Scala, Java-версия может быть легко переведена.
Разобрать необъявленный пользовательский заголовок
Давайте рассмотрим, что нам нужно прочитать WSS UserToken, не опубликованный в нашем WSDL:
<soapenv:Header> <wsse:Security xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"> <wsse:UsernameToken wsu:Id="UsernameToken-1"> <wsse:Username>login</wsse:Username> <wsse:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText">XXXX</wsse:Password> </wsse:UsernameToken> </wsse:Security> </soapenv:Header>
Для разбора этого читателя нам нужно расширить класс сообщения SoapHandler, ovveride handle для заголовка и пароля для контекста, ниже константы и полного класса
object AuthenticationHandlerConstants { val REQUEST_USERID: String = "authn_userid"; val REQUEST_PASSWORD: String = "authn_password"; val AUTHN_PREFIX: String = "wsse"; val AUTHN_LNAME: String = "Security"; val AUTHN_URI: String = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"; val AUTHN_STAUTS: String = "authnStatus"; val LOGIN: String = "login" val PWD: String = "passwd" }
import java.io.PrintStream; import java.util.HashSet; import java.util.Iterator; import java.util.Set; import javax.xml.namespace.QName; import javax.xml.soap.SOAPElement; import javax.xml.soap.SOAPHeader; import javax.xml.ws.handler.MessageContext; import javax.xml.ws.handler.soap.SOAPHandler; import javax.xml.ws.handler.soap.SOAPMessageContext; import org.apache.log4j.Logger; /** * @author Slim Ouertani */ class SecuritySOAPHandler extends SOAPHandler[SOAPMessageContext] { private val L = Logger.getLogger(classOf[SecuritySOAPHandler]); override def getHeaders(): Set[QName] = { val securityHeader = new QName("http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd", "Security", "wsse"); val headers = new HashSet[QName](); headers.add(securityHeader); return headers; } override def handleMessage(soapMessageContext: SOAPMessageContext): Boolean = { try { val outMessageIndicator = soapMessageContext.get(MessageContext.MESSAGE_OUTBOUND_PROPERTY).asInstanceOf[Boolean]; if (!outMessageIndicator) { val envelope = soapMessageContext.getMessage().getSOAPPart().getEnvelope() val header = envelope.getHeader() if (header == null) { L.warn("No headers found in the input SOAP request") } else { processSOAPHeader(header) match { case Some(_processSOAPHeader) => { soapMessageContext.put(AuthenticationHandlerConstants.AUTHN_STAUTS, java.lang.Boolean.TRUE); soapMessageContext.put(AuthenticationHandlerConstants.LOGIN, _processSOAPHeader._1); soapMessageContext.put(AuthenticationHandlerConstants.PWD, _processSOAPHeader._2); } case None => { soapMessageContext.put(AuthenticationHandlerConstants.AUTHN_STAUTS, java.lang.Boolean.FALSE); soapMessageContext.put(AuthenticationHandlerConstants.LOGIN, ""); soapMessageContext.put(AuthenticationHandlerConstants.PWD, ""); } } } } } catch { case ex: Exception => L.error(ex.getMessage(), ex); } soapMessageContext.setScope(AuthenticationHandlerConstants.AUTHN_STAUTS, MessageContext.Scope.APPLICATION); soapMessageContext.setScope(AuthenticationHandlerConstants.LOGIN, MessageContext.Scope.APPLICATION); soapMessageContext.setScope(AuthenticationHandlerConstants.PWD, MessageContext.Scope.APPLICATION); return true; } private def processSOAPHeaderInfo(e: SOAPElement): (String,String) = { var _id: String = null var _password: String = null val childElements = e.getChildElements(new QName(AuthenticationHandlerConstants.AUTHN_URI, "UsernameToken")); while (childElements.hasNext()) { val usernameToken = childElements.next(); // loop through child elements usernameToken match { case child: SOAPElement => { val childElements1 = child.getChildElements(new QName(AuthenticationHandlerConstants.AUTHN_URI, "Username")); val childElements2 = child.getChildElements(new QName(AuthenticationHandlerConstants.AUTHN_URI, "Password")); while (childElements1.hasNext()) { val next = childElements1.next(); next match { case l: SOAPElement => { val value = l.getValue(); _id = value; } } } while (childElements2.hasNext()) { val next = childElements2.next(); next match { case l: SOAPElement => { val value = l.getValue() _password = value; } } } } } } return (_id, _password); } private def processSOAPHeader(sh: SOAPHeader): Option[(String,String)] = { var authenticated: Option[(String,String)] = None val childElems = sh.getChildElements(new QName( AuthenticationHandlerConstants.AUTHN_URI, AuthenticationHandlerConstants.AUTHN_LNAME)); // iterate through child elements while (childElems.hasNext()) { val child = childElems.next().asInstanceOf[SOAPElement] authenticated = Some(processSOAPHeaderInfo(child)) } return authenticated } override def handleFault(soapMessageContext: SOAPMessageContext): Boolean = false }
В отличие от клиентской стороны нам нужно добавить xml-файл
handler.xml, чтобы включить предыдущий обработчик
<?xml version="1.0" encoding="UTF-8"?> <handler-chains xmlns="http://java.sun.com/xml/ns/javaee"> <handler-chain> <handler> <handler-name>SecuritySOAPHandler</handler-name> <handler-class>me.ouertani.slim.SecuritySOAPHandler</handler-class> </handler> </handler-chain> </handler-chains>
в декларации веб-сервиса:
- Цепочка этого обработчика: @HandlerChain (file = «handler.xml»)
- Вставить WebServiceContext: @Resource private var wsContext: WebServiceContext = _
- Получить сообщениеContext: val msgContext = wsContext.getMessageContext ()
- Проверьте сохраненные значения: val authnStatus = msgContext.get (AuthenticationHandlerConstants.AUTHN_STAUTS)
@WebService(serviceName = "WsService", targetNamespace = "http://slim.ouertani.me) @HandlerChain(file = "handler.xml") @Stateless() @Interceptors(Array(classOf[TracingInterceptor])) class WsService extends LogHelper{ @Resource private var wsContext: WebServiceContext = _ def authenticate() { val msgContext = wsContext.getMessageContext() val authnStatus = msgContext.get(AuthenticationHandlerConstants.AUTHN_STAUTS) if (authnStatus == null || !authnStatus.asInstanceOf[Boolean]) { L.warn("header authentification not found"); throw AuthenticationHeaderNotFound.toFaultResponse } try { val login = msgContext.get("login").asInstanceOf[String] val passwd = msgContext.get("passwd").asInstanceOf[String] L.info("user " + login + "pwd" + passwd + "try this service") } catch { case ex: Exception => L.warn(" exception to parse header authentification", ex); throw AuthenticationHeaderNotFound.toFaultResponse } } }
Добавление пользовательского заголовка
Публикация пользовательского заголовка проще, чем использование предыдущего обработчика:
1-First позволяет добавить пользовательский класс заголовка
import scala.reflect.BeanProperty import javax.xml.bind.annotation.{XmlType , XmlAccessType, XmlElement, XmlAccessorType}; /** * @author Slim Ouertani */ @XmlAccessorType(XmlAccessType.FIELD) @XmlType(name = "Header", propOrder = Array( "username", "password" )) class UsernameToken { @BeanProperty @XmlElement(required = true, nillable = false ) var username : String= "" @BeanProperty @XmlElement(required = true, nillable = false ) var password : String= "" }
2 — опубликовать этот заголовок, используя
@WebParam (name = «usernameToken», header = true)
def execute (@WebParam(name = "request" ) request : Request, @WebParam (name = "usernameToken",header = true) header) { .... }
3- извлечение поля пользователя и пароля аналогично доступу к свойствам класса:
val username = ""+header.getUsername() val password = ""+header.getPassword()
4 — обратите внимание, что предыдущий заголовок объявлен в методе WSDL и выдает этот вывод на интерфейс soapUI
<soapenv:Header> <ws:usernameToken> <username>login</username> <password>XXXXX</password> </ws:usernameToken> </soapenv:Header>
Вывод
Публикация определенного заголовка с использованием JAX-WS проще, чем с использованием обработчика. В противном случае обработчики на стороне сервера полезны для других целей, таких как регистрация сообщений ввода / вывода.