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

Spring Boot配置SOAP请求安全Header遇Must Understand错误求助

Spring Boot调用SOAP服务时添加WS-Security Header的问题

我对SOAP服务不太熟悉,正在尝试在Spring Boot应用中调用某SOAP服务。程序能运行,但因为无法按如下目标SOAP请求设置安全Header而抛出异常:

<soapenv:Envelope
    xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
    xmlns:web="http://webservices.service.company.com/">
    <soapenv:Header
        xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
        <wsse:Security>
            <wsse:UsernameToken>
                <wsse:Username>username</wsse:Username>
                <wsse:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText">Pa$$wrd</wsse:Password>
            </wsse:UsernameToken>
        </wsse:Security>
    </soapenv:Header>
    <soapenv:Body>
        ...
    </soapenv:Body>
</soapenv:Envelope>

我的配置类如下:

@Configuration
public class SoapClientConfig {
    @Bean
    public Jaxb2Marshaller marshaller() {
        Jaxb2Marshaller marshaller = new Jaxb2Marshaller();
        marshaller.setContextPath("com.company.ws.wsdl");
        return marshaller;
    }

    @Bean
    public SoapClient soapClient(Jaxb2Marshaller marshaller) {
        SoapClient client = new SoapClient();
        client.setDefaultUri("localhost:8080/service/myservice");
        client.setMarshaller(marshaller);
        client.setUnmarshaller(marshaller);
        return client;
    }
}

SoapClient类:

@Component
public class SoapClient extends WebServiceGatewaySupport {

    public CustSearchResponse getCustomer(CustomerSearchRequest custRequest) {
        ObjectFactory objectFactory = new ObjectFactory();
        JAXBElement<CustomerSearchRequest> request =
                objectFactory.createCustomerSearchRequest(custRequest);
        JAXBElement response = (JAXBElement)getWebServiceTemplate().marshalSendAndReceive(
                request);
        return (CustSearchResponse) response.getValue();
    }
}

我查了一些示例,但觉得太复杂,不知道怎么适配我的SOAP Header,求建议。


更新:添加安全拦截器后的错误

尝试添加如下Wss4jSecurityInterceptor:

@Bean
public Wss4jSecurityInterceptor securityInterceptor() {
    Wss4jSecurityInterceptor security = new Wss4jSecurityInterceptor();
    security.setSecurementActions(WSHandlerConstants.TIMESTAMP + " " + WSHandlerConstants.USERNAME_TOKEN);
    security.setSecurementPasswordType(WSConstants.PW_TEXT);
    security.setSecurementUsername("username");
    security.setSecurementPassword("password");
    return security;
}

并更新SoapClient Bean:

@Bean
public SoapClient soapClient(Jaxb2Marshaller marshaller) {
    SoapClient client = new SoapClient();
    client.setDefaultUri("localhost:8080/service/myservice");
    client.setMarshaller(marshaller);
    client.setUnmarshaller(marshaller);
    client.setInterceptors(new ClientInterceptor[]{ securityInterceptor() });
    return client;
}

现在出现如下错误:

org.springframework.ws.soap.client.SoapFaultClientException: 对SOAP-ENV头{http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd}Security的Must Understand检查失败
    at org.springframework.ws.soap.client.core.SoapFaultMessageResolver.resolveFault(SoapFaultMessageResolver.java:38)

    at org.springframework.ws.client.core.WebServiceTemplate.handleFault(WebServiceTemplate.java:795)
    at org.springframework.ws.client.core.WebServiceTemplate.doSendAndReceive(WebServiceTemplate.java:605)
    at org.springframework.ws.client.core.WebServiceTemplate.sendAndReceive(WebServiceTemplate.java:542)
    at org.springframework.ws.client.core.WebServiceTemplate.marshalSendAndReceive(WebServiceTemplate.java:394)
    at org.springframework.ws.client.core.WebServiceTemplate.marshalSendAndReceive(WebServiceTemplate.java:388)
    at org.springframework.ws.client.core.WebServiceTemplate.marshalSendAndReceive(WebServiceTemplate.java:378)

解决办法

问题根因

这个错误是因为SOAP服务端要求Security头必须标记为"必须理解"(MustUnderstand=true),但当前拦截器生成的头没有正确设置该属性;另外你添加的TIMESTAMP动作可能是服务端不需要的,也会引发处理异常。

具体修复步骤

  1. 修改安全拦截器配置
    移除不需要的TIMESTAMP动作(如果服务端没要求),同时显式设置Security头的MustUnderstand属性:

    @Bean
    public Wss4jSecurityInterceptor securityInterceptor() {
        Wss4jSecurityInterceptor security = new Wss4jSecurityInterceptor();
        // 仅保留USERNAME_TOKEN,去掉服务端不需要的TIMESTAMP
        security.setSecurementActions(WSHandlerConstants.USERNAME_TOKEN);
        security.setSecurementPasswordType(WSConstants.PW_TEXT);
        security.setSecurementUsername("username");
        security.setSecurementPassword("password");
        
        // 设置Security头的MustUnderstand属性为true
        Map<String, Object> securementProps = new HashMap<>();
        securementProps.put(WSHandlerConstants.MUST_UNDERSTAND, "true");
        security.setSecurementProperties(securementProps);
        
        return security;
    }
    
  2. 修正SoapClient的Bean配置
    确保拦截器被正确注入,同时补全请求地址的协议前缀(比如http://):

    @Bean
    public SoapClient soapClient(Jaxb2Marshaller marshaller, Wss4jSecurityInterceptor securityInterceptor) {
        SoapClient client = new SoapClient();
        client.setDefaultUri("http://localhost:8080/service/myservice");
        client.setMarshaller(marshaller);
        client.setUnmarshaller(marshaller);
        client.setInterceptors(new ClientInterceptor[]{securityInterceptor});
        return client;
    }
    
  3. 核对服务端要求

    • 确认服务端是否需要Timestamp,不需要就坚决移除,避免多余头导致服务端无法处理。
    • 确认密码的Type属性和服务端要求一致(你当前用的PasswordText和目标请求一致,这部分没问题)。
  4. 备选方案:手动构建SOAP头
    如果拦截器方式仍有问题,可以直接手动构建Security头:

    在SoapClient的getCustomer方法中添加头构建逻辑:

    public CustSearchResponse getCustomer(CustomerSearchRequest custRequest) {
        ObjectFactory objectFactory = new ObjectFactory();
        JAXBElement<CustomerSearchRequest> request =
                objectFactory.createCustomerSearchRequest(custRequest);
        
        // 手动生成WS-Security头
        SoapHeader soapHeader = getWebServiceTemplate().getMessageFactory().createWebServiceMessage().getSoapHeader();
        String securityHeaderXml = "<wsse:Security xmlns:wsse=\"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd\" soapenv:mustUnderstand=\"1\" xmlns:soapenv=\"http://schemas.xmlsoap.org/soap/envelope/\">" +
                "<wsse:UsernameToken>" +
                "<wsse:Username>username</wsse:Username>" +
                "<wsse:Password Type=\"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText\">Pa$$wrd</wsse:Password>" +
                "</wsse:UsernameToken>" +
                "</wsse:Security>";
        
        try {
            Source source = new StringSource(securityHeaderXml);
            soapHeader.getResult().setSystemId("");
            Transformer transformer = TransformerFactory.newInstance().newTransformer();
            transformer.transform(source, soapHeader.getResult());
        } catch (Exception e) {
            throw new RuntimeException("生成SOAP安全头失败", e);
        }
        
        JAXBElement response = (JAXBElement)getWebServiceTemplate().marshalSendAndReceive(request);
        return (CustSearchResponse) response.getValue();
    }
    

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.03 02:06:06