一.生成证书

证书本应是花钱买,客户端才能通过根CA仓库识别证书。

如果是自定义生成的证书,客户端访问时,会提示不安全的链接 是否继续访问,或者客户端可以将证书导入自己的本地CA仓库。

生成证书可以通过 openssl,或者jdk的工具keytool生成。

使用openssl,参考:https://www.jianshu.com/p/0e9ee7ed6c1d     ,先生成虚拟的CA,再生成证书,再用CA为证书签名

使用keytool:

keytool -genkeypair -alias client -keyalg RSA -keysize 2048 -storetype PKCS12 -keystore keystore.p12 -validity 3650

证书不同格式之间转换 参考:https://blog.csdn.net/qq_37049781/article/details/84837342

二.gateway配置

将证书放在 resources文件夹下

server:
  port: 9000
  ssl:
    enabled: true
    key-alias: client //证书别名
    key-store: classpath:keystore.p12  //证书位置
    key-store-password: 123456  //生成证书时的密码
    key-store-type: PKCS12  //证书类型

如果是JKS类型证书:

server:
  ssl:
    key-alias: spring
    enabled: true
    key-password: spring
    key-store: classpath:selfsigned.jks
    key-store-type: JKS
    key-store-provider: SUN
    key-store-password: spring

一般下游服务使用http,只有网关使用https,所以网关路由下游服务时,需要将https修改为http。以下是拦截器,在路由过滤器LoadBalancerClientFilter前 将请求修改。

这个参考《重新定义springcloud一书》,github地址:https://github.com/SpringCloud/spring-cloud-code/tree/master/ch18-4

import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.net.URI;


/**
 * 在LoadBalancerClientFilter执行之前将Https修改为Http
 * https://github.com/spring-cloud/spring-cloud-gateway/issues/378
 */
@Component
public class HttpsToHttpFilter implements GlobalFilter, Ordered {

    private static final int HTTPS_TO_HTTP_FILTER_ORDER = 10099;

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        URI originalUri = exchange.getRequest().getURI();
        ServerHttpRequest request = exchange.getRequest();
        ServerHttpRequest.Builder mutate = request.mutate();
        String forwardedUri = request.getURI().toString();
        if (forwardedUri != null && forwardedUri.startsWith("https")) {
            try {
                URI mutatedUri = new URI("http",
                        originalUri.getUserInfo(),
                        originalUri.getHost(),
                        originalUri.getPort(),
                        originalUri.getPath(),
                        originalUri.getQuery(),
                        originalUri.getFragment());
                mutate.uri(mutatedUri);
            } catch (Exception e) {
                throw new IllegalStateException(e.getMessage(), e);
            }
        }
        ServerHttpRequest build = mutate.build();
        return chain.filter(exchange.mutate().request(build).build());
    }

    /**
     * 由于LoadBalancerClientFilter的order是10100,
     * 要在LoadBalancerClientFilter执行之前将Https修改为Http,需要设置
     * order为10099
     * @return
     */
    @Override
    public int getOrder() {
        return HTTPS_TO_HTTP_FILTER_ORDER;
    }
}

三.问题

以上操作后,确实可以再浏览器以 https访问成功,但gateway报一个错误。

javax.net.ssl.SSLException: Received fatal alert: certificate_unknown

这个应该是客户端问题,客户端不信任这个证书,如果将证书添加到浏览器信任证书中,ie浏览器访问就不会报错,但谷歌浏览器还会。如果是正式证书应该不会有问题。

Logo

权威|前沿|技术|干货|国内首个API全生命周期开发者社区

更多推荐