You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spring Cloud Gateway未转发客户端证书致请求失败问题咨询

嗨,我来帮你解决这个问题!你遇到的certificate_unknown问题确实是因为Spring Cloud Gateway默认不会自动将客户端证书转发给后端服务,但这不是缺失的功能,只需要通过正确的配置和自定义过滤器就能实现证书转发。下面是具体的解决方案:

一、先让Gateway接收并验证客户端证书

首先,网关本身需要配置SSL并启用客户端认证,这样才能接收到客户端发送的证书。在你的application.yml(或application.properties)中添加以下配置:

server:
  ssl:
    enabled: true
    # 网关自身的服务端证书
    key-store: classpath:gateway-server.jks
    key-store-password: your-server-cert-password
    # 信任库,需要包含客户端证书的CA证书,用于验证客户端证书的有效性
    trust-store: classpath:gateway-truststore.jks
    trust-store-password: your-truststore-password
    # 要求客户端必须提供证书
    client-auth: need

这里的trust-store必须包含签发客户端证书的CA,否则网关会直接拒绝未信任的客户端请求。

二、自定义过滤器转发客户端证书

接下来需要编写一个自定义的Gateway过滤器,从请求中提取客户端证书,并将其传递给后端服务。通常有两种传递方式:

方式1:通过请求头传递证书(推荐)

这种方式简单易实现,后端服务可以从请求头中提取证书并进行验证。编写过滤器代码:

@Component
public class ClientCertForwardFilter implements GatewayFilter, Ordered {

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        // 从请求的SSL信息中获取客户端证书
        X509Certificate[] clientCerts = exchange.getRequest().getSslInfo()
                .map(SslInfo::getPeerCertificates)
                .orElse(new X509Certificate[0]);

        if (clientCerts.length > 0) {
            try {
                // 将证书编码为Base64字符串,放到自定义请求头中
                String encodedCert = new String(Base64.getEncoder().encode(clientCerts[0].getEncoded()));
                ServerHttpRequest modifiedRequest = exchange.getRequest().mutate()
                        .header("X-Client-Certificate", encodedCert)
                        .build();
                return chain.filter(exchange.mutate().request(modifiedRequest).build());
            } catch (CertificateEncodingException e) {
                return Mono.error(new RuntimeException("Failed to encode client certificate", e));
            }
        }
        // 如果没有证书,直接拒绝请求(根据你的业务需求调整)
        return Mono.error(new ResponseStatusException(HttpStatus.UNAUTHORIZED, "Client certificate missing"));
    }

    @Override
    public int getOrder() {
        // 设置过滤器优先级,确保在路由转发前执行
        return -1;
    }
}

然后在你的路由配置中引用这个过滤器(如果是全局过滤器可以不用单独配置):

spring:
  cloud:
    gateway:
      routes:
        - id: backend-service-route
          uri: https://your-backend-service:port
          predicates:
            - Path=/backend/**
          filters:
            - name: ClientCertForwardFilter

方式2:SSL层面传递证书(复杂场景)

如果后端服务必须要求SSL层面的客户端认证(即必须在TLS握手阶段提供证书),这种方式会更复杂:因为网关需要作为客户端向后端发起请求时,使用原始客户端的证书。但这里有个核心问题:客户端的私钥不会传递给网关,所以通常这种场景下,更推荐调整后端服务支持从请求头验证证书(即方式1),或者让网关持有一个被后端信任的固定客户端证书。

三、后端服务适配证书验证

如果用方式1传递证书,后端服务需要从请求头中提取证书并进行验证。编写一个Spring Boot过滤器:

@Component
public class ClientCertValidationFilter extends OncePerRequestFilter {

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        String certHeader = request.getHeader("X-Client-Certificate");
        if (certHeader == null) {
            response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Client certificate not provided");
            return;
        }

        try {
            // 解码证书并验证
            byte[] certBytes = Base64.getDecoder().decode(certHeader);
            CertificateFactory certFactory = CertificateFactory.getInstance("X.509");
            X509Certificate clientCert = (X509Certificate) certFactory.generateCertificate(new ByteArrayInputStream(certBytes));
            
            // 这里添加你的验证逻辑:比如检查证书是否在信任库、是否过期、主题是否合法等
            // 示例:验证证书签名是否可信
            TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
            KeyStore trustStore = KeyStore.getInstance("JKS");
            trustStore.load(new FileInputStream("classpath:backend-truststore.jks"), "your-truststore-password".toCharArray());
            tmf.init(trustStore);
            for (TrustManager tm : tmf.getTrustManagers()) {
                ((X509TrustManager) tm).checkClientTrusted(new X509Certificate[]{clientCert}, "RSA");
            }
        } catch (Exception e) {
            response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Invalid client certificate: " + e.getMessage());
            return;
        }

        filterChain.doFilter(request, response);
    }
}
总结

Spring Cloud Gateway本身具备支持证书转发的能力,只是默认没有开启这个功能,因为不同场景的需求差异较大。通过上述配置和自定义过滤器,你就能实现客户端证书从网关到后端服务的转发,解决certificate_unknown的问题。

内容的提问来源于stack exchange,提问作者Dashy

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 08:52:35