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

使用Camel CXF消费带HTTPS与安全头的SOAP API遇时间戳验证错误

Camel-CXF消费SOAP服务时PolicyException(Timestamp/IncludeTimestamp)问题解决

问题重现

使用Camel 2.20.3 + CXF 3.2.6消费带安全策略的SOAP服务时,抛出以下异常:

org.apache.cxf.ws.policy.PolicyException: These policy alternatives can not be satisfied:
{http://schemas.xmlsoap.org/ws/2005/07/securitypolicy}TransportBinding: Received Timestamp does not match the requirements
{http://schemas.xmlsoap.org/ws/2005/07/securitypolicy}IncludeTimestamp

SOAP响应返回a:InvalidSecurity错误,而SoapUI可正常调用该服务。WSDL安全策略要求:

  • HTTPS传输(HttpsToken)
  • 请求必须包含Timestamp(IncludeTimestamp)
  • 带签名的UsernameToken认证(SignedSupportingTokens + WssUsernameToken10)

核心原因

  1. DataFormat配置冲突:使用MESSAGE格式时,Camel直接传递原始SOAP消息,CXF无法自动触发WSDL安全策略对应的拦截器,导致未生成Timestamp和签名的UsernameToken。
  2. 安全参数配置不完整:仅设置用户名密码未满足IncludeTimestamp和SignedSupportingTokens的策略要求,缺少Timestamp生成、签名相关的WSS4J配置。
  3. 版本兼容性:Camel 2.20.3默认依赖CXF 3.1.x,手动引入CXF 3.2.6可能存在版本冲突。

解决方案

方案1:调整CXF端点配置,启用自动策略处理

1. 修改Camel路由

将dataFormat从MESSAGE改为POJO,让CXF自动处理消息转换和安全策略执行,同时移除路由中手动设置的username/password参数:

from("direct:userdata.soap.requests")
    .to("cxf://{{SOAP_URL}}" +
        "?serviceClass=org.tempuri.IUserManagementService" +
        "&serviceName={http://tempuri.org/}UserManagementService" +
        "&endpointName={http://tempuri.org/}BasicHttpBinding_IUserManagementService" +
        "&wsdlURL={{WSDL_URL}}" +
        "&dataFormat=POJO" +
        "&allowStreaming=false");

2. 完善请求上下文的WSS4J配置

在发送请求时,添加Timestamp生成、UsernameToken签名的相关参数:

private Object sendRequest(Object request, String operationName) throws Exception{
    // 改用InOut模式,匹配请求-响应的SOAP交互
    Exchange inExchange = endpoint.createExchange(ExchangePattern.InOut);
    inExchange.getIn().setHeader(CxfConstants.OPERATION_NAME, operationName);
    inExchange.getIn().setHeader(CxfConstants.OPERATION_NAMESPACE, "http://tempuri.org/");
    inExchange.getIn().setBody(request);

    Map<String, Object> context = new HashMap<>();
    // 配置WSS4J动作:生成Timestamp + 签名UsernameToken
    context.put("ws-security.action", "UsernameToken Timestamp");
    context.put("ws-security.username", soapUsername);
    context.put("ws-security.password", soapPassword);
    // 对应WSDL的WssUsernameToken10,使用明文密码(若服务端要求Digest则改为PasswordDigest)
    context.put("ws-security.passwordType", "PasswordText");
    // 签名回调处理器,用于获取签名密码
    context.put("ws-security.callback-handler", new PasswordCallback(soapPassword));
    // 签名密钥配置文件(需放在classpath下)
    context.put("ws-security.signature.properties", "client.properties");

    inExchange.getIn().setHeader(Client.REQUEST_CONTEXT, context);

    Exchange outExchange = channel.send(inExchange);
    Object result = outExchange.getIn().getBody(Object.class);
    if(result instanceof FaultException){
        throw (FaultException) result;
    }
    return result;
}

3. 实现密码回调处理器

public class PasswordCallback implements CallbackHandler {
    private final String password;

    public PasswordCallback(String password) {
        this.password = password;
    }

    @Override
    public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException {
        WSPasswordCallback pc = (WSPasswordCallback) callbacks[0];
        pc.setPassword(password);
    }
}

4. 添加签名密钥配置文件client.properties

在classpath下创建该文件,使用自签名证书即可(服务端若仅验证签名存在性,无需信任证书):

org.apache.ws.security.crypto.provider=org.apache.ws.security.components.crypto.Merlin
org.apache.ws.security.crypto.merlin.keystore.type=JKS
org.apache.ws.security.crypto.merlin.keystore.password=your-keystore-pass
org.apache.ws.security.crypto.merlin.keystore.file=client.jks

方案2:手动配置WSS4J拦截器

若自动策略处理失效,可手动添加WSS4J输出拦截器强制生成安全头:

1. 修改Camel路由,引用自定义拦截器

from("direct:userdata.soap.requests")
    .to("cxf://{{SOAP_URL}}" +
        "?serviceClass=org.tempuri.IUserManagementService" +
        "&serviceName={http://tempuri.org/}UserManagementService" +
        "&endpointName={http://tempuri.org/}BasicHttpBinding_IUserManagementService" +
        "&wsdlURL={{WSDL_URL}}" +
        "&dataFormat=POJO" +
        "&allowStreaming=false" +
        "&outInterceptors=#wss4jOutInterceptor");

2. 在Blueprint/Spring中定义拦截器

<bean id="wss4jOutInterceptor" class="org.apache.cxf.ws.security.wss4j.WSS4JOutInterceptor">
    <constructor-arg>
        <map>
            <entry key="action" value="UsernameToken Timestamp"/>
            <entry key="username" value="{{SOAP_USERNAME}}"/>
            <entry key="password" value="{{SOAP_PASSWORD}}"/>
            <entry key="passwordType" value="PasswordText"/>
            <entry key="signaturePropFile" value="client.properties"/>
            <entry key="callbackHandler" ref="passwordCallback"/>
        </map>
    </constructor-arg>
</bean>

<bean id="passwordCallback" class="com.your.package.PasswordCallback">
    <constructor-arg value="{{SOAP_PASSWORD}}"/>
</bean>

关键注意事项

  • 版本统一:将CXF依赖版本调整为Camel 2.20.3默认的3.1.12,避免版本冲突。
  • 日志验证:添加CXF日志拦截器,查看发送的SOAP消息是否包含<wsu:Timestamp>和带签名的<wsse:UsernameToken>元素,对比SoapUI的请求消息排查差异。

内容的提问来源于stack exchange,提问作者Raphi1

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.10 12:25:22