使用 Spring WS 获取 SOAP Header 的内容

Getting content of a SOAP Header using Spring WS

我正在尝试构建一个将从客户端接收 SOAP 消息的端点。我收到的消息在 soap header ...

中包含用户名和密码
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns="http://www.company.com/Application">
    <soapenv:Header  xmlns:wsse="http://__________.xsd">
        <wsse:Security >
            <wsse:UsernameToken>
                <wsse:Username>username</wsse:Username>
                <wsse:Password>password</wsse:Password>
            </wsse:UsernameToken>
        </wsse:Security>

   </soapenv:Header>
   <soapenv:Body>

我正在使用 Spring WS - 显而易见的解决方案是在 web.xml 中创建一个过滤器,它将完全绕过 Spring WS,解析 SOAP 消息,提取用户名和密码,然后继续 Spring WS,它将再次解析 SOAP。

有没有办法在不绕过Spring WS的情况下获取header的内容?

我试过在里面添加一个 bean sws:interceptors:

<sws:interceptors>

    <!-- extract Security details from Header -->
    <bean class="com.company.application.service.SecurityInterceptorService" />

    <!-- log full Body of request -->
    <bean class="org.springframework.ws.server.endpoint.interceptor.PayloadLoggingInterceptor"/>

    <!-- validate Request against XSD to make sure it's a valid request -->
    <bean id="CompanyApplication" class="com.company.application.interceptor.ValidatingInterceptor">
        <property name="schema" value="/WEB-INF/_______________.xsd" />
        <property name="validateRequest" value="true" />
        <property name="validateResponse" value="true" />
    </bean>

</sws:interceptors>

然后实施 class:

public class SecurityInterceptorService implements EndpointInterceptor {


    @Override
    public boolean handleRequest(MessageContext messageContext, Object endpoint) throws Exception {

        System.out.println("---------------");
        System.out.println("handleRequest")                                             ;
        System.out.println("---------------");


        return true;
    }

    @Override
    public boolean handleResponse(MessageContext messageContext, Object endpoint) throws Exception {

        System.out.println("---------------");
        System.out.println("handleResponse");
        System.out.println("---------------");



        return true;
    }

    @Override
    public boolean handleFault(MessageContext messageContext, Object endpoint) throws Exception {

        System.out.println("---------------");
        System.out.println("handleFault");
        System.out.println("---------------");


        return true;
    }

    @Override
    public void afterCompletion(MessageContext messageContext, Object endpoint, Exception ex) throws Exception {

        System.out.println("---------------");
        System.out.println("afterCompletion");
        System.out.println("---------------");

    }



}

endpoint 仅包含有关 handleRequest 内部端点的数据,并且在调试模式下遍历 messageContext 内部的许多层和层之后,我似乎无法发现内容header.

messageContext 中是否有我要查找的内容?如果是,我该如何访问它?

messageContext object,您可以检索请求或响应(在您的情况下,我想您需要请求)。

request/response基本上就是一个WebServiceMessage. If you examine the webServiceMessage, you will see that the object can be casted to a SoapMessage。从 soap 消息中,您现在可以获取 soap header.

WebServiceMessage webServiceMessageRequest = messageContext_.getRequest();
SoapMessage soapMessage = (SoapMessage) webServiceMessageRequest;
SoapHeader soapHeader = soapMessage.getSoapHeader()

之后,您可能希望获得 source object and convert it to a DOMSource object and then get the Node object,这使得信息检索更加容易。

Source bodySource = soapHeader .getSource();
DOMSource bodyDomSource = (DOMSource) bodySource;
Node bodyNode = _bodyDomSource.getNode();

如果你使用的是spring-boot你可以使用这种配置:

@EnableWs
@Configuration
public class WebServiceConfig extends WsConfigurerAdapter {

  @Override
  public void addInterceptors(List<EndpointInterceptor> interceptors) {
    PayloadValidatingInterceptor validatingInterceptor = new PayloadValidatingInterceptor();
    validatingInterceptor.setValidateRequest(true);
    validatingInterceptor.setValidateResponse(true);
    validatingInterceptor.setXsdSchema(resourceSchema());
    interceptors.add(validatingInterceptor);
  }

  @Bean
  public ServletRegistrationBean messageDispatcherServlet(ApplicationContext applicationContext) {
    MessageDispatcherServlet servlet = new MessageDispatcherServlet();
    servlet.setApplicationContext(applicationContext);
    servlet.setTransformWsdlLocations(true);
    return new ServletRegistrationBean(servlet, "/api/*");
  }

  @Bean(name = "registros")
  public DefaultWsdl11Definition defaultWsdl11Definition(XsdSchema countriesSchema) {
    DefaultWsdl11Definition wsdl11Definition = new DefaultWsdl11Definition();
    wsdl11Definition.setPortTypeName("ResourcePort");
    wsdl11Definition.setLocationUri("/api");
    wsdl11Definition.setTargetNamespace("http://resource.com/schema");
    wsdl11Definition.setSchema(resourceSchema());
    return wsdl11Definition;
  }

  @Bean
  public XsdSchema resourceSchema() {
    return new SimpleXsdSchema(new ClassPathResource("registro.xsd"));
  }
}

在这个例子中,addInterceptors 方法是重要的方法,其他 3 个是公开 WSDL 的基本方法 API。

也许对其他人有用。

没有简单的方法可以用 Spring-ws 解组 Soap headers (it's currently not supported) 但是,您可以在带注释的@PayloadRoot 方法中访问 SoapHeaderElement,并使用 JAXB 执行解组过程。

@Endpoint
public class SubmitEndpoint implements EndpointInterface {

    private static final String NAMESPACE_URI = "http://www.example.com/namespace";

    private Security unmarshallSecurityFromSoapHeader(SoapHeaderElement header) {
        Security security = null;
        try {

            JAXBContext context = JAXBContext.newInstance(Security.class);
            Unmarshaller unmarshaller = context.createUnmarshaller();
            security = (Security) unmarshaller.unmarshal(header.getSource());

        } catch (JAXBException e) {
            e.printStackTrace();
        }
        return security;
    }

    @PayloadRoot(namespace = NAMESPACE_URI, localPart = "submit")
    @ResponsePayload
    public SubmitResponse submit(@RequestPayload Submit submit, @SoapHeader(
            value = "{http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd}Security") SoapHeaderElement wsseSecurityHeader) throws JAXBException {


        Security security = unmarshallSecurityFromSoapHeader(wsseSecurityHeader);

    }
}

Security.java

@Getter
@Setter
@XmlAccessorType(XmlAccessType.FIELD)
@XmlRootElement(namespace = Security.SECURITY_NS, name = "Security")
public class Security {

    public static final String SECURITY_NS = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd";

    @XmlElement(namespace = SECURITY_NS, name = "UsernameToken")
    private UsernameToken usernameToken;

}

UsernameToken.java

@Getter
@Setter
@XmlAccessorType(XmlAccessType.FIELD)
@XmlRootElement(namespace = Security.SECURITY_NS, name = "UsernameToken")
public class UsernameToken {

    @XmlElement(name = "Username", namespace = Security.SECURITY_NS)
    private String username;

    @XmlElement(name = "Password", namespace = Security.SECURITY_NS)
    private String password;

}