You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.22 09:40:39