• SpringBoot_minio sdk使用自签名https证书错误处理


    1.问题描述

    minio 8.4.4 使用自签名的https的api连接会报错证书错误

    1.1 报错日志

    PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
    
    • 1

    1.2 maven 依赖配置

            
            <dependency>
                <groupId>io.miniogroupId>
                <artifactId>minioartifactId>
                <version>8.4.4version>
                <exclusions>
                    <exclusion>
                        <groupId>com.squareup.okhttp3groupId>
                        <artifactId>okhttpartifactId>
                    exclusion>
                exclusions>
            dependency>
    
            <dependency>
                <groupId>com.squareup.okhttp3groupId>
                <artifactId>okhttpartifactId>
                <version>4.10.0version>
            dependency>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    1.3 当前spring MinioClient配置

    @Configuration
    @EnableConfigurationProperties(MinioProperties.class)
    public class MinioConfig {
        @Bean
        public MinioClient minioClient(MinioProperties properties){
            properties.check();
            return MinioClient.builder()
                    .endpoint(properties.getEndpoint())
                    .credentials(properties.getAccessKey(), properties.getSecretKey())
                    .build();
    
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    2.问题分析

    通常是因为MinIO默认情况下会验证服务器的TLS证书。在生产环境中,使用自签名证书并不推荐,因为它们不受信任的证书颁发机构(CA)签署,可能会导致安全问题。

    3.问题解决

    3.1 使用受信任的证书

    为了在生产环境中确保安全性,建议获取一个受信任的SSL证书,可以从证书颁发机构(CA)购买,或者使用免费的证书颁发机构(例如Let’s Encrypt)获取SSL证书。

    3.2 忽略证书验证

    3.2.1 minio客户端

    在MinIO客户端(例如mc命令行工具)中,可以使用–insecure选项来忽略证书验证。例如:

    mc --insecure <command>
    
    • 1

    这会告诉MinIO客户端不要验证服务器的TLS证书。请注意,这种做法会降低安全性,不建议在生产环境中使用。

    3.2.2 minio sdk 忽略证书验证

    在使用Java SDK与自签名证书的服务器进行通信时,一般可以通过自定义SSLContext来忽略证书验证。

    MinIO的Java SDK(version 8.0.6及以上)允许自定义OkHttpClient,我们可以使用httpClient方法传递一个自定义的OkHttpClient实例。以便在HTTP、正常HTTPS和自签名HTTPS之间实现兼容性

    下面是如何使用自定义的OkHttpClient实现对HTTP、正常HTTPS和自签名HTTPS的兼容性

    @Configuration
    @EnableConfigurationProperties(MinioProperties.class)
    public class MinioConfig {
        private static final Logger LOGGER = LoggerFactory.getLogger(MinioConfig.class);
    
        @Bean
        public MinioClient minioClient(MinioProperties properties){
            properties.check();
    
            // Create a trust manager that does not validate certificate chains
            TrustManager[] trustAllCerts = new TrustManager[] {
                    new X509TrustManager() {
                        public X509Certificate[] getAcceptedIssuers() {
                            return new X509Certificate[0];
                        }
    
                        public void checkClientTrusted(X509Certificate[] certs, String authType) {
                            // Do nothing (trust any client certificate)
                        }
    
                        public void checkServerTrusted(X509Certificate[] certs, String authType) {
                            // Do nothing (trust any server certificate)
                        }
                    }
            };
    
            // Install the all-trusting trust manager
            SSLContext sslContext = null;
            try {
                sslContext = SSLContext.getInstance("SSL");
                sslContext.init(null, trustAllCerts, new java.security.SecureRandom());
            } catch (Exception e) {
                LOGGER.error("Install the all-trusting trust manager error:{}", e.getMessage());
            }
    
    
            // Create a custom OkHttpClient that trusts all certificates
            OkHttpClient customHttpClient = new OkHttpClient.Builder()
                    .sslSocketFactory(sslContext.getSocketFactory(), (X509TrustManager) trustAllCerts[0])
                    .hostnameVerifier((hostname, session) -> true)
                    .build();
    
            // Set the custom SSLContext for MinioClient
            return MinioClient.builder()
                    .endpoint(properties.getEndpoint())
                    .credentials(properties.getAccessKey(), properties.getSecretKey())
                    .httpClient(customHttpClient)
                    .build();
    
        }
    
    
    }
    
    
    • 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
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    1. 在上面的代码中,我们创建了一个SSLContext,其中的X509TrustManager会信任所有证书,无论其是否由信任的证书颁发机构(CA)签署。
    2. 创建了一个自定义的OkHttpClient,该客户端信任所有证书。然后,我们使用MinioClient.builder()方法,将自定义的OkHttpClient传递给httpClient()方法

    这种方法会将所有证书都视为受信任的,因此请仅在非生产环境中使用此方法,以确保通信的安全性和完整性。在生产环境中,建议使用受信任的SSL证书。

    3.2.2.1 拓展: 补充minioclient请求日志

    之前minioclient与服务器端交互使用默认的httpclient的客户端,请求没有打印详细日志. 既然上面自定义自己的httpclient那么可以补充自定义拦截器打印日志

    public class CustomLoggingInterceptor implements Interceptor {
        @Override
        public Response intercept(Chain chain) throws IOException {
            Request request = chain.request();
            
            long startTime = System.nanoTime();
            System.out.println("Sending request " + request.url() + " on " + chain.connection() + "\n" + request.headers());
    
            Response response = chain.proceed(request);
            
            long endTime = System.nanoTime();
            System.out.println("Received response for " + response.request().url() + " in " + ((endTime - startTime) / 1e6) + "ms\n" + response.headers());
    
            MediaType contentType = response.body().contentType();
            String content = response.body().string();
            System.out.println("Response body:\n" + content);
    
            ResponseBody wrappedBody = ResponseBody.create(contentType, content);
            return response.newBuilder().body(wrappedBody).build();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    修改MinioConfig增加okhttp拦截器

            // Create a custom OkHttpClient that trusts all certificates
            OkHttpClient customHttpClient = new OkHttpClient.Builder()
                    .sslSocketFactory(sslContext.getSocketFactory(), (X509TrustManager) trustAllCerts[0])
                    .hostnameVerifier((hostname, session) -> true)
                    .addInterceptor(new CustomLoggingInterceptor()) // Add custom interceptor here
                    .build();
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    效果
    在这里插入图片描述

    4. 问题总结

    minio客户端本质使用httpclient与服务端交互,因此证书问题处理其实只是httpclient对证书的兼容处理。该处理方式可以运用到其他使用到httpclient的场景。

    5.附录

    代码优化

    @Configuration
    @EnableConfigurationProperties(MinioProperties.class)
    public class MinioConfig {
        private static final Logger LOGGER = LoggerFactory.getLogger(MinioConfig.class);
    
        @Bean
        public MinioClient minioClient(MinioProperties properties){
            properties.check();
    
            OkHttpClient customHttpClient = null;
            if (properties.getEndpoint().startsWith("https://")) {
                // 如果是HTTPS,使用自定义的OkHttpClient处理自签名的HTTPS请求
                customHttpClient = createCustomOkHttpClient();
            }
    
            MinioClient minioClient;
            if (customHttpClient != null) {
                // 如果使用了自定义的OkHttpClient
                minioClient = MinioClient.builder()
                        .endpoint(properties.getEndpoint())
                        .credentials(properties.getAccessKey(), properties.getSecretKey())
                        .httpClient(customHttpClient)
                        .build();
    
            } else {
                // 如果是普通HTTP,使用默认的OkHttpClient
                minioClient = MinioClient.builder()
                        .endpoint(properties.getEndpoint())
                        .credentials(properties.getAccessKey(), properties.getSecretKey())
                        .build();
    
            }
            return minioClient;
        }
    
        /**
         * Set the custom SSLContext for MinioClient
         * @return
         */
        private static OkHttpClient createCustomOkHttpClient() {
            // 创建自定义的OkHttpClient,用于处理自签名的HTTPS请求
    
            // Create a trust manager that does not validate certificate chains
            TrustManager[] trustAllCerts = new TrustManager[] {
                    new X509TrustManager() {
                        public X509Certificate[] getAcceptedIssuers() {
                            return new X509Certificate[0];
                        }
    
                        public void checkClientTrusted(X509Certificate[] certs, String authType) {
                            // Do nothing (trust any client certificate)
                        }
    
                        public void checkServerTrusted(X509Certificate[] certs, String authType) {
                            // Do nothing (trust any server certificate)
                        }
                    }
            };
    
            // Install the all-trusting trust manager
            SSLContext sslContext = null;
            try {
                sslContext = SSLContext.getInstance("SSL");
                sslContext.init(null, trustAllCerts, new java.security.SecureRandom());
            } catch (Exception e) {
                LOGGER.error("Install the all-trusting trust manager error:{}", e.getMessage());
            }
    
    
            // Create a custom OkHttpClient that trusts all certificates
            OkHttpClient customHttpClient = new OkHttpClient.Builder()
                    .sslSocketFactory(sslContext.getSocketFactory(), (X509TrustManager) trustAllCerts[0])
                    .hostnameVerifier((hostname, session) -> true)
                    // 增加minio http请求日志打印
                    //.addInterceptor(new CustomLoggingInterceptor()) // Add custom interceptor here
                    .build();
            return customHttpClient;
        }
    
    
    }
    
    
    • 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
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
  • 相关阅读:
    从 Docker Hub 拉取镜像受阻?这些解决方案帮你轻松应对
    超硬核解析!Apache Hudi灵活的Payload机制
    基于DQN的强化学习 快速浏览(基础知识+示例代码)
    【网络技术】【Kali Linux】Wireshark嗅探(十六)TLS(传输层安全协议)报文捕获及分析
    C++ 数据结构与算法(十三)(单调栈)
    日期间隔计算器-计算两个日期之间相差多少天-计算某天之后的多少天是几号计算器
    OC-消息转发
    世界杯winner只属于你——MESSI
    GSN前瞻预处理
    VK1620温控仪/智能电表LED数显驱动芯片3/4线接口内置 RC振荡器,提供技术支持
  • 原文地址:https://blog.csdn.net/Myron_007/article/details/133988355