DHL-SOAP API:如何为CreateShipmentOrderRequest添加SOAP认证头
问题原因
你当前代码添加的是HTTP协议层的Basic认证头,和接口要求的SOAP信封内的XML格式认证头完全是两个维度的内容,服务端解析请求时读不到要求的Authentification节点,就会一直返回登录失败。
你之前写的HTTP头添加逻辑可以直接删除,该接口不识别HTTP层的Basic认证。
解决方案
推荐两种实现方式,优先选第一种,代码改动量最小;如果因为项目使用的JAX-WS实现(CXF/Metro/Axis2)差异导致第一种不生效,用第二种Handler方案,通用性最强。
方式一:直接构造SOAP头注入请求上下文
不需要额外新增类,直接在原有初始化port的逻辑后追加SOAP头构造代码即可:
import javax.xml.namespace.QName; import javax.xml.soap.SOAPConstants; import javax.xml.soap.SOAPElement; import javax.xml.soap.SOAPFactory; import javax.xml.ws.BindingProvider; import javax.xml.ws.soap.SOAPBinding; import java.util.ArrayList; import java.util.List; import java.util.Map; // 原有初始化代码保留 GVAPI20De service = new GVAPI20De(); GKV3XAPIServicePortType port = service.getGKVAPISOAP11Port0(); Map<String, Object> req_ctx = ((BindingProvider)port).getRequestContext(); req_ctx.put(BindingProvider.ENDPOINT_ADDRESS_PROPERTY, CvpConstants.DHL_WSDL); req_ctx.put("javax.xml.ws.client.connectionTimeout", "60000"); // --------------- 新增以下认证头构造逻辑 --------------- // DHL接口cis前缀对应的固定命名空间,不要写错 final String CIS_NAMESPACE = "http://dhl.de/webservice/cisbase"; try { SOAPElement authNode = SOAPFactory.newInstance() .createElement(new QName(CIS_NAMESPACE, "Authentification", "cis")); // 拼接用户名节点 SOAPElement userNode = authNode.addChildElement(new QName(CIS_NAMESPACE, "user", "cis")); userNode.setTextContent(CvpConstants.DHL_USER); // 拼接签名(密码)节点 SOAPElement signatureNode = authNode.addChildElement(new QName(CIS_NAMESPACE, "signature", "cis")); signatureNode.setTextContent(CvpConstants.DHL_PASSWORD); // 把构造好的头注入请求上下文 List<SOAPElement> headers = new ArrayList<>(); headers.add(authNode); // 如果是Sun JDK自带的Metro实现用下面的key req_ctx.put("com.sun.xml.ws.api.message.Headers", headers.toArray(new SOAPElement[0])); // 如果是Apache CXF实现,注释掉上面一行,改用下面的key // req_ctx.put("javax.xml.ws.soap.headers", headers); } catch (Exception e) { throw new RuntimeException("构造DHL SOAP认证头失败", e); } // --------------- 新增逻辑结束 --------------- // 原有调用逻辑不变 CreateShipmentOrderResponse createShipmentOrder = port.createShipmentOrder(request);
方式二:自定义SOAPHandler注入认证头(通用方案)
这种方式不挑JAX-WS的具体实现,所有场景都能生效。
首先新增一个Handler类:
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; import java.util.Collections; import java.util.Iterator; import java.util.Set; public class DhlAuthSoapHandler implements SOAPHandler<SOAPMessageContext> { private static final String CIS_NAMESPACE = "http://dhl.de/webservice/cisbase"; private final String dhlUser; private final String dhlPassword; public DhlAuthSoapHandler(String dhlUser, String dhlPassword) { this.dhlUser = dhlUser; this.dhlPassword = dhlPassword; } @Override public Set<QName> getHeaders() { return Collections.singleton(new QName(CIS_NAMESPACE, "Authentification", "cis")); } @Override public boolean handleMessage(SOAPMessageContext context) { // 只处理发往服务端的出站请求 Boolean isOutbound = (Boolean) context.get(MessageContext.MESSAGE_OUTBOUND_PROPERTY); if (!isOutbound) { return true; } try { SOAPEnvelope envelope = context.getMessage().getSOAPPart().getEnvelope(); SOAPHeader header = envelope.getHeader() == null ? envelope.addHeader() : envelope.getHeader(); // 避免重复添加认证头 Iterator<?> authNodes = header.getChildElements(new QName(CIS_NAMESPACE, "Authentification")); if (authNodes.hasNext()) { return true; } // 构造认证节点 SOAPElement authNode = header.addChildElement("Authentification", "cis", CIS_NAMESPACE); SOAPElement userNode = authNode.addChildElement("user", "cis", CIS_NAMESPACE); userNode.setTextContent(dhlUser); SOAPElement sigNode = authNode.addChildElement("signature", "cis", CIS_NAMESPACE); sigNode.setTextContent(dhlPassword); } catch (Exception e) { throw new RuntimeException("添加DHL认证头失败", e); } return true; } @Override public boolean handleFault(SOAPMessageContext context) { return true; } @Override public void close(MessageContext context) {} }
然后在你原有初始化port的代码后,绑定这个Handler即可:
// 初始化port完成后追加 BindingProvider bindingProvider = (BindingProvider) port; List<javax.xml.ws.handler.Handler> handlerChain = new ArrayList<>(); handlerChain.add(new DhlAuthSoapHandler(CvpConstants.DHL_USER, CvpConstants.DHL_PASSWORD)); bindingProvider.getBinding().setHandlerChain(handlerChain);
注意事项
- 确认
cis前缀对应的命名空间和你本地生成的客户端代码里的命名空间一致,通常DHL接口的该命名空间固定为http://dhl.de/webservice/cisbase,如果命名空间写错,服务端无法识别认证头,依然会返回登录失败 - 确认传入的用户名格式符合要求,一般为
开发者ID_01这类格式,签名(密码)和用户名是对应关系,不要传错
内容的提问来源于stack exchange,提问作者Till Glöckner
相关产品推荐
相关产品推荐

