xiejun
2024-11-01 80b6cbfc9c861469146318d0b3dd5f8b8b525b8a
Source/BladeX-Tool/blade-core-cloud/src/main/java/org/springblade/core/cloud/http/RestTemplateConfiguration.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,191 @@
/*
 *      Copyright (c) 2018-2028, DreamLu All rights reserved.
 *
 *  Redistribution and use in source and binary forms, with or without
 *  modification, are permitted provided that the following conditions are met:
 *
 *  Redistributions of source code must retain the above copyright notice,
 *  this list of conditions and the following disclaimer.
 *  Redistributions in binary form must reproduce the above copyright
 *  notice, this list of conditions and the following disclaimer in the
 *  documentation and/or other materials provided with the distribution.
 *  Neither the name of the dreamlu.net developer nor the names of its
 *  contributors may be used to endorse or promote products derived from
 *  this software without specific prior written permission.
 *  Author: DreamLu å¢æ˜¥æ¢¦ (596392912@qq.com)
 */
package org.springblade.core.cloud.http;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import okhttp3.ConnectionPool;
import okhttp3.OkHttpClient;
import org.springblade.core.cloud.http.logger.HttpLoggingInterceptor;
import org.springblade.core.cloud.http.logger.OkHttpSlf4jLogger;
import org.springblade.core.tool.ssl.DisableValidationTrustManager;
import org.springblade.core.tool.ssl.TrustAllHostNames;
import org.springblade.core.tool.utils.Charsets;
import org.springblade.core.tool.utils.Holder;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.http.client.OkHttp3ClientHttpRequestFactory;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.StringHttpMessageConverter;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.web.client.RestTemplate;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.util.List;
import java.util.concurrent.TimeUnit;
/**
 * Http RestTemplateHeaderInterceptor é…ç½®
 *
 * @author L.cm
 */
@Slf4j
@RequiredArgsConstructor
@AutoConfiguration
@ConditionalOnClass(OkHttpClient.class)
@ConditionalOnProperty(value = "blade.http.enabled", matchIfMissing = true)
public class RestTemplateConfiguration {
   private final BladeHttpProperties properties;
   /**
    * okhttp3 è¯·æ±‚日志拦截器
    *
    * @return HttpLoggingInterceptor
    */
   @Bean
   public HttpLoggingInterceptor loggingInterceptor() {
      HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor(new OkHttpSlf4jLogger());
      interceptor.setLevel(properties.getLevel());
      return interceptor;
   }
   /**
    * okhttp3 é“¾æŽ¥æ± é…ç½®
    *
    * @return okhttp3.ConnectionPool
    */
   @Bean
   @ConditionalOnMissingBean
   public ConnectionPool httpClientConnectionPool() {
      int maxTotalConnections = properties.getMaxConnections();
      long timeToLive = properties.getTimeToLive();
      TimeUnit ttlUnit = properties.getTimeUnit();
      return new ConnectionPool(maxTotalConnections, timeToLive, ttlUnit);
   }
   /**
    * é…ç½®OkHttpClient
    *
    * @param connectionPool é“¾æŽ¥æ± é…ç½®
    * @param interceptor    æ‹¦æˆªå™¨
    * @return OkHttpClient
    */
   @Bean
   @ConditionalOnMissingBean
   public OkHttpClient okHttpClient(ConnectionPool connectionPool, HttpLoggingInterceptor interceptor) {
      boolean followRedirects = properties.isFollowRedirects();
      int connectTimeout = properties.getConnectionTimeout();
      return this.createBuilder(properties.isDisableSslValidation())
         .connectTimeout(connectTimeout, TimeUnit.MILLISECONDS)
         .writeTimeout(30, TimeUnit.SECONDS)
         .readTimeout(30, TimeUnit.SECONDS)
         .followRedirects(followRedirects)
         .connectionPool(connectionPool)
         .addInterceptor(interceptor)
         .build();
   }
   private OkHttpClient.Builder createBuilder(boolean disableSslValidation) {
      OkHttpClient.Builder builder = new OkHttpClient.Builder();
      if (disableSslValidation) {
         try {
            X509TrustManager disabledTrustManager = DisableValidationTrustManager.INSTANCE;
            TrustManager[] trustManagers = new TrustManager[]{disabledTrustManager};
            SSLContext sslContext = SSLContext.getInstance("SSL");
            sslContext.init(null, trustManagers, Holder.SECURE_RANDOM);
            SSLSocketFactory disabledSslSocketFactory = sslContext.getSocketFactory();
            builder.sslSocketFactory(disabledSslSocketFactory, disabledTrustManager);
            builder.hostnameVerifier(TrustAllHostNames.INSTANCE);
         } catch (NoSuchAlgorithmException | KeyManagementException e) {
            log.warn("Error setting SSLSocketFactory in OKHttpClient", e);
         }
      }
      return builder;
   }
   @Bean
   public RestTemplateHeaderInterceptor requestHeaderInterceptor() {
      return new RestTemplateHeaderInterceptor();
   }
   @AutoConfiguration
   @RequiredArgsConstructor
   @ConditionalOnClass(OkHttpClient.class)
   @ConditionalOnProperty(value = "blade.http.rest-template.enable")
   public static class RestTemplateAutoConfiguration {
      private final ApplicationContext context;
      /**
       * æ™®é€šçš„ RestTemplate,不透传请求头,一般只做外部 http è°ƒç”¨
       *
       * @param okHttpClient OkHttpClient
       * @return RestTemplate
       */
      @Bean
      @ConditionalOnMissingBean
      public RestTemplate restTemplate(RestTemplateBuilder restTemplateBuilder, OkHttpClient okHttpClient) {
         restTemplateBuilder.requestFactory(() -> new OkHttp3ClientHttpRequestFactory(okHttpClient));
         RestTemplate restTemplate = restTemplateBuilder.build();
         configMessageConverters(context, restTemplate.getMessageConverters());
         return restTemplate;
      }
   }
   @AutoConfiguration
   @RequiredArgsConstructor
   @ConditionalOnClass(OkHttpClient.class)
   @ConditionalOnProperty(value = "blade.http.lb-rest-template.enable")
   public static class LbRestTemplateAutoConfiguration {
      private final ApplicationContext context;
      /**
       * æ”¯æŒè´Ÿè½½å‡è¡¡çš„ LbRestTemplate
       *
       * @param okHttpClient OkHttpClient
       * @return LbRestTemplate
       */
      @Bean
      @LoadBalanced
      @ConditionalOnMissingBean
      public LbRestTemplate lbRestTemplate(RestTemplateBuilder restTemplateBuilder, OkHttpClient okHttpClient) {
         restTemplateBuilder.requestFactory(() -> new OkHttp3ClientHttpRequestFactory(okHttpClient));
         LbRestTemplate restTemplate = restTemplateBuilder.build(LbRestTemplate.class);
         restTemplate.getInterceptors().add(context.getBean(RestTemplateHeaderInterceptor.class));
         configMessageConverters(context, restTemplate.getMessageConverters());
         return restTemplate;
      }
   }
   private static void configMessageConverters(ApplicationContext context, List<HttpMessageConverter<?>> converters) {
      converters.removeIf(x -> x instanceof StringHttpMessageConverter || x instanceof MappingJackson2HttpMessageConverter);
      converters.add(new StringHttpMessageConverter(Charsets.UTF_8));
      converters.add(new MappingJackson2HttpMessageConverter(context.getBean(ObjectMapper.class)));
   }
}