Цель
Управление заголовком 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 проще, чем с использованием обработчика. В противном случае обработчики на стороне сервера полезны для других целей, таких как регистрация сообщений ввода / вывода.