Gateway 生产实战 📖 前置阅读:本文假设读者已掌握 Gateway 的 Route/Predicate/Filter 全操作。如果还不熟悉,建议先阅读前三篇:核心概念、Predicate 全解、Filter 全操作。
一、⚡ 路由和 Filter 都调通了——但你能上线吗? 开发环境一切正常——localhost 上 Gateway 跑得稳稳的。但上线之前你至少还要解决:
① 认证——用户登录后的 JWT Token 在网关统一校验 ② 限流——防止恶意刷接口——一个 IP 一秒最多 10 次 ③ 熔断——后端挂了——网关直接降级返回而不是把 500 抛给前端 ④ 跨域——前端从不同域名调网关——浏览器会拦截 ⑤ 监控——请求量、错误率、延迟——全部看不到就是盲飞 ⑥ TraceId——一个请求穿过网关到后端多个服务——怎么串联日志? 这一篇把以上每个问题都给出可直接使用的配置和代码。
二、🔐 JWT 鉴权——全局统一校验 2.1 为什么在网关做鉴权? 每个后端服务都自己解析 JWT——重复代码、分散维护、容易漏掉。在网关统一做——后端只信任网关传过来的 Header 就行了:
浏览器带 JWT → 网关解析 → Header 中放 userId + role → 后端直接用 2.2 完整的 JWT 鉴权 GlobalFilter @Component @Order(-100) public class JwtAuthGlobalFilter implements GlobalFilter { // 白名单——不需要 Token 的接口 private static final List<String> WHITE_LIST = List.of( "/api/public/login", "/api/public/register", "/api/public/health" ); // 从配置中心拿——这里简化为常量 private static final String SECRET_KEY = "your-256-bit-secret-key"; @Override public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) { String path = exchange.getRequest().getURI().getPath(); // ① 白名单放行 if (isWhiteListed(path)) { return chain.filter(exchange); } // ② 提取 Token String authHeader = exchange.getRequest() .getHeaders().getFirst(HttpHeaders.AUTHORIZATION); if (authHeader == null || !authHeader.startsWith("Bearer ")) { return unauthorized(exchange, "缺少认证 Token"); } String token = authHeader.substring(7); // ③ 解析 JWT try { Claims claims = Jwts.parser() .setSigningKey(SECRET_KEY) .parseClaimsJws(token) .getBody(); // 检查是否过期 if (claims.getExpiration().before(new Date())) { return unauthorized(exchange, "Token 已过期"); } // ④ 把用户信息写入请求头——后端直接用 ServerHttpRequest mutatedRequest = exchange.getRequest().mutate() .header("X-User-Id", String.valueOf(claims.get("userId"))) .header("X-User-Name", claims.get("userName", String.class)) .header("X-User-Role", claims.get("role", String.class)) .build(); // ⑤ 用修改后的 Request 继续 return chain.filter(exchange.mutate().request(mutatedRequest).build()); } catch (JwtException e) { return unauthorized(exchange, "Token 无效: " + e.getMessage()); } } private boolean isWhiteListed(String path) { return WHITE_LIST.stream().anyMatch(path::startsWith); } private Mono<Void> unauthorized(ServerWebExchange exchange, String message) { exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED); exchange.getResponse().getHeaders() .setContentType(MediaType.APPLICATION_JSON); // 返回 JSON 错误信息 byte[] body = ("{\"code\":401,\"message\":\"" + message + "\"}").getBytes(); DataBuffer buffer = exchange.getResponse() .bufferFactory().wrap(body); return exchange.getResponse().writeWith(Mono.just(buffer)); } } 2.3 后端如何信任网关 后端服务应该只信任从网关来的请求——加一个内部 Token 做服务间认证:
...