Spring Boot CALL API 消耗

Posted

技术标签:

【中文标题】Spring Boot CALL API 消耗【英文标题】:Springboot SLL API consume 【发布时间】:2019-08-22 12:13:59 【问题描述】:

我正在尝试使用来自第 3 方服务器的 API。第 3 方向我发送了一个名为 certificate.p12 的 SSL 证书,这是我用来进行握手的证书文件。我使用 SSL 创建了一个自定义 RestTemplate,如下所示:

@Configuration
public class CustomRestTemplate 
    private static final String PASSWORD = "fake_password";
    private static final String RESOURCE_PATH = "keystore/certificate.p12";
    private static final String KEY_TYPE = "PKCS12";

    @Bean
    public RestTemplate restTemplate(RestTemplateBuilder builder) throws Exception 
        char[] password = PASSWORD.toCharArray();

        SSLContext sslContext = SSLContextBuilder
                .create()
//                .loadKeyMaterial(keyStore(RESOURCE_PATH, password), password)
                .loadKeyMaterial(keyStore(getClass().getClassLoader().getResource(RESOURCE_PATH).getFile(), password), password)
                .loadTrustMaterial(null, new TrustSelfSignedStrategy())
                .build();

        HttpClient client = HttpClients
                .custom()
                .setSSLContext(sslContext)
                .build();

        return builder
                .requestFactory(() -> new HttpComponentsClientHttpRequestFactory(client))
                .build();
    

    private KeyStore keyStore(String file, char[] password) throws Exception 
        KeyStore keyStore = KeyStore.getInstance(KEY_TYPE);

        File key = ResourceUtils.getFile(file);
        try (InputStream in = new FileInputStream(key)) 
            keyStore.load(in, password);
        

        return keyStore;
    

然后我使用以下代码调用端点:

@Component
@Service
public class TransactionService implements TransactionInterface 
    @Autowired
    private CustomRestTemplate restTemplate = new CustomRestTemplate();

    private static final String BASE_URL = "https://41.x.x.x:xxxx/";

    @Override
    public List<Transaction> getUnsentTransactions(int connectionId) throws Exception 
        HttpEntity<?> httpEntity = new HttpEntity<>(null, new HttpHeaders());

        ResponseEntity<Transaction[]> resp = restTemplate
            .restTemplate(new RestTemplateBuilder())
            .exchange(BASE_URL + "path/end_point/" + connectionId, HttpMethod.GET, httpEntity, Transaction[].class);

        return Arrays.asList(resp.getBody());
    

我在尝试使用 api 时得到以下堆栈跟踪:

org.springframework.web.client.ResourceAccessException: I/O error on GET request for \"https://41.x.x.x:xxxx/path/endpoint/parameters\": Certificate for <41.x.x.x> doesn't match any of the subject alternative names: [some_name_here, some_name_here];

我对 TLS 或 SSL 证书没有太多经验。我现在真的被困住了,希望我能在这里得到一些帮助。第 3 方为我提供了一个测试站点,我可以在其中测试端点,将 certificate.p12 文件导入浏览器后,我可以使用他们的测试站点访问端点,但我的 Springboot 应用程序仍然无法到达端点。

我需要将证书复制到特定文件夹吗?情况似乎并非如此,因为如果我更改路径或文件名,我会收到 FileNotFoundException,如果我为 certificate.p12 文件输入错误的密码,我会收到密码不正确的错误。我尝试使用 Postman 对其进行测试,但 Postman 返回的堆栈跟踪与我的 Web 应用程序相同。

看了上面的信息,我是不是遗漏了什么?运行时是否未创建密钥库?我是否需要将证书绑定到 JVM 或我的传出请求?

任何帮助将不胜感激。

谢谢

【问题讨论】:

似乎 41.x.x.x 未列出您的 SSL 证书的 SAN 条目。当您通过浏览器访问您的应用程序时,请检查您的证书 SAN 条目。在浏览器地址栏中,单击锁定图标 -> 证书 -> 详细信息 -> 检查主题备用名称 @Haran 当我在浏览器的详细信息下查看证书时,它只有一个 CN,根本没有列出 SAN 【参考方案1】:

您似乎正在尝试连接到证书中没有有效名称的服务器。例如,如果您要连接到“***.com”,则证书需要在“主题”或“主题替代名称”字段中使用该域。

即使是一个测试站点也应该有一个有效的证书,但如果这是不可能的(因为它是一个第三方站点,你不能自己更改它),你可以使用this question禁用验证

当然,这应该只用于测试。

【讨论】:

感谢您的回复,我有点困惑(对证书没有太多经验)。如果第 3 方公司生成包含该信息的新证书文件,或者我应该从我身边做些什么,然后添加 valid name 主题里只有一个CN没有SAN 这不是你的问题。您正在连接到服务器,并且您和服务器都有证书。这是相互 SSL 身份验证。服务器必须遵循一些规则,其中之一是必须将域名指定为主题或 SAN。如果您在浏览器中检查 *** 证书(绿色锁),您将看到它已颁发给 *.stackexchange.com(不是您要连接的域),但 SANS 之一是 *.***.com(其中是正确的域)。这允许我们在多个端点中使用相同的证书。 总结一下:服务器的证书不遵守规则,如果他们不更改它,您唯一可以做的就是忽略错误。有时会在 3rd 方测试页面上发生这种情况,但这是一种不好的做法。 我明白你在说什么。我将要求他们修复他们提供给我们的证书并包括 SAN。你如何忽略这个错误?我是否明确需要这样做?

以上是关于Spring Boot CALL API 消耗的主要内容,如果未能解决你的问题,请参考以下文章

Spring Boot JWT - 如何实现刷新令牌和注销 REST-API

Spring Boot闆嗘垚smart-doc鐢熸垚api鏂囨。

Spring Boot 消耗太多内存

Spring-boot / Http call angular 2的角度没有'Access-Control-Allow-Origin' [重复]

Spring boot - 没有嵌入式 tomcat 的 Rest Call 客户端

Spring Boot在测试时嘲笑其他休息客户端