因为安全要求,需要我们的服务全链路 https
1、web 页面:打包放在nginx里
nginx dockerfile 及配置:
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 配置:
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;
keepalive_timeout 65;
include /etc/nginx/conf.d/*.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;
}
location /web/ {
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
@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();
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[] {};
}
};
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;
}
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 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();
CloseableHttpClient httpClient = HttpClients.custom().setSSLContext(sslContext).
setSSLHostnameVerifier(new NoopHostnameVerifier()).build();
3.注册中心是使用的 nacos
nacos 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:
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