安全规范中有一条是要求尽量使用https而弃用http(新Chrome将标记非HTTPS网站为不安全),其实启用https和之前的ipv6改造一样,并不是什么高难度或者工作流繁多的的改造,只需将中间件启用https支持即可。在spring boot项目中,基本上都是使用内置的中间件(tomcat、netty等),下面记录下改造的思路:
方案1: 服务均启用https,步骤有:
- 所有的micro service均启用https
- 把eureka的注册地址改为https方式
- 把config service的注册地址改为https方式
- 把feign的调用方式改为支持https方式
- 每个micro service均需导入证书,并且访问的时候需要导入证书(不然会报unable to find valid certification path to requested target错误)
- 证书里需要把micro service的serviceId注册进去(不然会报Certificate doesn't match any of the subject alternative names错误)
方案比较稳妥,所有服务中均走了ssl,满足安全条件,就是乍一看改造量有点多啊......
方案2:micro service只会暴露gateway,其余服务均是不暴露的,因此我们可以只改造gateway服务,步骤有:
- gateway service启用https
- gateway service改造lb://micro_service_id的地址替换方式
该方案改造量最小,且由于spring cloud的服务隔离的关系,并无需担心其余服务的安全问题。
实现方案:
1. 生成证书
可以用JDK自带的keytool工具,来生成证书,具体语法为:
C:\Users\Administrator>keytool 密钥和证书管理工具 命令: -certreq 生成证书请求 -changealias 更改条目的别名 -delete 删除条目 -exportcert 导出证书 -genkeypair 生成密钥对 -genseckey 生成密钥 -gencert 根据证书请求生成证书 -importcert 导入证书或证书链 -importpass 导入口令 -importkeystore 从其他密钥库导入一个或所有条目 -keypasswd 更改条目的密钥口令 -list 列出密钥库中的条目 -printcert 打印证书内容 -printcertreq 打印证书请求的内容 -printcrl 打印 CRL 文件的内容 -storepasswd 更改密钥库的存储口令 使用 "keytool -command_name -help" 获取 command_name 的用法
生成证书的语句为:
keytool -genkeypair -alias "ncdt" -keyalg "RSA" -keystore "C:\ncdt.keystore"
执行后,根据提示完成即可(测试用的话,直接一直按回车,最后确认y即可),如下图:
2. spring cloud gateway项目启用https(其实也就是spring boot项目启用https),只需要在application.yml里增加ssl配置即可。
server: port: 443 ssl: key-alias: ncdt enabled: true key-store-password: password key-store-type: JKS key-store: ncdt.keystore
3.spring cloud gateway项目增加HttpsReconstructFilter
如何来重写这个filter呢,首先先不要写这个filter,直接启动gateway并访问地址:https://localhost/,就会报如下错误(里面的*****是我自己的服务名,故隐藏了):

2018-09-19 11:19:26.420 INFO 10808 --- [ctor-http-nio-1] c.netflix.loadbalancer.BaseLoadBalancer : Client: ***** instantiated a LoadBalancer: DynamicServerListLoadBalancer:{NFLoadBalancer:name=*****,current list of Servers=[],Load balancer stats=Zone stats: {},Server stats: []}ServerList:null 2018-09-19 11:19:26.424 INFO 10808 --- [ctor-http-nio-1] c.n.l.DynamicServerListLoadBalancer : Using serverListUpdater PollingServerListUpdater 2018-09-19 11:19:26.427 INFO 10808 --- [ctor-http-nio-1] c.n.l.DynamicServerListLoadBalancer : DynamicServerListLoadBalancer for client ***** initialized: DynamicServerListLoadBalancer:{NFLoadBalancer:name=*****,current list of Servers=[],Load balancer stats=Zone stats: {},Server stats: []}ServerList:org.springframework.cloud.netflix.ribbon.eureka.DomainExtractingServerList@7b98208b 2018-09-19 11:19:26.490 ERROR 10808 --- [ctor-http-nio-1] .a.w.r.e.DefaultErrorWebExceptionHandler : Failed to handle request [GET https://localhost/******] org.springframework.cloud.gateway.support.NotFoundException: Unable to find instance for ***** at org.springframework.cloud.gateway.filter.LoadBalancerClientFilter.filter(LoadBalancerClientFilter.java:72) ~[spring-cloud-gateway-core-2.0.1.RELEASE.jar:2.0.1.RELEASE] at org.springframework.cloud.gateway.handler.FilteringWebHandler$GatewayFilterAdapter.filter(FilteringWebHandler.java:133) ~[spring-cloud-gateway-core-2.0.1.RELEASE.jar:2.0.1.RELEASE] at org.springframework.cloud.gateway.filter.OrderedGatewayFilter.filter(OrderedGatewayFilter.java:44) ~[spring-cloud-gateway-core-2.0.1.RELEASE.jar:2.0.1.RELEASE] at org.springframework.cloud.gateway.handler.FilteringWebHandler$DefaultGatewayFilterChain.lambda$filter$0(FilteringWebHandler.java:115) ~[spring-cloud-gateway-core-2.0.1.RELEASE.jar:2.0.1.RELEASE] at reactor.core.publisher.MonoDefer.subscribe(MonoDefer.java:45) ~[reactor-core-3.1.8.RELEASE.jar:3.1.8.RELEASE] at reactor.core.publisher.MonoDefer.subscribe(MonoDefer.java:53) ~[reactor-core-3.1.8.RELEASE.jar:3.1.8.RELEASE] at reactor.core.publisher.MonoDefer.subscribe(MonoDefer.java:53) ~[reactor-core-3.1.8.RELEASE.jar:3.1.8.RELEASE] at reactor.core.publisher.MonoPeekTerminal.subscribe(MonoPeekTerminal.java:61) ~[reactor-core-3.1.8.RELEASE.jar:3.1.8.RELEASE] at reactor.core.publisher.MonoDefer.subscribe(MonoDefer.java:53) ~[reactor-core-3.1.8.RELEASE.jar:3.1.8.RELEASE] at reactor.core.publisher.MonoDefer.subscribe(MonoDefer.java:53) ~[reactor-core-3.1.8.RELEASE.jar:3.1.8.RELEASE] at reactor.core.publisher.Mono.subscribe(Mono.java:3080) ~[reactor-core-3.1.8.RELEASE.jar:3.1.8.RELEASE] at reactor.core.publisher.MonoIgnoreThen$ThenIgnoreMain.drain(MonoIgnoreThen.java:172) ~[reactor-core-3.1.8.RELEASE.jar:3.1.8.RELEASE] at reactor.core.publisher.MonoIgnoreThen.subscribe(MonoIgnoreThen.java:56) ~[reactor-core-3.1.8.RELEASE.jar:3.1.8.RELEASE] at reactor.core.publisher.MonoDefer.subscribe(MonoDefer.java:53) ~[reactor-core-3.1.8.RELEASE.jar:3.1.8.RELEASE] at reactor.core.publisher.MonoDefer.subscribe(MonoDefer.java:53) ~[reactor-core-3.1.8.RELEASE.jar:3.1.8.RELEASE] at reactor.core.publisher.Mono.subscribe(Mono.java:3080) ~[reactor-core-3.1.8.RELEASE.jar:3.1.8.RELEASE] at reactor.core.publisher.MonoIgnoreThen$ThenIgnoreMain.drain(MonoIgnoreThen.java:172) ~[reactor-core-3.1.8.RELEASE.jar:3.1.8.RELEASE] at reactor.core.publisher.MonoIgnoreThen.subscribe(MonoIgnoreThen.java:56) ~[reactor-core-3.1.8.RELEASE.jar:3.1.8.RELEASE] at reactor.core.publisher.MonoFlatMap$FlatMapMain.onNext(MonoFlatMap.java:150) ~[reactor-core-3.1.8.RELEASE.jar:3.1.8.RELEASE] at reactor.core.publisher.FluxSwitchIfEmpty$SwitchIfEmptySubscriber.onNext(FluxSwitchIfEmpty.java:67) ~[reactor-core-3.1.8.RELEASE.jar:3.1.8.RELEASE] at reactor.core.publisher.MonoNext$NextSubscriber.onNext(MonoNext.java:76) ~[reactor-core-3.1.8.RELEASE.jar:3.1.8.RELEASE] at reactor.core.publisher.FluxConcatMap$ConcatMapImmediate.innerNext(FluxConcatMap.java:271) ~[reactor-core-3.1.8.RELEASE.jar:3.1.8.RELEASE] at reactor.core.publisher.FluxConcatMap$ConcatMapInner.onNext(FluxConcatMap.java:803) ~[reactor-core-3.1.8.RELEASE.jar:3.1.8.RELEASE] at reactor.core.publisher.FluxMap$MapSubscriber.onNext(FluxMap.java:108) ~[reactor-core-3.1.8.RELEASE.jar:3.1.8.RELEASE] at reactor.core.publisher.FluxSwitchIfEmpty$SwitchIfEmptySubscriber.onNext(FluxSwitchIfEmpty.java:67) ~[reactor-core-3.1.8.RELEASE.jar:3.1.8.RELEASE] at reactor.core.publisher.Operators$MonoSubscriber.complete(Operators.java:1083) ~[reactor-core-3.1.8.RELEASE.jar:3.1.8.RELEASE] at reactor.core.publisher.MonoFlatMap$FlatMapMain.onNext(MonoFlatMap.java:144) ~[reactor-core-3.1.8.RELEASE.jar:3.1.8.RELEASE] at reactor.core.publisher.FluxMap$MapSubscriber.onNext(FluxMap.java:108) ~[reactor-core-3.1.8.RELEASE.jar:3.1.8.RELEASE] at reactor.core.publisher.MonoNext$NextSubscriber.onNext(MonoNext.java:76) ~[reactor-core-3.1.8.RELEASE.jar:3.1.8.RELEASE] at reactor.core.publisher.FluxConcatMap$ConcatMapImmediate.innerNext(FluxConcatMap.java:271) ~[reactor-core-3.1.8.RELEASE.jar:3.1.8.RELEASE] at reactor.core.publisher.FluxConcatMap$ConcatMapInner.onNext(FluxConcatMap.java:803) ~[reactor-core-3.1.8.RELEASE.jar:3.1.8.RELEASE] at reactor.core.publisher.FluxOnErrorResume$ResumeSubscriber.onNext(FluxOnErrorResume.java:73) ~[reactor-core-3.1.8.RELEASE.jar:3.1.8.RELEASE] at reactor.core.publisher.FluxPeek$PeekSubscriber.onNext(FluxPeek.java:185) ~[reactor-core-3.1.8.RELEASE.jar:3.1.8.RELEASE] at reactor.core.publisher.Operators$MonoSubscriber.complete(Operators.java:1083) ~[reactor-core-3.1.8.RELEASE.jar:3.1.8.RELEASE] at reactor.core.publisher.MonoFilterWhen$MonoFilterWhenMain.onNext(MonoFilterWhen.java:138) ~[reactor-core-3.1.8.RELEASE.jar:3.1.8.RELEASE] at reactor.core.publisher.Operators$ScalarSubscription.request(Operators.java:1640) ~[reactor-core-3.1.8.RELEASE.jar:3.1.8.RELEASE] at reactor.core.publisher.MonoFilterWhen$MonoFilterWhenMain.onSubscribe(MonoFilterWhen.java:103) ~[reactor-core-3.1.8.RELEASE.jar:3.1.8.RELEASE] at reactor.core.publisher.MonoJust.subscribe(MonoJust.java:54) ~[reactor-core-3.1.8.RELEASE.jar:3.1.8.RELEASE] at reactor.core.publisher.MonoFilterWhen.subscribe(MonoFilterWhen.java:56) ~[reactor-core-3.1.8.RELEASE.jar:3.1.8.RELEASE] at reactor.core.publisher.MonoPeek.subscribe(MonoPeek.java:71) ~[reactor-core-3.1.8.RELEASE.jar:3.1.8.RELEASE] at reactor.core.publisher.MonoOnErrorResume.subscribe(MonoOnErrorResume.java:44) ~[reactor-core-3.1.8.RELEASE.jar:3.1.8.RELEASE] at reactor.core.publisher.Mono.subscribe(Mono.java:3080) ~[reactor-core-3.1.8.RELEASE.jar:3.1.8.RELEASE] at reactor.core.publisher.FluxConcatMap$ConcatMapImmediate.drain(FluxConcatMap.java:418) ~[reactor-core-3.1.8.RELEASE.jar:3.1.8.RELEASE] at reactor.core.publisher.FluxConcatMap$ConcatMapImmediate.onNext(FluxConcatMap.java:241) ~[reactor-core-3.1.8.RELEASE.jar:3.1.8.RELEASE] at reactor.core.publisher.FluxDematerialize$DematerializeSubscriber.onNext(FluxDematerialize.java:114) ~[reactor-core-3.1.8.RELEASE.jar:3.1.8.RELEASE] at reactor.core.publisher.FluxDematerialize$DematerializeSubscriber.onNext(FluxDematerialize.java:42) ~[reactor-core-3.1.8.RELEASE.jar:3.1.8.RELEASE] at reactor.core.publisher.FluxIterable$IterableSubscription.slowPath(FluxIterable.java:244) ~[reactor-core-3.1.8.RELEASE.jar:3.1.8.RELEASE] at reactor.core.publisher.FluxIterable$IterableSubscription.request(FluxIterable.java:202) ~[reactor-core-3.1.8.RELEASE.jar:3.1.8.RELEASE] at reactor.core.publisher.FluxDematerialize$DematerializeSubscriber.request(FluxDematerialize.java:157) ~[reactor-core-3.1.8.RELEASE.jar:3.1.8.RELEASE] at reactor.core.publisher.FluxConcatMap$ConcatMapImmediate.onSubscribe(FluxConcatMap.java:227) ~[reactor-core-3.1.8.RELEASE.jar:3.1.8.RELEASE] at reactor.core.publisher.FluxDematerialize$DematerializeSubscriber.onSubscribe(FluxDematerialize.java:88) ~[reactor-core-3.1.8.RELEASE.jar:3.1.8.RELEASE] at reactor.core.publisher.FluxIterable.subscribe(FluxIterable.java:140) ~[reactor-core-3.1.8.RELEASE.jar:3.1.8.RELEASE] at reactor.core.publisher.FluxIterable.subscribe(FluxIterable.java:64) ~[reactor-core-3.1.8.RELEASE.jar:3.1.8.RELEASE] at reactor.core.publisher.FluxDematerialize.subscribe(FluxDematerialize.java:39) ~[reactor-core-3.1.8.RELEASE.jar:3.1.8.RELEASE] at reactor.core.publisher.FluxDefer.subscribe(FluxDefer.java:55) ~[reactor-core-3.1.8.RELEASE.jar:3.1.8.RELEASE] at reactor.core.publisher.FluxConcatMap.subscribe(FluxConcatMap.java:121) ~[reactor-core-3.1.8.RELEASE.jar:3.1.8.RELEASE] at reactor.core.publisher.MonoNext.subscribe(MonoNext.java:40) ~[reactor-core-3.1.8.RELEASE.jar:3.1.8.RELEASE] at reactor.core.publisher.MonoMap.subscribe(MonoMap.java:55) ~[reactor-core-3.1.8.RELEASE.jar:3.1.8.RELEASE] at reactor.core.publisher.MonoFlatMap.subscribe(MonoFlatMap.java:60) ~[reactor-core-3.1.8.RELEASE.jar:3.1.8.RELEASE] at reactor.core.publisher.MonoSwitchIfEmpty.subscribe(MonoSwitchIfEmpty.java:44) ~[reactor-core-3.1.8.RELEASE.jar:3.1.8.RELEASE] at reactor.core.publisher.MonoMap.subscribe(MonoMap.java:55) ~[reactor-core-3.1.8.RELEASE.jar:3.1.8.RELEASE] at reactor.core.publisher.Mono.subscribe(Mono.java:3080) ~[reactor-core-3.1.8.RELEASE.jar:3.1.8.RELEASE] at reactor.core.publisher.FluxConcatMap$ConcatMapImmediate.drain(FluxConcatMap.java:418) ~[reactor-core-3.1.8.RELEASE.jar:3.1.8.RELEASE] at reactor.core.publisher.FluxConcatMap$ConcatMapImmediate.onSubscribe(FluxConcatMap.java:210) ~[reactor-core-3.1.8.RELEASE.jar:3.1.8.RELEASE] at reactor.core.publisher.FluxIterable.subscribe(FluxIterable.java:140) ~[reactor-core-3.1.8.RELEASE.jar:3.1.8.RELEASE] at reactor.core.publisher.FluxIterable.subscribe(FluxIterable.java:64) ~[reactor-core-3.1.8.RELEASE.jar:3.1.8.RELEASE] at reactor.core.publisher.FluxConcatMap.subscribe(FluxConcatMap.java:121) ~[reactor-core-3.1.8.RELEASE.jar:3.1.8.RELEASE] at reactor.core.publisher.MonoNext.subscribe(MonoNext.java:40) ~[reactor-core-3.1.8.RELEASE.jar:3.1.8.RELEASE] at reactor.core.publisher.MonoSwitchIfEmpty.subscribe(MonoSwitchIfEmpty.java:44) ~[reactor-core-3.1.8.RELEASE.jar:3.1.8.RELEASE] at reactor.core.publisher.MonoFlatMap.subscribe(MonoFlatMap.java:60) ~[reactor-core-3.1.8.RELEASE.jar:3.1.8.RELEASE] at reactor.core.publisher.MonoFlatMap.subscribe(MonoFlatMap.java:60) ~[reactor-core-3.1.8.RELEASE.jar:3.1.8.RELEASE] at reactor.core.publisher.MonoDefer.subscribe(MonoDefer.java:53) ~[reactor-core-3.1.8.RELEASE.jar:3.1.8.RELEASE] at reactor.core.publisher.MonoPeekTerminal.subscribe(MonoPeekTerminal.java:61) ~[reactor-core-3.1.8.RELEASE.jar:3.1.8.RELEASE] at reactor.core.publisher.MonoFlatMap$FlatMapMain.onNext(MonoFlatMap.java:150) ~[reactor-core-3.1.8.RELEASE.jar:3.1.8.RELEASE] at reactor.core.publisher.Operators$MonoSubscriber.complete(Operators.java:1083) ~[reactor-core-3.1.8.RELEASE.jar:3.1.8.RELEASE] at reactor.core.publisher.MonoZip$ZipCoordinator.signal(MonoZip.java:247) ~[reactor-core-3.1.8.RELEASE.jar:3.1.8.RELEASE] at reactor.core.publisher.MonoZip$ZipInner.onNext(MonoZip.java:329) ~[reactor-core-3.1.8.RELEASE.jar:3.1.8.RELEASE] at reactor.core.publisher.Operators$ScalarSubscription.request(Operators.java:1640) ~[reactor-core-3.1.8.RELEASE.jar:3.1.8.RELEASE] at reactor.core.publisher.MonoZip$ZipInner.onSubscribe(MonoZip.java:318) ~[reactor-core-3.1.8.RELEASE.jar:3.1.8.RELEASE] at reactor.core.publisher.MonoJust.subscribe(MonoJust.java:54) ~[reactor-core-3.1.8.RELEASE.jar:3.1.8.RELEASE] at reactor.core.publisher.Mono.subscribe(Mono.java:3080) ~[reactor-core-3.1.8.RELEASE.jar:3.1.8.RELEASE] at reactor.core.publisher.MonoZip.subscribe(MonoZip.java:128) ~[reactor-core-3.1.8.RELEASE.jar:3.1.8.RELEASE] at reactor.core.publisher.MonoFlatMap.subscribe(MonoFlatMap.java:60) ~[reactor-core-3.1.8.RELEASE.jar:3.1.8.RELEASE] at reactor.core.publisher.MonoDefer.subscribe(MonoDefer.java:53) ~[reactor-core-3.1.8.RELEASE.jar:3.1.8.RELEASE] at reactor.core.publisher.MonoDefer.subscribe(MonoDefer.java:53) ~[reactor-core-3.1.8.RELEASE.jar:3.1.8.RELEASE] at reactor.core.publisher.MonoPeekTerminal.subscribe(MonoPeekTerminal.java:61) ~[reactor-core-3.1.8.RELEASE.jar:3.1.8.RELEASE] at reactor.core.publisher.MonoPeekFuseable.subscribe(MonoPeekFuseable.java:74) ~[reactor-core-3.1.8.RELEASE.jar:3.1.8.RELEASE] at reactor.core.publisher.MonoDefer.subscribe(MonoDefer.java:53) ~[reactor-core-3.1.8.RELEASE.jar:3.1.8.RELEASE] at reactor.core.publisher.MonoDefer.subscribe(MonoDefer.java:53) ~[reactor-core-3.1.8.RELEASE.jar:3.1.8.RELEASE] at reactor.core.publisher.MonoOnErrorResume.subscribe(MonoOnErrorResume.java:44) ~[reactor-core-3.1.8.RELEASE.jar:3.1.8.RELEASE] at reactor.core.publisher.MonoOnErrorResume.subscribe(MonoOnErrorResume.java:44) ~[reactor-core-3.1.8.RELEASE.jar:3.1.8.RELEASE] at reactor.core.publisher.MonoOnErrorResume.subscribe(MonoOnErrorResume.java:44) ~[reactor-core-3.1.8.RELEASE.jar:3.1.8.RELEASE] at reactor.core.publisher.Mono.subscribe(Mono.java:3080) ~[reactor-core-3.1.8.RELEASE.jar:3.1.8.RELEASE] at reactor.core.publisher.MonoIgnoreThen$ThenIgnoreMain.drain(MonoIgnoreThen.java:172) ~[reactor-core-3.1.8.RELEASE.jar:3.1.8.RELEASE] at reactor.core.publisher.MonoIgnoreThen.subscribe(MonoIgnoreThen.java:56) ~[reactor-core-3.1.8.RELEASE.jar:3.1.8.RELEASE] at reactor.core.publisher.MonoPeekFuseable.subscribe(MonoPeekFuseable.java:70) ~[reactor-core-3.1.8.RELEASE.jar:3.1.8.RELEASE] at reactor.core.publisher.MonoPeekTerminal.subscribe(MonoPeekTerminal.java:61) ~[reactor-core-3.1.8.RELEASE.jar:3.1.8.RELEASE] at reactor.ipc.netty.channel.ChannelOperations.applyHandler(ChannelOperations.java:380) ~[reactor-netty-0.7.8.RELEASE.jar:0.7.8.RELEASE] at reactor.ipc.netty.http.server.HttpServerOperations.onHandlerStart(HttpServerOperations.java:398) ~[reactor-netty-0.7.8.RELEASE.jar:0.7.8.RELEASE] at io.netty.util.concurrent.AbstractEventExecutor.safeExecute$$$capture(AbstractEventExecutor.java:163) ~[netty-common-4.1.27.Final.jar:4.1.27.Final] at io.netty.util.concurrent.AbstractEventExecutor.safeExecute(AbstractEventExecutor.java) ~[netty-common-4.1.27.Final.jar:4.1.27.Final] at io.netty.util.concurrent.SingleThreadEventExecutor.runAllTasks(SingleThreadEventExecutor.java:404) ~[netty-common-4.1.27.Final.jar:4.1.27.Final] at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:464) ~[netty-transport-4.1.27.Final.jar:4.1.27.Final] at io.netty.util.concurrent.SingleThreadEventExecutor$5.run(SingleThreadEventExecutor.java:884) ~[netty-common-4.1.27.Final.jar:4.1.27.Final] at java.lang.Thread.run(Thread.java:745) ~[na:1.8.0_77]
可以看到里面调用的是ribbon的loadbalancer,因此我们可以直接看gateway里的全局过滤器接口GlobalFilter,找这个接口的所有实现类:
找到LoadBalancerClientFilter,可以看到这个filter调用了loadBalancer.reconstructURI(new DelegatingServiceInstance(instance, overrideScheme), uri),并重新写入到GATEWAY_REQUEST_URL_ATTR中。
在继续往下找(代码不难),最终会看到这个filter是执行了RibbonUtils的一个私有方法upgradeConnection,功能是:如果调用的服务要求安全的,就判断原始uri的scheme是否是http,是则转化为https,否则就按照原始请求的。
那么我们的要求是gateway为https,转发后的uri依然保持http不变,与上功能相反,因此我们可以仿造LoadBalancerClientFilter,并重写RibbonUtils的upgradeConnection方法,调整filter的执行顺序,由于不是升级连接,因此重命名了一下,代码如下:
/** * @author zhangqiuyang * Created on 2018/6/9. */
@Configuration
public class HttpsReconstructFilter implements GlobalFilter, Ordered {
private static final int HTTPS_RECONSTRUCT_FILTER_ORDER = 10101; /** * Process the Web request and (optionally) delegate to the next * {@code WebFilter} through the given {@link GatewayFilterChain}. * * @param exchange the current server exchange * @param chain provides a way to delegate to the next filter * @return {@code Mono<Void>} to indicate when request processing is complete */ @Override public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) { URI url = exchange.getAttribute(GATEWAY_REQUEST_URL_ATTR); if (url != null && "https".equals(url.getScheme())) { exchange.getAttributes().put(GATEWAY_REQUEST_URL_ATTR, reconstructURIConnection(url)); } return chain.filter(exchange); } /** * 重构url,把https修改为http * * @param uri * @return */ private static URI reconstructURIConnection(URI uri) { UriComponentsBuilder uriComponentsBuilder = UriComponentsBuilder.fromUri(uri).scheme("http"); if (uri.getRawQuery() != null) { // When building the URI, UriComponentsBuilder verify the allowed characters and does not // support the '+' so we replace it for its equivalent '%20'. // See issue https://jira.spring.io/browse/SPR-10172 uriComponentsBuilder.replaceQuery(uri.getRawQuery().replace("+", "%20")); } return uriComponentsBuilder.build(true).toUri(); } /** * Get the order value of this object. * <p>Higher values are interpreted as lower priority. As a consequence, * the object with the lowest value has the highest priority (somewhat * analogous to Servlet {@code load-on-startup} values). * <p>Same order values will result in arbitrary sort positions for the * affected objects. * * @return the order value * @see #HIGHEST_PRECEDENCE * @see #LOWEST_PRECEDENCE */ @Override public int getOrder() { return HTTPS_RECONSTRUCT_FILTER_ORDER; } }
改造就完成了,重启服务后访问,就可以看到效果。
生产环境证书:
许多生产环境的证书都是从godaddy(参考官方帮助)或者其他网站上申请的,得到的是一个gd_bundle开头的.crt证书和一个随机名称.crt证书,那这时如果要在spring boot上用这个证书,则需要进行一下转换,把证书转换成jks证书。
openssl pkcs12 -export -in 随机名称证书.crt -inkey 私钥.key -out ncdt.p12 -name tomcat -CAfile gd_bundle开头证书.crt -caname root -chain
执行完会要求输入密码,自己指定即可。
keytool -importkeystore -destkeystore ncdt.jks -srckeystore ncdt.p12 -srcstoretype PKCS12
执行完再次输入密码,生成.jks文件。
同时,spring boot的ssl配置应该为:
server: port: 443 ssl: enabled: true key-store-password: 密码 key-store: ncdt.jks
重启完成配置。