Java SOAP客户端添加PasswordText类型UsernameToken请求头求助
解决方案:在Java SOAP客户端添加PasswordText类型的UsernameToken
我之前也碰到过一模一样的问题,SOAPUI里要手动加这个头,Java客户端一开始也摸不着头脑,后来摸索出两种靠谱的方法,给你分享下:
方法1:使用JAX-WS Handler(标准JDK实现,无需额外依赖)
这是最通用的方式,不管你用的是原生JAX-WS还是其他基于JAX-WS的框架都能用。核心思路是写一个SOAP请求拦截器,在出站请求里手动构建UsernameToken的XML结构并添加到SOAP Header中。
第一步:创建UsernameToken Handler类
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; import java.util.Set; public class UsernameTokenHandler implements SOAPHandler<SOAPMessageContext> { private final String username; private final String password; public UsernameTokenHandler(String username, String password) { this.username = username; this.password = password; } @Override public boolean handleMessage(SOAPMessageContext context) { // 只处理出站请求(发送给服务端的请求) Boolean isOutbound = (Boolean) context.get(MessageContext.MESSAGE_OUTBOUND_PROPERTY); if (isOutbound) { try { SOAPMessage soapMsg = context.getMessage(); SOAPEnvelope envelope = soapMsg.getSOAPPart().getEnvelope(); SOAPHeader header = envelope.getHeader(); // 如果Header不存在,先创建一个 if (header == null) { header = envelope.addHeader(); } // 定义WS-Security的命名空间和前缀 String wsSecNs = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"; String wsSecPrefix = "wsse"; envelope.addNamespaceDeclaration(wsSecPrefix, wsSecNs); // 创建UsernameToken根元素 SOAPElement usernameToken = header.addChildElement("UsernameToken", wsSecPrefix); // 添加WS-Utility命名空间(部分服务端要求) usernameToken.addAttribute( new QName("http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd", "Id", "wsu"), "UsernameToken-1" ); // 添加Username子元素 SOAPElement usernameElem = usernameToken.addChildElement("Username", wsSecPrefix); usernameElem.addTextNode(username); // 添加Password子元素,指定类型为PasswordText SOAPElement passwordElem = usernameToken.addChildElement("Password", wsSecPrefix); passwordElem.addAttribute( new QName("Type"), "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText" ); passwordElem.addTextNode(password); // 保存修改 soapMsg.saveChanges(); } catch (SOAPException e) { e.printStackTrace(); return false; } } return true; } @Override public boolean handleFault(SOAPMessageContext context) { return true; } @Override public void close(MessageContext context) {} @Override public Set<QName> getHeaders() { return Set.of(new QName("http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd", "UsernameToken")); } }
第二步:将Handler添加到SOAP客户端
// 假设你的服务客户端是通过wsimport生成的MyServiceService和MyServicePortType MyServiceService service = new MyServiceService(); MyServicePortType port = service.getMyServicePort(); // 获取BindingProvider,添加Handler链 BindingProvider bindingProvider = (BindingProvider) port; List<Handler> handlerChain = bindingProvider.getBinding().getHandlerChain(); handlerChain.add(new UsernameTokenHandler("你的用户名", "你的密码")); bindingProvider.getBinding().setHandlerChain(handlerChain); // 现在调用服务方法,请求会自动带上UsernameToken头 port.yourServiceMethod();
方法2:使用Apache CXF框架(更简洁,适合用CXF的项目)
如果你的项目用的是Apache CXF,那配置会简单很多,CXF自带WS-Security的拦截器,不用手动拼XML。
第一步:添加CXF WS-Security依赖(Maven为例)
<dependency> <groupId>org.apache.cxf</groupId> <artifactId>cxf-rt-ws-security</artifactId> <version>3.5.5</version> <!-- 替换成你的CXF版本 --> </dependency>
第二步:配置WSS4J出站拦截器
import org.apache.cxf.endpoint.Client; import org.apache.cxf.frontend.ClientProxy; import org.apache.cxf.ws.security.wss4j.WSS4JOutInterceptor; import java.util.HashMap; import java.util.Map; // 创建CXF客户端实例 MyServiceService service = new MyServiceService(); MyServicePortType port = service.getMyServicePort(); // 获取CXF底层Client对象 Client client = ClientProxy.getClient(port); // 配置WS-Security参数 Map<String, Object> securityProps = new HashMap<>(); securityProps.put("action", "UsernameToken"); // 指定要添加UsernameToken securityProps.put("user", "你的用户名"); securityProps.put("passwordType", "PasswordText"); // 明文密码类型 securityProps.put("password", "你的密码"); // 添加出站拦截器 WSS4JOutInterceptor outInterceptor = new WSS4JOutInterceptor(securityProps); client.getOutInterceptors().add(outInterceptor); // 调用服务即可 port.yourServiceMethod();
注意事项
- 确保WS-Security的命名空间和服务端要求一致,上面用的是OASIS标准的命名空间,大部分服务端都兼容。
- 测试时可以用SOAPUI抓包对比,看看生成的请求头和你手动添加的结构是否一致。
- 如果服务端要求带Nonce和Created时间戳,只需要在Handler里额外添加这两个元素,或者在CXF的配置里加上
addCreated和addNonce参数。
内容的提问来源于stack exchange,提问作者Touqeer
相关产品推荐
相关产品推荐

