Цель
Управление заголовком JAXWS на стороне клиента, например добавление токена имени пользователя WSS или регистрация сообщения saop.
Вступление
В телекоммуникационной ИТ-среде и специально для промежуточного программного обеспечения мы редко выполняем всю работу, а скорее делегируем некоторые бизнес-процессы другим уровням. Связь между веб-сервисами широко используется между решениями. Цель этого руководства — познакомиться с использованием обработчика на стороне клиента путем добавления WSS UserToken или регистрации сообщения мыла на консоли.
Добавление необъявленного пользовательского заголовка
Некоторому клиенту Ws необходимо добавить пользовательский заголовок, который не объявлен в WSDL. Добавление токена имени пользователя WSS похоже на добавление этого фрагмента XML в элемент заголовка:
<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>
В отличие от LogicalHandler SOAPHandler имеет доступ ко всему сообщению Soap. Давайте создадим локальный компонент без сохранения состояния WSSUsernameTokenSecurityHandler, расширяющий SOAPHandler для создания предыдущего заголовка.
Ниже класса WSSUsernameTokenSecurityHandler
package me.slim.ouertani;
import java.util.Set;
import java.util.TreeSet;
import javax.annotation.Resource;
import javax.ejb.LocalBean;
import javax.ejb.Stateless;
import javax.xml.namespace.QName;
import javax.xml.soap.*;
import javax.xml.ws.handler.MessageContext;
import javax.xml.ws.handler.soap.SOAPHandler;
import javax.xml.ws.handler.soap.SOAPMessageContext;
@Stateless
@LocalBean
public class WSSUsernameTokenSecurityHandler implements SOAPHandler<SOAPMessageContext> {
@Resource(lookup = "login")
private String login;
@Resource(lookup = "pwd")
private String pwd;
public WSSUsernameTokenSecurityHandler() {
}
@Override
public boolean handleMessage(SOAPMessageContext context) {
Boolean outboundProperty =
(Boolean) context.get(MessageContext.MESSAGE_OUTBOUND_PROPERTY);
if (outboundProperty.booleanValue()) {
try {
SOAPEnvelope envelope = context.getMessage().getSOAPPart().getEnvelope();
SOAPFactory factory = SOAPFactory.newInstance();
String prefix = "wsse";
String uri = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd";
SOAPElement securityElem =
factory.createElement("Security", prefix, uri);
SOAPElement tokenElem =
factory.createElement("UsernameToken", prefix, uri);
tokenElem.addAttribute(QName.valueOf("wsu:Id"), "UsernameToken-2");
tokenElem.addAttribute(QName.valueOf("xmlns:wsu"), "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd");
SOAPElement userElem =
factory.createElement("Username", prefix, uri);
userElem.addTextNode(login);
SOAPElement pwdElem =
factory.createElement("Password", prefix, uri);
pwdElem.addTextNode(pwd);
pwdElem.addAttribute(QName.valueOf("Type"), "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText");
tokenElem.addChildElement(userElem);
tokenElem.addChildElement(pwdElem);
securityElem.addChildElement(tokenElem);
SOAPHeader header = envelope.addHeader();
header.addChildElement(securityElem);
} catch (Exception e) {
e.printStackTrace();
}
} else {
// inbound
}
return true;
}
@Override
public Set<QName> getHeaders() {
return new TreeSet();
}
@Override
public boolean handleFault(SOAPMessageContext context) {
return false;
}
@Override
public void close(MessageContext context) {
//
}
}
Затем объявите StatelessBean и введите оба Webservice через @WebServiceRef и WSSUsernameTokenSecurityHandler через @EJB. Метод init обратного вызова добавит HandlerResolver к службе. Ниже полная реализация
@Stateless
@LocalBean
public class WebServiceClientBean {
@WebServiceRef()
private WsService service;
@EJB
private WSSUsernameTokenSecurityHandler wSSUsernameTokenSecurityHandler;
@PostConstruct
private void init() {
service.setHandlerResolver( new HandlerResolver() {
@Override
public List<Handler> getHandlerChain(PortInfo portInfo) {
List<Handler> handlerList = new ArrayList<Handler>();
handlerList.add(wSSUsernameTokenSecurityHandler);
return handlerList;
}
});
}
public WsResponse getService(WsRequest wsRequest) {
WsPort port = service.getPort();
return port.invoqueService(wsRequest);
}
}
Регистрация сообщений SOAP
Ведение журнала обмена XML-сообщениями также может быть с использованием обработчиков, ниже обработчика фрагмента scala, используемого для входа и выхода сообщений:
class RequestResponsePrinter extends SOAPHandler[SOAPMessageContext] with LogHelper {
override def getHeaders(): Set[QName] = {
return new TreeSet();
}
override def handleMessage(context: SOAPMessageContext): Boolean = {
val sb = new ToStringBuilder(this).append("Call operation", "handleMessage");
L.debug(sb);
val outboundProperty = context.get(MessageContext.MESSAGE_OUTBOUND_PROPERTY).asInstanceOf[Boolean]
try {
val msg = context.getMessage()
val out = new ByteArrayOutputStream();
msg.writeTo(out);
val strMsg = new String(out.toByteArray());
L.debug("outbound : "+outboundProperty.booleanValue()+" [" + strMsg + "]");
} catch {
case e: Exception =>
L.error(sb.append("EXCEPTION", e.getMessage()), e);
}
return true;
}
override def handleFault(context: SOAPMessageContext): Boolean = {
return false;
}
override def close(context: MessageContext) {
//
}
}
- Scala-версия обработчика обработчиков
new HandlerResolver() {
override def getHandlerChain(portInfo: PortInfo): java.util.List[javax.xml.ws.handler.Handler[_ <: javax.xml.ws.handler.MessageContext]] = {
import scala.collection.mutable.ArrayBuffer
val handlerList = ArrayBuffer[Handler[_ <: javax.xml.ws.handler.MessageContext]]()
handlerList += new RequestResponsePrinter()
return handlerList
}
};
Вывод
Мы интенсивно использовали обработчик на стороне клиента. На стороне сервера все может немного измениться, продолжение следует …