如何使用证书保护 Spring soap 服务

How to secure a Spring soap service with certificates

我设法创建了一个需要用户名和密码才能调用函数的 soap 服务器和客户端。下面的代码展示了我是如何做到的

客户:

package hello;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ClassPathResource;
import org.springframework.oxm.jaxb.Jaxb2Marshaller;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.context.SecurityContextImpl;
import org.springframework.ws.client.support.interceptor.ClientInterceptor;
import org.springframework.ws.soap.security.xwss.XwsSecurityInterceptor;
import org.springframework.ws.soap.security.xwss.callback.SpringUsernamePasswordCallbackHandler;

@Configuration
public class SoftlayerConfiguration {

    @Bean
    public Jaxb2Marshaller marshaller() {
        Jaxb2Marshaller marshaller = new Jaxb2Marshaller();
        marshaller.setContextPath("be.elision.soap.cloud");
        return marshaller;
    }

    @Bean
    public SoftlayerClient softlayerClient(Jaxb2Marshaller marshaller) {
        SoftlayerClient client = new SoftlayerClient();
        client.setDefaultUri("http://192.168.137.107:8080/ws");
        client.setMarshaller(marshaller);
        client.setUnmarshaller(marshaller);
        client.setInterceptors(new ClientInterceptor[]{securityInterceptor()});
        return client;
    }

    @Bean
    XwsSecurityInterceptor securityInterceptor() {
        XwsSecurityInterceptor securityInterceptor = new XwsSecurityInterceptor();
        securityInterceptor.setSecureRequest(true);
        securityInterceptor.setValidateRequest(true);
        securityInterceptor.setCallbackHandler(callbackHandler());
        securityInterceptor.setPolicyConfiguration(new ClassPathResource("securityPolicy.xml"));
        return securityInterceptor;
    }

    @Bean
    SpringUsernamePasswordCallbackHandler callbackHandler() {
        SecurityContextHolder.setContext(new SecurityContextImpl());
        SecurityContextHolder.getContext().setAuthentication(new UsernamePasswordAuthenticationToken("user", "password"));
        return new SpringUsernamePasswordCallbackHandler();
    }

}

服务器:

package be.elision.main;

import java.util.Collections;
import java.util.List;

import org.springframework.boot.context.embedded.ServletRegistrationBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ClassPathResource;
import org.springframework.ws.config.annotation.EnableWs;
import org.springframework.ws.config.annotation.WsConfigurerAdapter;
import org.springframework.ws.server.EndpointInterceptor;
import org.springframework.ws.server.endpoint.interceptor.PayloadLoggingInterceptor;
import org.springframework.ws.soap.security.xwss.XwsSecurityInterceptor;
import org.springframework.ws.soap.security.xwss.callback.SimplePasswordValidationCallbackHandler;
import org.springframework.ws.soap.server.endpoint.interceptor.PayloadValidatingInterceptor;
import org.springframework.ws.transport.http.MessageDispatcherServlet;
import org.springframework.ws.wsdl.wsdl11.DefaultWsdl11Definition;
import org.springframework.xml.xsd.SimpleXsdSchema;
import org.springframework.xml.xsd.XsdSchema;

@EnableWs
@Configuration
public class WebServiceConfig extends WsConfigurerAdapter {
    @Bean
    public ServletRegistrationBean messageDispatcherServlet(
            ApplicationContext applicationContext) {
        MessageDispatcherServlet servlet = new MessageDispatcherServlet();
        servlet.setApplicationContext(applicationContext);
        servlet.setTransformWsdlLocations(true);
        return new ServletRegistrationBean(servlet, "/ws/*");
    }

    @Bean(name = "devices")
    public DefaultWsdl11Definition defaultWsdl11Definition(
            XsdSchema devicesSchema) {
        DefaultWsdl11Definition wsdl11Definition = new DefaultWsdl11Definition();
        wsdl11Definition.setPortTypeName("DevicesPort");
        wsdl11Definition.setLocationUri("/ws");
        wsdl11Definition.setTargetNamespace("http://elision.be/soap/cloud");
        wsdl11Definition.setSchema(devicesSchema);
        return wsdl11Definition;
    }

    // toevoegen van xsd aan bean
    @Bean
    public XsdSchema devicesSchema() {
        return new SimpleXsdSchema(new ClassPathResource("devices.xsd"));
    }

    @Bean
    XwsSecurityInterceptor securityInterceptor() {
        XwsSecurityInterceptor securityInterceptor = new XwsSecurityInterceptor();
        securityInterceptor.setCallbackHandler(callbackHandler());
        securityInterceptor.setPolicyConfiguration(new ClassPathResource(
                "securityPolicy.xml"));
        return securityInterceptor;
    }

    @Bean
    SimplePasswordValidationCallbackHandler callbackHandler() {
        SimplePasswordValidationCallbackHandler callbackHandler = new SimplePasswordValidationCallbackHandler();
        callbackHandler.setUsersMap(Collections
                .singletonMap("user", "password"));
        return callbackHandler;
    }

    @Bean
    PayloadLoggingInterceptor payloadLoggingInterceptor() {
        return new PayloadLoggingInterceptor();
    }

    @Bean
    PayloadValidatingInterceptor payloadValidatingInterceptor() {
        final PayloadValidatingInterceptor payloadValidatingInterceptor = new PayloadValidatingInterceptor();
        payloadValidatingInterceptor.setSchema(new ClassPathResource(
                "devices.xsd"));
        return payloadValidatingInterceptor;
    }

    @Override
    public void addInterceptors(List<EndpointInterceptor> interceptors) {
        interceptors.add(payloadLoggingInterceptor());
        interceptors.add(payloadValidatingInterceptor());
        interceptors.add(securityInterceptor());
    }

}

安全策略:

<xwss:SecurityConfiguration xmlns:xwss="http://java.sun.com/xml/ns/xwss/config">
    <xwss:UsernameToken digestPassword="false" useNonce="false" />
</xwss:SecurityConfiguration>

但现在我想用证书代替这个用户名和密码认证。我不喜欢在 xml 中执行此操作,而是在我现有的代码中执行此操作,如上所示。

几个月前我们终于解决了这个问题,据我所知我们的配置是这样的。

将此添加到您的 web.xml

<servlet>
    <servlet-name>mvc-dispatcher</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
    <servlet-name>mvc-dispatcher</servlet-name>
    <url-pattern>/</url-pattern>
</servlet-mapping>

这需要存在于您的 mvc-dispatcher-servlet.xml

    <beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mvc="http://www.springframework.org/schema/mvc"
    xsi:schemaLocation="
        http://www.springframework.org/schema/beans     
        http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
        http://www.springframework.org/schema/context 
        http://www.springframework.org/schema/context/spring-context-3.0.xsd
        http://www.springframework.org/schema/mvc
        http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd
        http://www.springframework.org/schema/web-services
        http://www.springframework.org/schema/web-services/web-services-2.0.xsd">

    <context:component-scan base-package="com.yourpackage" />
    <bean id="messageFactory" class="org.springframework.ws.soap.saaj.SaajSoapMessageFactory" />
    <bean id="webServiceTemplate" class="org.springframework.ws.client.core.WebServiceTemplate">
        <constructor-arg ref="messageFactory" />
        <property name="defaultUri"
            value="${backend.ip}devices" />
        <property name="interceptors">

            <list>
                <ref local="xwsSecurityInterceptor" />
            </list>
        </property>

    </bean>

    <bean id="xwsSecurityInterceptor"
        class="org.springframework.ws.soap.security.xwss.XwsSecurityInterceptor">
        <property name="policyConfiguration" value="/WEB-INF/securityPolicy.xml" />
        <property name="callbackHandlers">
            <list>
                <ref bean="keyStoreHandler" />
            </list>
        </property>
    </bean>

    <bean id="keyStore"
        class="org.springframework.ws.soap.security.support.KeyStoreFactoryBean">
        <property name="password" value="yourpassword" />
        <property name="location" value="/WEB-INF/yourkeystore.jks" />
    </bean>

    <bean id="keyStoreHandler"
        class="org.springframework.ws.soap.security.xwss.callback.KeyStoreCallbackHandler">
        <property name="keyStore" ref="keyStore" />
        <property name="privateKeyPassword" value="yourpassword" />
        <property name="defaultAlias" value="client" />
    </bean>

    <bean
        class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix">
            <value>/WEB-INF/pages/</value>
        </property>
        <property name="suffix">
            <value>.jsp</value>
        </property>
    </bean>

    <!-- LOAD PROPERTIES -->
    <context:property-placeholder
        location="WEB-INF/config.properties"
        ignore-unresolvable="true" />

    <mvc:resources mapping="/resources/**" location="/resources/" />
    <mvc:annotation-driven />

</beans>

jks 文件是一个 java 密钥库文件,其中包含您的证书。

SecurityPolicy.xml

    <xwss:SecurityConfiguration dumpMessages="true"     xmlns:xwss="http://java.sun.com/xml/ns/xwss/config">
   <xwss:Sign includeTimestamp="true">
         <xwss:X509Token certificateAlias="yourcertificatealias" /> 
    </xwss:Sign> 
</xwss:SecurityConfiguration>

这一切都是使用 oracle 的 xws-security 库完成的,为此您需要以下依赖项。

<dependency>
        <groupId>com.sun.xml.wss</groupId>
        <artifactId>xws-security</artifactId>
        <version>3.0</version>
        <exclusions>
            <exclusion>
                <groupId>javax.xml.crypto</groupId>
                <artifactId>xmldsig</artifactId>
            </exclusion>
        </exclusions>
    </dependency>

在这本很棒的书中找到了关于证书身份验证的一些很好的解释后,我们终于成功了!

'Spring Web Services 2 Cookbook by Hamidreza Sattari'

如果您对此实施还有任何疑问,请尽管提问! :d