實現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服務了。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM