【问题标题】:How to configure TestRestTemplate to use a Keystore?如何配置 TestRestTemplate 以使用密钥库?
【发布时间】:2019-10-16 02:28:52
【问题描述】:

我的项目有一系列使用TestRestTemplateMockMvc 的集成测试。这些都顺利通过了。

我现在已将 Spring Boot Starter SecuritySpring Security OAuth2 Autoconfigure 依赖项添加到我的项目中。我添加了一个扩展 WebSecurityConfigurerAdapter 的自定义类,以允许(目前)对我的应用程序进行开放访问。这里是课堂

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity httpSecurity) throws Exception {
        httpSecurity
            .authorizeRequests()
            .anyRequest()
            .permitAll();
    }

    @Override
    public void configure(WebSecurity webSecurity) {
        webSecurity
            .ignoring()
            .antMatchers(HttpMethod.OPTIONS, "/**");
    }
}

该应用程序还需要充当OAuth2 Resource Server,因此我还用@EnableResourceServer 注释了我的主类。我在运行应用程序时提供可信密钥库的路径作为运行参数。 -Djavax.net.ssl.trustStore=<where the cert is stored locally> -Djavax.net.ssl.trustStorePassword=<the password>

应用程序运行良好,但现在所有的集成测试都失败了。以下是使用TestRestTemplate的所有测试常见的错误示例

Could not fetch user details: class org.springframework.web.client.ResourceAccessException, I/O error on GET request for <the path to my userinfo URL>: 
PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: 
unable to find valid certification path to requested target; nested exception is javax.net.ssl.SSLHandshakeException: 
PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target

似乎需要指示我用于测试的TestRestTemplate 使用与应用程序相同的密钥库。是否有可能做到这一点? MockMvc 的工作原理是什么?

【问题讨论】:

    标签: spring-security integration-testing keystore spring-security-oauth2 openid-connect


    【解决方案1】:

    我认为您可能还需要传递 -Djavax.net.ssl.trustStore= -Djavax.net.ssl.trustStorePassword= 运行测试时的参数。 要在配置和 maven 中运行单个测试传递参数,您也可以传递这些参数。

    以下两个链接可能会有所帮助

    Specifying trust store information in spring boot application.properties

    http://codeboarding.com/tag/testresttemplate/

    【讨论】:

      【解决方案2】:

      谢谢,您发布的第一个链接非常有用。这是我接受任何证书的 RestTemplate 的工作代码,如果其他人觉得它有用的话。它仍然依赖于提供的有效令牌,但这是另一回事。

      private RestTemplate buildRestTemplate() throws Exception {
          SSLContext sslContext = new SSLContextBuilder()
              .loadTrustMaterial(
                  new TrustSelfSignedStrategy()
              ).build();
          SSLConnectionSocketFactory socketFactory =
              new SSLConnectionSocketFactory(sslContext);
          HttpClient httpClient = HttpClients.custom()
              .setSSLSocketFactory(socketFactory).build();
          HttpComponentsClientHttpRequestFactory factory =
              new HttpComponentsClientHttpRequestFactory(httpClient);
          return new RestTemplate(factory);
      }
      

      【讨论】:

        【解决方案3】:

        Spring Boot 2 解决方案

        以下答案针对的是针对 Spring Boot 2 进行开发并使用自签名证书进行开发的人们(推荐用于生产的适当证书 - 请参阅 https://letsencrypt.org/)。

        您可以使用keytool 命令创建包含自签名证书的密钥库文件:-

        keytool -genkey -storetype PKCS12 \
            -alias selfsigned_localhost_sslserver \
            -keyalg RSA -keysize 2048 -validity 3650 \
            -dname "CN=localhost, OU=Engineering, O=Acme Corp, L=New York, S=New York, C=US" \
            -noprompt -keypass changeit -storepass changeit \
            -keystore keystore-self-signed.p12
        

        keystore-self-signed.p12 文件将包含一个自签名证书,并且此文件可以移动到 src/main/resources 文件夹(或src/test/resources,如果您愿意)。

        将以下内容添加到您的 application.yaml Spring 配置以使用 SSL 并指向密钥库:-

        server:
          port: 443
          ssl:
            enabled: true
            key-store: classpath:keystore-self-signed.p12
            key-store-type: PKCS12
            protocol: TLS
            enabled-protocols: TLSv1.2   # Best practice - see https://github.com/ssllabs/research/wiki/SSL-and-TLS-Deployment-Best-Practices
            key-password: changeit
            key-store-password: changeit
        

        让我们创建一个超级简单的 Spring Boot 控制器端点来测试:-

        @RestController
        public class PingController {
        
            @GetMapping("/ping")
            public ResponseEntity<String> ping() {
                return new ResponseEntity<>("pong", HttpStatus.OK);
            }
        
        }
        

        我们现在可以使用curl 命令(或邮递员)访问此端点,即

        $ curl https://localhost/ping --insecure --silent
        pong
        

        注意:如果我们不包含--insecure,那么curl 将返回curl: (60) SSL certificate problem: self signed certificate

        要使用TestRestTemplate 对其端点进行适当的 Spring Boot 集成测试,我们可以执行以下操作:-

        @RunWith(SpringRunner.class)
        @SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
        public class PingControllerTest {
        
            @Value("${server.ssl.key-store}")
            private Resource keyStore;   // inject keystore specified in config
        
            @Value("${server.ssl.key-store-password}")
            private String keyStorePassword;  // inject password from config
        
            @LocalServerPort
            protected int port;   // server port picked randomly at runtime
        
            private TestRestTemplate restTemplate;
        
            @Before
            public void setup() throws Exception {
                SSLContext sslContext = new SSLContextBuilder()
                    .loadTrustMaterial(
                        keyStore.getURL(),
                        keyStorePassword.toCharArray()
                    ).build();
                SSLConnectionSocketFactory socketFactory = new SSLConnectionSocketFactory(sslContext);
                HttpClient httpClient = HttpClients.custom().setSSLSocketFactory(socketFactory).build();
                HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory(
                    httpClient);
                RestTemplateBuilder rtb = new RestTemplateBuilder()
                    .requestFactory(() -> factory)
                    .rootUri("https://localhost:" + port);
                this.restTemplate = new TestRestTemplate(rtb, null, null, HttpClientOption.SSL);
            }
        
            @Test
            public void shouldPing() {
                ResponseEntity<String> result = restTemplate.getForEntity("/ping", String.class);
                assertEquals(HttpStatus.OK, result.getStatusCode());
                assertEquals("pong", result.getBody());
            }
        
        
        }
        

        如您所见,setup 方法创建了一个 SSLContext 对象的实例,该对象加载(并“信任”)keystore-self-signed.p12 文件中的自签名证书(通过 Spring Resource 对象注入) .

        SSLContext 类被注入到 SSLConnectionSocketFactory 对象中,该对象又被注入到 HttpClient 对象中,然后被注入到 HttpComponentsClientHttpRequestFactory 对象中。

        这个工厂对象最终被注入到TestRestTemplate 实例中,用于shouldPing 集成测试。

        注意 - 我最初使用以下代码浪费了时间:

        ...
        this.restTemplate = new TestRestTemplate(rgb);
        

        ...但是这返回了...

        org.springframework.web.client.ResourceAccessException: I/O error on GET request for "https://localhost:56976/ping": 
            sun.security.validator.ValidatorException: PKIX path building failed: 
            sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target; nested exception is 
            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
        

        通过TestRestTemplate调试后,我意识到你必须使用TestRestTemplateHttpClientOption.SSL的4参数构造函数,即

        this.restTemplate = new TestRestTemplate(rtb, null, null, HttpClientOption.SSL);
        

        但是,如果您使用的是普通的 RestTemplate(例如在 Spring 测试之外),那么 以下作品:-

        ...
        RestTemplate restTemplate = new RestTemplate(rgb);
        

        注意,为了改进 - 创建一个返回 TestRestTemplate 实例的 @Bean 方法。

        【讨论】:

          猜你喜欢
          • 2016-01-28
          • 1970-01-01
          • 2019-11-25
          • 2018-08-02
          • 2014-06-26
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2017-12-11
          相关资源
          最近更新 更多