zuul转发https请求及feign到后端服务(https)跳过ssl证书校验

本文详细描述了如何在安全需求下,通过NginxDockerfile配置HTTPS,以及如何在Zuul和OkHttp中处理SSL验证,包括自定义SSL信任策略和跳过验证,同时介绍了Nacos的HTTPS配置,确保服务的全链路安全连接。

因为安全要求,需要我们的服务全链路 https

1、web 页面:打包放在nginx里

nginx dockerfile 及配置:

NginxDockerfile:
#NginxDockerfile
FROM nginx
MAINTAINER xxx "xxx@qq.com"
WORKDIR /usr/share/nginx/html/
COPY nginx.conf /etc/nginx/
COPY mime.types /etc/nginx/
COPY default.conf /etc/nginx/conf.d/default.conf
RUN sed -i "s#443#8000#g" /etc/nginx/conf.d/default.conf 
COPY lulu_sit.tar.gz /usr/share/nginx/html
RUN mkdir -p /var/log/nginx && mkdir -p /usr/share/nginx/lulutmp/lulu_sit && tar -xzvf /usr/share/nginx/html/lulu_sit.tar.gz -C /usr/share/nginx/lulutmp/ && mv /usr/share/nginx/lulutmp/lulu_sit/* /usr/share/nginx/html/
RUN chown -R nginx:nginx /usr/share/nginx/html/ && chmod -R 755 /usr/sbin/nginx  && \
        chown -R nginx:nginx /var/cache/nginx && \
        chown -R nginx:nginx /var/log/nginx && \
        chown -R nginx:nginx /etc/nginx/conf.d
RUN touch /var/run/nginx.pid && \
        chown -R nginx:nginx /var/run/nginx.pid
EXPOSE 8000
ENTRYPOINT ["/docker-entrypoint.sh"]
CMD ["nginx","-c","/etc/nginx/nginx.conf", "-g", "daemon off;"]
nginx 配置:
#nginx.conf
user  nginx;
worker_processes  auto;
error_log  /var/log/nginx/error.log notice;
pid        /var/run/nginx.pid;
events {
    worker_connections  1024;
}
http {
    include       /etc/nginx/mime.types;
    default_type  application/octet-stream;
    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';
    access_log  /var/log/nginx/access.log  main;
    sendfile        on;
    #tcp_nopush     on;
    keepalive_timeout  65;
    #gzip  on;
    include /etc/nginx/conf.d/*.conf;
}
#default.conf
server {
    listen       443    ssl;
    server_name  xxxxxx;
    client_max_body_size 100m;
    client_header_buffer_size 5120k;
    large_client_header_buffers 16 5120k;
    ssl_certificate      /opt/crt/tls.crt;
    ssl_certificate_key  /opt/crt/tls.key;
    ssl_session_timeout  10m;  
    ssl_session_cache shared:SSL:10m;
    ssl_session_tickets off;
    ssl_stapling on;
    ssl_stapling_verify on;
    ssl_protocols TLSv1.1 TLSv1.2 TLSv1.3;
    ssl_ciphers   ECDHE-RSA-AES256-GCM-SHA512:DHE-RSA-AES256-GCM-SHA512:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-SHA384;
    ssl_prefer_server_ciphers   on;
    location / {
        root /usr/share/nginx/html;
        #add_header Cache-Control "no-cache, no-store, must-revalidate";
        #add_header Pragma "no-cache";
        #add_header Expires 0;
        #add_header Set-Cookie 'env=sit;path=/';
        #index  index.html index.htm;
    }
    #location ~ .*\.(gif|jpg|jpeg|png|bmp|swf|js|css|html)$ {
        #禁止缓存,每次都从服务器请求
        #add_header Cache-Control no-store; 
    #}
    location /web/ {
    	#sh-crm-gate是gate的serviceId,可改为ip
        proxy_pass https://sh-crm-gate:8080/web/;
        proxy_connect_timeout 7000;
        proxy_read_timeout 7000;
        charset utf-8;
    }
    error_page   500 502 503 504  /50x.html;
    location = /50x.html {
        root   /usr/share/nginx/html;
    }
}

2、后端服务:gate(zuul),interface(此处就不多列业务服务了),xxljob(1.7.2版本)

2.1.1、(重点)因为某些原因我们这个版本的架构网关路由使用的是 zuul,zuul 在 router 服务时一般情况下使用的是 httpclient(我们的版本是 org.apache.httpcomponents:httpclient:4.5.13)也就是CloseableHttpClient,可以在启动类或者配置类里面加上下面这块代码覆盖Bean :CloseableHttpClient

//跳过ssl验证
 @Bean
    public CloseableHttpClient getIgnoeSSLClient() throws Exception {
        SSLContext sslContext = SSLContexts.custom().loadTrustMaterial(null, new TrustStrategy() {
            @Override
            public boolean isTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {
                return true;
            }
        }).build();
        //创建httpClient
        CloseableHttpClient client = HttpClients.custom().setSSLContext(sslContext).
                setSSLHostnameVerifier(new NoopHostnameVerifier()).build();
        return client;
    }

2.1.2、如果是使用了 okhttp 可以加如下代码

package com.arvato.crm.configuration;

import lombok.extern.slf4j.Slf4j;
import lombok.var;
import okhttp3.ConnectionPool;
import okhttp3.OkHttpClient;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import javax.net.ssl.*;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.cert.X509Certificate;
import java.util.concurrent.TimeUnit;
@Slf4j
@Configuration
public class OkHttpConfig {

    @Value("${okhttp.connectTimeout:30}")
    private Long connectTimeout;
    @Value("${okhttp.readTimeout:30}")
    private Long readTimeout;
    @Value("${okhttp.writeTimeout:30}")
    private Long writeTimeout;
    @Value("${okhttp.maxConnections:10}")
    private Integer maxConnections;
    @Value("${okhttp.keepAliveDuration:5}")
    private Long keepAliveDuration;

    public static final X509TrustManager IGNORE_SSL_TRUST_MANAGER_X509 = new X509TrustManager() {
        @Override
        public void checkClientTrusted(X509Certificate[] chain, String authType) {
        }

        @Override
        public void checkServerTrusted(X509Certificate[] chain, String authType) {
        }

        @Override
        public X509Certificate[] getAcceptedIssuers() {
            return new X509Certificate[] {};
        }
    };

    /**
     * Get initialized SSLContext instance which ignored SSL certification
     *
     * @return
     * @throws NoSuchAlgorithmException
     * @throws KeyManagementException
     */
    public static SSLContext getIgnoreInitedSslContext() throws NoSuchAlgorithmException, KeyManagementException {
        var sslContext = SSLContext.getInstance("SSL");
        sslContext.init(null, new TrustManager[] { IGNORE_SSL_TRUST_MANAGER_X509 }, new SecureRandom());
        return sslContext;
    }

    /**
     * Get HostnameVerifier which ignored SSL certification
     *
     * @return
     */
    public static HostnameVerifier getIgnoreSslHostnameVerifier() {
        return new HostnameVerifier() {
            @Override
            public boolean verify(String arg0, SSLSession arg1) {
                return true;
            }
        };
    }

    @Bean
    public OkHttpClient okHttpClient() throws Exception{
        log.info("okhttp.connectTimeout: {}",connectTimeout);
        log.info("okhttp.readTimeout: {}",readTimeout);
        log.info("okhttp.writeTimeout: {}",writeTimeout);
        log.info("okhttp.maxConnections: {}",maxConnections);
        log.info("okhttp.keepAliveDuration: {}",keepAliveDuration);

        return new OkHttpClient.Builder()
                .connectTimeout(connectTimeout, TimeUnit.SECONDS)
                .readTimeout(readTimeout, TimeUnit.SECONDS)
                .writeTimeout(writeTimeout, TimeUnit.SECONDS)
                .retryOnConnectionFailure(false)
                .connectionPool(new ConnectionPool(maxConnections , keepAliveDuration, TimeUnit.MINUTES))
                .sslSocketFactory(getIgnoreInitedSslContext().getSocketFactory(),IGNORE_SSL_TRUST_MANAGER_X509)
                .hostnameVerifier(getIgnoreSslHostnameVerifier())
                .build();
    }
}

2.2、interface 服务使用 feign调用其他服务时需要配置 feign 跳过 ssl 验证:

2.2.1 feign配置类:
package com.arvato.crm.config;

import feign.Client;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.cloud.netflix.ribbon.SpringClientFactory;
import org.springframework.cloud.openfeign.ribbon.CachingSpringLoadBalancerFactory;
import org.springframework.cloud.openfeign.ribbon.LoadBalancerFeignClient;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.X509Certificate;

@Configuration
public class FeignConfiguration {

    @Bean
    public CachingSpringLoadBalancerFactory cachingFactory(SpringClientFactory clientFactory) {
        return new CachingSpringLoadBalancerFactory(clientFactory);
    }

    @Bean
    @ConditionalOnMissingBean
    public Client feignClient(CachingSpringLoadBalancerFactory cachingFactory,
                              SpringClientFactory clientFactory) throws NoSuchAlgorithmException, KeyManagementException {
        SSLContext ctx = SSLContext.getInstance("SSL");
        X509TrustManager tm = new X509TrustManager() {
            @Override
            public void checkClientTrusted(X509Certificate[] chain, String authType) {
            }
            @Override
            public void checkServerTrusted(X509Certificate[] chain, String authType) {
            }
            @Override
            public X509Certificate[] getAcceptedIssuers() {
                //如果这里后续报空指针,就return new X509Certificate[0]
                return null;
            }
        };
        ctx.init(null, new TrustManager[]{tm}, null);
        return new LoadBalancerFeignClient(new Client.Default(ctx.getSocketFactory(),
                (hostname, session) -> true),
                cachingFactory, clientFactory);
    }
}

2.2.2:feignclient配置参考:
@FeignClient(name = "sh-crm-gate",url = "https://sh-crm-gate:8080",configuration = FeignConfiguration.class)

2.3、因为使用的 xxljob版本过低,xxljob里的CloseableHttpClient 并没有设置跳过 ssl 校验(高版本已经没有AdminApiUtil这个类了,而且新的类(好像是 xxljobUtil)已经做了跳过ssl校验),所以需要覆盖 xxljob 的类:AdminApiUtil.java,直接复制到我们的项目里,包名路径保持和 xxljob 里的一样,只需要修改callapi 方法里的CloseableHttpClient即可,代码如下

SSLContext sslContext = SSLContexts.custom().loadTrustMaterial(null, new TrustStrategy() {
            @Override
            public boolean isTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {
                return true;
            }
        }).build();
        //创建httpClient
        CloseableHttpClient httpClient = HttpClients.custom().setSSLContext(sslContext).
                setSSLHostnameVerifier(new NoopHostnameVerifier()).build();

3.注册中心是使用的 nacos

nacos https配置(重点):

#例如gate服务 注册为 https 的服务
spring:
    application:
        name: sh-crm-gate
    cloud:
      nacos:
        discovery:
          secure: true
        service:
          secure: true
server:
    port: 8080
    tomcat:
        uri-encoding: UTF-8
        max-threads: 5000
        max-connections: 50000
  #ssl配置
    ssl:
      enabled: true
      key-store: /opt/xxx.p12
      key-store-password: xxxxxxx
      key-alias: xxxxxx
      key-store-type: PKCS12

GateDockerfile

FROM alpine:3.17.1
RUN sed -i -e 's|dl-cdn.alpinelinux.org|mirrors.aliyun.com|g' /etc/apk/repositories
RUN apk add openjdk8 curl jq
RUN apk upgrade && apk add tzdata
WORKDIR /build
#设置时区
RUN cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && echo 'Asia/Shanghai' >/etc/timezone \
    && mkdir config
EXPOSE 8080
RUN addgroup -g 5000 -S lululemonapp \
    && adduser -h /home/lululemonapp lululemonapp  -D -G lululemonapp -u 5000 -s /bin/sh \
    && chown -R lululemonapp:lululemonapp /opt \
    && chown -R lululemonapp:lululemonapp /build \
    && chown -R lululemonapp:lululemonapp /mnt
USER lululemonapp
COPY gate.* /build
COPY gate_server.jar /build
COPY GateDockerfile /build
COPY saml-pub-key /build
#启动脚本
CMD sh gate.sh

gate.sh

#!/bin/bash
cat /vault/secrets/config.json > config/gate.json
grep db_pwd  -rl config/ | xargs sed -i "s/db_pwd/spring.datasource.password/g"
grep 'redis_pwd'  -rl config/ | xargs sed -i 's/redis_pwd/spring.redis.password/g'
grep rabbit_pwd  -rl config/ | xargs sed -i "s/rabbit_pwd/spring.rabbitmq.password/g"
config=$(cat config/gate.json)
echo "service starting................"
java  -Dspring.application.json="$config"  -jar "gate_server.jar" --spring.config.location=/opt/bootstrap.yml
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值