我是Spring Boot的新手 . 到目前为止,我很享受 . 我开发了一个演示SSL休息Web服务器,可以正确处理相互X.509证书身份验证 . 使用带有自签名客户端和服务器证书的IE浏览器,我测试了演示休息Web服务器是否正常工作 - 服务器和浏览器都成功地交换和验证彼此的证书 .
我在查找SSL客户端示例时遇到问题,该示例显示了如何包含客户端证书并发布https . 有人有一个简单的休息客户端示例,显示如何使用我的ssl服务器?
最诚挚的问候,Steve Mansfield
鉴于你是一个例子,它展示了如何使用Spring的 RestTemplate 和Apache的 HttpClient 配置了客户端证书,并信任来自服务器的自签名证书:
RestTemplate
HttpClient
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType()); keyStore.load(new FileInputStream(new File("keystore.jks")), "secret".toCharArray()); SSLConnectionSocketFactory socketFactory = new SSLConnectionSocketFactory( new SSLContextBuilder() .loadTrustMaterial(null, new TrustSelfSignedStrategy()) .loadKeyMaterial(keyStore, "password".toCharArray()).build()); HttpClient httpClient = HttpClients.custom().setSSLSocketFactory(socketFactory).build(); ClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory( httpClient); RestTemplate restTemplate = new RestTemplate(requestFactory); ResponseEntity<String> response = restTemplate.getForEntity( "https://localhost:8443", String.class);
我无法让安迪提交的上述客户工作 . 我一直收到错误,说“localhost!= clientname” . 无论如何,我让这个工作正常 .
import java.io.IOException; import org.apache.commons.httpclient.HttpClient; import org.apache.commons.httpclient.HttpException; import org.apache.commons.httpclient.URI; import org.apache.commons.httpclient.methods.GetMethod; public class SSLClient { static { System.setProperty("javax.net.ssl.trustStore","c:/apachekeys/client1.jks"); System.setProperty("javax.net.ssl.trustStorePassword", "password"); System.setProperty("javax.net.ssl.keyStore", "c:/apachekeys/client1.jks"); System.setProperty("javax.net.ssl.keyStorePassword", "password"); } public static void main(String[] args) throws HttpException, IOException { HttpClient client = new HttpClient(); GetMethod method = new GetMethod(); method.setURI(new URI("https://localhost:8443/restserver", false)); client.executeMethod(method); System.out.println(method.getResponseBodyAsString()); } }
user1707141的例子对我不起作用,skmansfield似乎更依赖于特定的文件,这不是Spring Boot / Maven的惯例 . 另外Andy Wilkinson的答案使用构造函数SSLConnectionSocketFactory,它在Apache httpclient 4.4中已弃用,而且看起来也很复杂 .
所以我创建了一个示例项目,应该在这里显示100%可理解的内容:https://github.com/jonashackt/spring-boot-rest-clientcertificate
除了在Testclass中正常使用RestTemplate和 @Autowired 之外,一定要像这样配置你的RestTemplate:
@Autowired
package de.jonashackt.restexamples; import org.apache.http.client.HttpClient; import org.apache.http.impl.client.HttpClients; import org.apache.http.ssl.SSLContextBuilder; import org.springframework.boot.web.client.RestTemplateBuilder; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.http.client.HttpComponentsClientHttpRequestFactory; import org.springframework.util.ResourceUtils; import org.springframework.web.client.RestTemplate; import javax.net.ssl.SSLContext; @Configuration public class RestClientCertTestConfiguration { private String allPassword = "allpassword"; @Bean public RestTemplate restTemplate(RestTemplateBuilder builder) throws Exception { SSLContext sslContext = SSLContextBuilder .create() .loadKeyMaterial(ResourceUtils.getFile("classpath:keystore.jks"), allPassword.toCharArray(), allPassword.toCharArray()) .loadTrustMaterial(ResourceUtils.getFile("classpath:truststore.jks"), allPassword.toCharArray()) .build(); HttpClient client = HttpClients.custom() .setSSLContext(sslContext) .build(); return builder .requestFactory(new HttpComponentsClientHttpRequestFactory(client)) .build(); } }
我知道为时已晚,但这里的代码对我有用 .
@SpringBootApplication public class Application { private static final Logger log = LoggerFactory.getLogger(Application.class); public static void main(String args[]) { makeWebServiceCall(); } public static void makeWebServiceCall() { TrustStrategy acceptingTrustStrategy = (X509Certificate[] chain, String authType) -> true; SSLContext sslContext; ResponseEntity<String> response = null; try { sslContext = org.apache.http.ssl.SSLContexts.custom().loadTrustMaterial(null, acceptingTrustStrategy) .build(); SSLConnectionSocketFactory csf = new SSLConnectionSocketFactory(sslContext); CloseableHttpClient httpClient = HttpClients.custom().setSSLSocketFactory(csf).build(); HttpComponentsClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory(); requestFactory.setHttpClient(httpClient); RestTemplate restTemplate = new RestTemplate(requestFactory); StringBuffer plainCreds = new StringBuffer(); plainCreds.append("username"); plainCreds.append(":"); plainCreds.append("password"); byte[] plainCredsBytes = plainCreds.toString().getBytes(); byte[] base64CredsBytes = Base64.getEncoder().encode(plainCredsBytes); String userBase64Credentials = new String(base64CredsBytes); HttpHeaders headers = new HttpHeaders(); headers.add("Authorization", "Basic " + userBase64Credentials); headers.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON)); headers.setContentType(MediaType.APPLICATION_JSON); HttpEntity entity = new HttpEntity<>(headers); String url = "https:restUrl"; response = restTemplate.exchange(url, HttpMethod.GET, entity, String.class); if(response.getStatusCodeValue() == 200) { log.info("Success! Further processing based on the need"); } else { log.info("****************Status code received: " + response.getStatusCodeValue() + ".************************"); } } catch (KeyManagementException | NoSuchAlgorithmException | KeyStoreException e) { log.error("Exception occured. Here are the exception details: ", e); } catch(HttpClientErrorException e) { if(e.getRawStatusCode() == 403) { log.info("****************Status code received: " + e.getRawStatusCode() + ". You do not have access to the requested resource.************************"); } else if(e.getRawStatusCode() == 404) { log.info("****************Status code received: " + e.getRawStatusCode() + ". Resource does not exist(or) the service is not up.************************"); } else if(e.getRawStatusCode() == 400) { log.info("****************Status code received: " + e.getRawStatusCode() + ". Bad Request.************************"); } else { log.info("****************Status code received: " + e.getRawStatusCode() + ".************************"); } log.info("****************Response body: " + e.getResponseBodyAsString() + "************************"); } } }
这是maven提交的
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>org.springframework</groupId> <artifactId>gs-consuming-rest</artifactId> <version>0.1.0</version> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>1.5.2.RELEASE</version> </parent> <properties> <java.version>1.8</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-web</artifactId> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> </dependency> <!-- https://mvnrepository.com/artifact/org.apache.httpcomponents/httpclient --> <dependency> <groupId>org.apache.httpcomponents</groupId> <artifactId>httpclient</artifactId> <version>4.5.3</version> </dependency> <!-- https://mvnrepository.com/artifact/org.apache.httpcomponents/httpcore --> <dependency> <groupId>org.apache.httpcomponents</groupId> <artifactId>httpcore</artifactId> <version>4.4.6</version> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build>
其他方式来做到这一点 . 注入keyStoreLocation和keyStorePassword的值
@Configuration public class SampleSSLClient extends RestTemplate{ /** The key store password. */ private String keyStorePassword; /** The key store location. */ private String keyStoreLocation; /** The rest template. */ @Autowired private RestTemplate restTemplate; /** The http components client http request factory. */ private HttpComponentsClientHttpRequestFactory httpComponentsClientHttpRequestFactory; /** * Instantiates a new custom rest template. */ public CustomRestTemplate() { super(); } public CustomRestTemplate(RestTemplate restTemplate){ this.restTemplate = getRestTemplate(); } /** * Rest template. * * @return the rest template */ public RestTemplate getRestTemplate() { if (null == httpComponentsClientHttpRequestFactory) { httpComponentsClientHttpRequestFactory = loadCert(); restTemplate.setRequestFactory(httpComponentsClientHttpRequestFactory); } return restTemplate; } /** * Load cert. * * @return the http components client http request factory */ private HttpComponentsClientHttpRequestFactory loadCert() { try { char[] keypass = keyStorePassword.toCharArray(); SSLContext sslContext = SSLContextBuilder.create() .loadKeyMaterial(getkeyStore(keyStoreLocation, keypass), keypass) .loadTrustMaterial(null, new TrustSelfSignedStrategy()).build(); HttpClient client = HttpClients.custom().setSSLContext(sslContext).build(); httpComponentsClientHttpRequestFactory = new HttpComponentsClientHttpRequestFactory(client); httpComponentsClientHttpRequestFactory.setConnectTimeout(5000); httpComponentsClientHttpRequestFactory.setReadTimeout(30000); } catch (Exception ex) { LOGGER.error(MessageFormat.format("Some Error", ex.getMessage()), ex); } return httpComponentsClientHttpRequestFactory; } /** * Key store. * * @param storePath the store path * @param password the password * @return the key store */ private KeyStore getkeyStore(String storePath, char[] password) { KeyStore keyStore; try { keyStore = KeyStore.getInstance(KeyStore.getDefaultType()); File key = ResourceUtils.getFile(storePath); try (InputStream in = new FileInputStream(key)) { keyStore.load(in, password); } }catch (Exception ex) { LOGGER.error(MessageFormat.format("Some Error", ex.getMessage()), ex); } return keyStore; } /** * Sets the key store password. * * @param keyStorePassword the new key store password */ public void setKeyStorePassword(String keyStorePassword) { this.keyStorePassword = keyStorePassword; } /** * Sets the key store location. * * @param keyStoreLocation the new key store location */ public void setKeyStoreLocation(String keyStoreLocation) { this.keyStoreLocation = keyStoreLocation; } /** * Sets the rest template. * * @param restTemplate the new rest template */ public void setRestTemplate(RestTemplate restTemplate) { this.restTemplate = restTemplate; }
这对我有用:
TrustStrategy acceptingTrustStrategy = (X509Certificate[] chain, String authType) -> true; javax.net.ssl.SSLContext sslContext = org.apache.http.ssl.SSLContexts.custom() .loadTrustMaterial(null, acceptingTrustStrategy).build(); SSLConnectionSocketFactory csf = new SSLConnectionSocketFactory(sslContext); CloseableHttpClient httpClient = HttpClients.custom().setSSLSocketFactory(csf).build(); HttpComponentsClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory(); requestFactory.setHttpClient(httpClient); RestTemplate restTemplate = new RestTemplate(requestFactory);
6 回答
鉴于你是一个例子,它展示了如何使用Spring的
RestTemplate
和Apache的HttpClient
配置了客户端证书,并信任来自服务器的自签名证书:我无法让安迪提交的上述客户工作 . 我一直收到错误,说“localhost!= clientname” . 无论如何,我让这个工作正常 .
user1707141的例子对我不起作用,skmansfield似乎更依赖于特定的文件,这不是Spring Boot / Maven的惯例 . 另外Andy Wilkinson的答案使用构造函数SSLConnectionSocketFactory,它在Apache httpclient 4.4中已弃用,而且看起来也很复杂 .
所以我创建了一个示例项目,应该在这里显示100%可理解的内容:https://github.com/jonashackt/spring-boot-rest-clientcertificate
除了在Testclass中正常使用RestTemplate和
@Autowired
之外,一定要像这样配置你的RestTemplate:我知道为时已晚,但这里的代码对我有用 .
这是maven提交的
其他方式来做到这一点 . 注入keyStoreLocation和keyStorePassword的值
这对我有用: