• 实现Spring RESTful服务的SSL


    详见:http://blog.yemou.net/article/query/info/tytfjhfascvhzxcyt386

    本文介绍如何让基于Spring的REST服务变得SSL/TSL化。

    首先,假设一个Spring REST 服务如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    @Controller
    @RequestMapping("/")
    public class RestService {
        @RequestMapping(method = RequestMethod.GET)
        @ResponseBody
        public String get() {
            return "Called the get Rest Service";
        }
    }

     Web.xml的配置如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    <?xml version="1.0" encoding="UTF-8"?>
    <web-app
        version="3.1"
        xmlns="http://xmlns.jcp.org/xml/ns/javaee"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd">
      
        <servlet>
            <servlet-name>rest</servlet-name>
            <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
            <init-param>
                <param-name>contextClass</param-name>
                <param-value>org.springframework.web.context.support.AnnotationConfigWebApplicationContext</param-value>
            </init-param>
            <init-param>
                <param-name>contextConfigLocation</param-name>
                <param-value>com.radcortez.rest.ssl</param-value>
            </init-param>
            <load-on-startup>1</load-on-startup>
        </servlet>
      
        <servlet-mapping>
            <servlet-name>rest</servlet-name>
            <url-pattern>/</url-pattern>
        </servlet-mapping>
      
        <security-constraint>
            <web-resource-collection>
                <web-resource-name>Rest Application</web-resource-name>
                <url-pattern>/*</url-pattern>
            </web-resource-collection>
            <user-data-constraint>
                <!-- Needed for our application to respond to https requests -->
                <transport-guarantee>CONFIDENTIAL</transport-guarantee>
            </user-data-constraint>
        </security-constraint>
    </web-app>

     注意其中security-constraintuser-data-constraint 和 <transport-guarantee>CONFIDENTIAL</transport-guarantee>配置,这些指定这个应用需要一个安全连接。

     运行这个服务,部署应用到TomEE,键入网址:https://localhost:8443/,如果你正常配置了tomcat的SSL配置,浏览https和端口844应该一切正常,返回:Called the Rest Service

     如果现在调用客户端不是一般浏览器,而是一个Java客户端,这时会抛出错误:

    Message: I/O error on GET request for "https://localhost:8443/":sun.security.validator.ValidatorException:

    Exception: Caused by: javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target

     这是因为客户端JDK并没有你服务器的证书,你需要导入,这里我们展示使用编程方式提供信任蜜月的方式,这样做的好处:

    • 你可以运行应用代码在多个环境(和JDK无关)

    • 你不必每次手工将证书导入JDK

    • 你也不必升级JDK时得记住你的证书

    • 其他原因导致你不能直接向JDK导入证书

     编写代码:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    RestClientConfig.java
    @Configuration
    @PropertySource("classpath:config.properties")
    public class RestClientConfig {
        @Bean
        public RestOperations restOperations(ClientHttpRequestFactory clientHttpRequestFactory) 
        throws Exception {
            return new RestTemplate(clientHttpRequestFactory);
        }
      
        @Bean
        public ClientHttpRequestFactory clientHttpRequestFactory(HttpClient httpClient) {
            return new HttpComponentsClientHttpRequestFactory(httpClient);
        }
      
        @Bean
        public HttpClient httpClient(@Value("${keystore.file}") String file,
                                     @Value("${keystore.pass}") String password)
                                      throws Exception {
            KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
            FileInputStream instream = new FileInputStream(new File(file));
            try {
                trustStore.load(instream, password.toCharArray());
            finally {
                instream.close();
            }
      
            SSLContext sslcontext =
                    SSLContexts.custom()
                    .loadTrustMaterial(trustStore, new TrustSelfSignedStrategy()).build();
            SSLConnectionSocketFactory sslsf =
                    new SSLConnectionSocketFactory(sslcontext, new String[]{"TLSv1.2"}, null,
                                                   BROWSER_COMPATIBLE_HOSTNAME_VERIFIER);
            return HttpClients.custom().setSSLSocketFactory(sslsf).build();
        }
      
        @Bean
        public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
            return new PropertySourcesPlaceholderConfigurer();
        }
    }

    这里我们使用Spring RestOperations接口规定一个RESTful操作的基本集合,下面我们使用Apache HTTP组件SSLConnectionSocketFactory 提供的功能来校验服务器的信任密钥,也是使用服务器的 KeyStore

    RestServiceClientIT.java

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    @RunWith(SpringJUnit4ClassRunner.class)
    @ContextConfiguration(classes = RestClientConfig.class)
    public class RestServiceClientIT {
        @Autowired
        private RestOperations rest;
      
        @Test
        public void testRestRequest() throws Exception {
            ResponseEntity response = rest.getForEntity("https://localhost:8443/", String.class);
            System.out.println("response = " + response);
            System.out.println("response.getBody() = " + response.getBody());
        }
    }

    上面是一个简单的测试类,我们需要一个属性文件提供keystore文件位置和密码:

    config.properties

    keystore.file=${user.home}/.keystore

    keystore.pass=changeit

     现在我们可以运行测试客户端,你应该得到如下:

    Response: <200 OK,Called the get Rest Service,{Server=[Apache-Coyote/1.1], Cache-Control=[private], Expires=[Thu, 01 Jan 1970 01:00:00 WET], Content-Type=, Content-Length=[27], Date=[Tue, 23 Dec 2014 01:29:20 GMT]}>

    Body: Called the get Rest Service

     这说明一切正常,现在,你可以使用Java客户端以SSL/TLS方式调用你的REST服务了。

  • 相关阅读:
    git
    uniapp
    laravel
    laravel
    js
    js -上传文件获取名字赋值
    laravel
    持续集成
    持续集成
    持续集成
  • 原文地址:https://www.cnblogs.com/grefr/p/6088044.html
Copyright © 2020-2023  润新知