wsaa sistemas factura electronica agiles java web-services jax-ws webservice-client

java - sistemas - ¿Cómo agregar encabezado a solicitud SOAP?



sistemas agiles (4)

Clase principal de muestra:

package test; import java.util.ArrayList; import java.util.List; import javax.xml.ws.BindingProvider; import javax.xml.ws.handler.Handler; // next headers is generated from "NetBeans New Webservice Client" import sk.firma.wstest.definitions.*; import sk.firma.wstest.schemas.*; /** * * @author Jan */ public class TestWSService { /** * @param args the command line arguments */ public static void main(String[] args) { try { WsService service = new WsService(); Ws port = service.getWsKsSoap11(); // This is the block that apply the Ws Security to the request BindingProvider bindingProvider = (BindingProvider) port; @SuppressWarnings("rawtypes") List<Handler> handlerChain = new ArrayList<Handler>(); handlerChain.add(new WSSecurityHeaderSOAPHandler("username", "password")); bindingProvider.getBinding().setHandlerChain(handlerChain); // Initialize and Run Service InVal inVal = new InVal(); ReturnValue retVal = port.test(inVal); } catch (Exception e) { e.printStackTrace(); } } }

Y ahora WS-Security Header SOAP Handler:

package test; import java.text.DateFormat; import java.text.SimpleDateFormat; import java.util.Date; import java.util.HashSet; import java.util.Set; import java.util.TimeZone; import javax.xml.namespace.QName; import javax.xml.soap.SOAPElement; import javax.xml.soap.SOAPEnvelope; 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; public class WSSecurityHeaderSOAPHandler implements SOAPHandler<SOAPMessageContext> { private static final String URL_WSSE_NAMESPACE = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"; private static final String URL_WSU_NAMESPACE = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"; private final String usernameText; private final String passwordText; public WSSecurityHeaderSOAPHandler(String usernameText, String passwordText) { this.usernameText = usernameText; this.passwordText = passwordText; } public String getCurrentDateTime() { /* e.g. 2001-10-13T09:00:00Z */ final SimpleDateFormat FORMATTER_DATETIME_NO_MS = new SimpleDateFormat("yyyy-MM-dd''T''HH:mm:ss"); DateFormat dfETC = FORMATTER_DATETIME_NO_MS; dfETC.setTimeZone(TimeZone.getTimeZone("CET")); StringBuffer dateETC = new StringBuffer(dfETC.format(new Date())); dateETC.append(''Z''); return dateETC.toString(); } public String getCurrentDateTimePlusDelay(long delayInSeconds) { /* e.g. 2001-10-13T09:00:00Z */ final SimpleDateFormat FORMATTER_DATETIME_NO_MS = new SimpleDateFormat("yyyy-MM-dd''T''HH:mm:ss"); DateFormat dfETC = FORMATTER_DATETIME_NO_MS; dfETC.setTimeZone(TimeZone.getTimeZone("CET")); Date date = new Date(); long timeInMsecs = date.getTime(); date.setTime(timeInMsecs + delayInSeconds*1000L); StringBuffer dateETC = new StringBuffer(dfETC.format(date)); dateETC.append(''Z''); return dateETC.toString(); } @Override public boolean handleMessage(SOAPMessageContext soapMessageContext) { Boolean outboundProperty = (Boolean) soapMessageContext.get(MessageContext.MESSAGE_OUTBOUND_PROPERTY); if (outboundProperty) { try { SOAPEnvelope soapEnvelope = soapMessageContext.getMessage().getSOAPPart().getEnvelope(); SOAPHeader header = soapEnvelope.getHeader(); if (header == null) { header = soapEnvelope.addHeader(); } SOAPElement securityHeaderElement = header.addChildElement("Security", "wsse", URL_WSSE_NAMESPACE); securityHeaderElement.addAttribute(soapEnvelope.createName("S:mustUnderstand"), "1"); // Add Timestamp element to "Security" soapHeaderElement // Sample: <u:Timestamp> // <u:Created>2011-10-13T08:20:01.183Z</u:Created> // <u:Expires>2011-10-13T17:25:01.183Z</u:Expires> // </u:Timestamp> javax.xml.soap.Name timestampElementName = soapEnvelope.createName("Timestamp", "wsu", URL_WSU_NAMESPACE); SOAPElement timestampSOAPElement = securityHeaderElement.addChildElement(timestampElementName); String created = getCurrentDateTime(); String expires = getCurrentDateTimePlusDelay(60L*60L); /* 60 minutes delay */ // Add Created to Timestamp SOAPElement createdSOAPElement = timestampSOAPElement .addChildElement("Created"/* local name */, "wsu" /* prefix */, URL_WSU_NAMESPACE); createdSOAPElement.addTextNode(created); // Add Expires to Timestamp SOAPElement expiresSOAPElement = timestampSOAPElement .addChildElement("Expires"/* local name */, "wsu" /* prefix */,URL_WSU_NAMESPACE); expiresSOAPElement.addTextNode(expires); // Add usernameToken to "Security" soapHeaderElement javax.xml.soap.Name usernameTokenElementName = soapEnvelope.createName("UsernameToken", "wsse", URL_WSSE_NAMESPACE); SOAPElement usernameTokenSOAPElement = securityHeaderElement.addChildElement(usernameTokenElementName); // Add Username to usernameToken SOAPElement userNameSOAPElement = usernameTokenSOAPElement .addChildElement("Username"/* local name */, "wsse" /* prefix */, URL_WSSE_NAMESPACE); userNameSOAPElement.addTextNode(this.usernameText); // Add password to UsernameToken javax.xml.soap.Name passwordElementName = soapEnvelope.createName("Password", "wsse", URL_WSSE_NAMESPACE); SOAPElement passwordSOAPElement = usernameTokenSOAPElement.addChildElement(passwordElementName); /* Add "Type" attribute to <Password> header element */ //passwordSOAPElement.addAttribute(soapEnvelope.createName("Type", "", ""), // "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText"); passwordSOAPElement.addTextNode(this.passwordText); } catch (Exception e) { throw new RuntimeException("Error on wsSecurityHandler: " + e.getMessage()); } } return true; } @Override public void close(MessageContext context) { // TODO Auto-generated method stub } @Override public boolean handleFault(SOAPMessageContext context) { // TODO Auto-generated method stub return true; } @Override public Set<QName> getHeaders() { // throw new UnsupportedOperationException("Not supported yet."); final QName securityHeader = new QName("http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd", "Security", "wsse"); final HashSet headers = new HashSet(); headers.add(securityHeader); return headers; } }

Intento invocar el servicio web HTTPS SOAP a través del código java:

URL url = new URL("https://somehost:8181/services/"SomeService?wsdl"); QName qname = new QName("http://services.somehost.com/", "SomeService"); Service service = Service.create(url, qname); SomeService port = service.getPort(SomeService .class); port.doSomething();

Pero consigue la excepción:

threw an unexpected exception: javax.xml.ws.soap.SOAPFaultException: Security Requirements not met - No Security header in message

Cuando analicé la solicitud de muestra correcta, determiné que tenía que contener el encabezado:

<S:Header> <To xmlns="http://www.w3.org/2005/08/addressing">http://somehost:8181/services/SomeService</To> <Action xmlns="http://www.w3.org/2005/08/addressing">https://somehost:8181/services/"SomeService/doSomethingRequest</Action> <ReplyTo xmlns="http://www.w3.org/2005/08/addressing"> <Address>http://www.w3.org/2005/08/addressing/anonymous</Address> </ReplyTo> <MessageID xmlns="http://www.w3.org/2005/08/addressing">uuid:3428539e-d645-72ae-adc0-5423c1e68942</MessageID> <wsse:Security S:mustUnderstand="true"> <wsu:Timestamp wsu:Id="_1" xmlns:ns14="http://docs.oasis-open.org/ws-sx/ws-secureconversation/200512" xmlns:ns13="http://schemas.xmlsoap.org/soap/envelope/"> <wsu:Created>2013-01-15T16:36:30Z</wsu:Created> <wsu:Expires>2014-01-15T14:06:30Z</wsu:Expires> </wsu:Timestamp> </wsse:Security>

Entonces, ¿cómo agregar este encabezado a mi solicitud SOAP?


He seguido los pasos mencionados por @LaabidiRaissi. El código funciona bien pero nunca agrega el elemento de seguridad debajo del encabezado. Lo he confirmado imprimiendo el mensaje SOAP saliente a System.out. Después de una investigación profunda, he encontrado que el mensaje SOAP debe guardarse explícitamente para reflejar el encabezado del mensaje actualizado.

soapMessage.saveChanges();

Para más referencia - Revisa este enlace.


Personalmente agrego dos clases: HeaderHandler y HeaderHandlerResolver:

import java.util.HashSet; import java.util.Set; import javax.xml.namespace.QName; import javax.xml.soap.SOAPElement; import javax.xml.soap.SOAPEnvelope; import javax.xml.soap.SOAPHeader; import javax.xml.soap.SOAPMessage; import javax.xml.ws.handler.MessageContext; import javax.xml.ws.handler.soap.SOAPHandler; import javax.xml.ws.handler.soap.SOAPMessageContext; public class HeaderHandler implements SOAPHandler<SOAPMessageContext> { public boolean handleMessage(SOAPMessageContext smc) { Boolean outboundProperty = (Boolean) smc.get(MessageContext.MESSAGE_OUTBOUND_PROPERTY); if (outboundProperty.booleanValue()) { SOAPMessage message = smc.getMessage(); try { SOAPEnvelope envelope = smc.getMessage().getSOAPPart().getEnvelope(); SOAPHeader header = envelope.addHeader(); SOAPElement security = header.addChildElement("Security", "wsse", "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"); SOAPElement usernameToken = security.addChildElement("UsernameToken", "wsse"); usernameToken.addAttribute(new QName("xmlns:wsu"), "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"); SOAPElement username = usernameToken.addChildElement("Username", "wsse"); username.addTextNode("test"); SOAPElement password = usernameToken.addChildElement("Password", "wsse"); password.setAttribute("Type", "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText"); password.addTextNode("test321"); //Print out the outbound SOAP message to System.out message.writeTo(System.out); System.out.println(""); } catch (Exception e) { e.printStackTrace(); } } else { try { //This handler does nothing with the response from the Web Service so //we just print out the SOAP message. SOAPMessage message = smc.getMessage(); message.writeTo(System.out); System.out.println(""); } catch (Exception ex) { ex.printStackTrace(); } } return outboundProperty; } public Set getHeaders() { // The code below is added on order to invoke Spring secured WS. // Otherwise, // http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd // won''t be recognised final QName securityHeader = new QName( "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd", "Security", "wsse"); final HashSet headers = new HashSet(); headers.add(securityHeader); return headers; } public boolean handleFault(SOAPMessageContext context) { //throw new UnsupportedOperationException("Not supported yet."); return true; } public void close(MessageContext context) { //throw new UnsupportedOperationException("Not supported yet."); } }

Y

import java.util.ArrayList; import java.util.List; import javax.xml.ws.handler.Handler; import javax.xml.ws.handler.HandlerResolver; import javax.xml.ws.handler.PortInfo; public class HeaderHandlerResolver implements HandlerResolver { public List<Handler> getHandlerChain(PortInfo portInfo) { List<Handler> handlerChain = new ArrayList<Handler>(); HeaderHandler hh = new HeaderHandler(); handlerChain.add(hh); return handlerChain; } }

En la clase HeaderHandler, puede agregar las credenciales necesarias. Para usarlos finalmente:

HeaderHandlerResolver handlerResolver = new HeaderHandlerResolver(); service.setHandlerResolver(handlerResolver);


También puede usar Apache wss4j para agregar fácilmente el encabezado y también cifrar su contraseña.

import org.apache.ws.security.WSConstants; import org.apache.ws.security.message.WSSecHeader; import org.apache.ws.security.message.WSSecUsernameToken; import javax.xml.namespace.QName; import javax.xml.soap.SOAPMessage; import javax.xml.soap.SOAPPart; import javax.xml.ws.handler.MessageContext; import javax.xml.ws.handler.soap.SOAPHandler; import javax.xml.ws.handler.soap.SOAPMessageContext; import java.io.ByteArrayOutputStream; import java.util.Set; public class WSSecurityHeaderSOAPHandler implements SOAPHandler<SOAPMessageContext> { private final String usernameText; private final String passwordText; public WSSecurityHeaderSOAPHandler(String usernameText, String passwordText) { this.usernameText = usernameText; this.passwordText = passwordText; } @Override public boolean handleMessage(SOAPMessageContext context) { Boolean outboundProperty = (Boolean) context.get(MessageContext.MESSAGE_OUTBOUND_PROPERTY); if (outboundProperty.booleanValue()) { try { SOAPMessage soapMessage = context.getMessage(); soapMessage.removeAllAttachments(); SOAPPart soappart = soapMessage.getSOAPPart(); WSSecHeader wsSecHeader = new WSSecHeader(); wsSecHeader.insertSecurityHeader(soappart); WSSecUsernameToken token = new WSSecUsernameToken(); token.setPasswordType(WSConstants.PASSWORD_DIGEST); token.setUserInfo(usernameText, passwordText); token.build(soappart, wsSecHeader); soapMessage.saveChanges(); } catch (Exception e) { throw new RuntimeException("Error on wsSecurityHandler: " + e.getMessage()); } } return true; } @Override public boolean handleFault(SOAPMessageContext context) { return false; } @Override public void close(MessageContext context) { } @Override public Set<QName> getHeaders() { return null; } }

Y necesitas actualizar tu solicitud de esta manera:

// This is the block that apply the Ws Security to the request BindingProvider bindingProvider = (BindingProvider) portType; List<Handler> handlerChain = new ArrayList<>(); handlerChain.add(new WSSecurityHeaderSOAPHandler("username", "password")); bindingProvider.getBinding().setHandlerChain(handlerChain);

Dependencia de Maven:

<dependency> <groupId>org.apache.ws.security</groupId> <artifactId>wss4j</artifactId> <version>1.6.19</version> </dependency>