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

基于Reactive Netty的Spring Cloud Gateway受限URL双向认证实现

在Spring Cloud Gateway(Reactive Netty)实现路径级客户端证书认证方案

我来帮你梳理下替代Tomcat web.xml配置、实现路径差异化证书校验的最优方案——完全基于Reactive Netty,不用降级TLS版本,也不用折腾容易踩坑的TLS重协商。

先明确核心逻辑

你的需求本质是:全局TLS握手时允许客户端可选提供证书,再通过网关过滤器针对特定路径强制校验证书有效性,替代原Tomcat里的安全约束逻辑。

步骤1:全局TLS基础配置(设为want模式)

先在application.properties里配置SSL,把客户端认证设为want——这会让Reactive Netty接受客户端证书但不强制要求,后续我们在过滤器里做精细化校验,同时保留TLS1.3支持:

server.ssl.enabled=true
server.ssl.key-store=classpath:server-keystore.jks
server.ssl.key-store-password=your-keystore-pass
server.ssl.trust-store=classpath:server-truststore.jks
server.ssl.trust-store-password=your-truststore-pass
# 允许客户端提供证书,但不强制(关键配置)
server.ssl.client-auth=want
# 同时支持TLS1.2和TLS1.3,不用降级
server.ssl.enabled-protocols=TLSv1.2,TLSv1.3

步骤2:自定义网关过滤器实现路径级证书校验

创建一个GatewayFilterFactory,专门针对受限路径校验客户端证书的有效性:

import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import javax.net.ssl.SSLSession;
import java.security.cert.X509Certificate;

@Component
public class ClientCertValidationFilterFactory extends AbstractGatewayFilterFactory<ClientCertValidationFilterFactory.Config> {

    public ClientCertValidationFilterFactory() {
        super(Config.class);
    }

    @Override
    public GatewayFilter apply(Config config) {
        return (exchange, chain) -> {
            // 从请求的SSL信息中获取客户端证书
            SSLSession sslSession = exchange.getRequest().getSslInfo().getSslSession();
            X509Certificate[] clientCerts;
            try {
                clientCerts = (X509Certificate[]) sslSession.getPeerCertificates();
            } catch (Exception e) {
                // 客户端未提供证书,直接返回401
                return unauthorizedResponse(exchange);
            }

            // 校验证书有效性:这里可以扩展逻辑(比如检查信任链、有效期、CN白名单等)
            if (clientCerts == null || clientCerts.length == 0 || !isValidCert(clientCerts[0])) {
                return unauthorizedResponse(exchange);
            }

            // 证书有效,继续转发请求
            return chain.filter(exchange);
        };
    }

    private Mono<Void> unauthorizedResponse(ServerWebExchange exchange) {
        exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
        return exchange.getResponse().setComplete();
    }

    private boolean isValidCert(X509Certificate cert) {
        try {
            // 基础校验:检查证书有效期
            cert.checkValidity();
            // 可选:结合信任库验证证书是否被信任,或者校验证书主题/颁发者
            return true;
        } catch (Exception e) {
            return false;
        }
    }

    // 可以添加自定义配置参数,比如允许的证书主题前缀
    public static class Config {}
}

步骤3:路由配置区分公开/受限路径

在application.yml里配置网关路由,给受限路径绑定上面的过滤器,公开路径直接放行:

spring:
  cloud:
    gateway:
      routes:
        # 公开路径:无需证书校验
        - id: public-legacy-routes
          uri: lb://your-legacy-app
          predicates:
            - Path=/rest/public/**
          filters:
            - StripPrefix=1
        # 受限路径:强制证书校验
        - id: secure-legacy-routes
          uri: lb://your-legacy-app
          predicates:
            - Path=/rest/secure/**
          filters:
            - StripPrefix=1
            - ClientCertValidation

步骤4:清理遗留应用配置

现在网关已经接管了所有安全校验和HTTPS强制,你可以完全移除Tomcatweb.xml里的<security-constraint>、<login-config>、<security-role>相关配置,让遗留应用专注处理业务逻辑。

额外补充:全局HTTPS强制跳转

对应原web.xml里/*的CONFIDENTIAL约束,添加一个全局过滤器把所有HTTP请求重定向到HTTPS:

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

@Component
public class HttpsRedirectFilter implements GlobalFilter {
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        if (!exchange.getRequest().isSecure()) {
            String httpsUrl = "https://" + exchange.getRequest().getHeaders().getHost() + exchange.getRequest().getPath();
            exchange.getResponse().setStatusCode(HttpStatus.PERMANENT_REDIRECT);
            exchange.getResponse().getHeaders().setLocation(java.net.URI.create(httpsUrl));
            return exchange.getResponse().setComplete();
        }
        return chain.filter(exchange);
    }
}

为什么这个方案比TLS重协商更好

  1. 兼容性拉满:同时支持TLS1.2和TLS1.3,不用依赖浏览器的重协商支持
  2. 实现更简洁:不用手动操作Reactive Netty底层的SslHandler,避免各种诡异的会话状态问题
  3. 扩展性更强:可以在过滤器里灵活添加证书校验逻辑(比如角色映射、白名单),完全对齐原Tomcat的certificate角色逻辑
  4. 性能更优:全局TLS握手一次完成,没有重协商的额外开销

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.11 08:24:16