
1. 项目背景与核心价值微服务架构下鉴权一直是开发者头疼的问题。传统方案在每个服务中重复校验令牌既冗余又难以维护。最近在电商项目中我用Spring Cloud GatewaySa-Token组合实现了无侵入式鉴权效果出乎意料地好。这套方案的核心优势在于鉴权逻辑完全前置到网关层业务服务只需关注自身逻辑基于Sa-Token的会话管理天然支持分布式环境用户身份信息通过请求头自动传递服务间调用零改造支持动态路由与权限规则的灵活配置2. 技术选型解析2.1 为什么选择Spring Cloud Gateway相比Zuul等传统方案Gateway的优势非常明显基于Netty的异步非阻塞模型性能提升40%以上原生支持服务注册中心集成实测Nacos兼容性最佳谓词(Predicate)和过滤器(Filter)机制高度灵活与Spring生态无缝整合配置管理更便捷关键配置示例spring: cloud: gateway: routes: - id: user-service uri: lb://user-service predicates: - Path/api/user/** filters: - StripPrefix12.2 Sa-Token的独特优势对比Shiro和Spring Security零配置开箱即用学习曲线平缓分布式会话方案内置支持Redis/JWT等多种存储细粒度权限控制只需注解即可实现活跃的社区支持GitHub 5k stars核心依赖dependency groupIdcn.dev33/groupId artifactIdsa-token-spring-boot-starter/artifactId version1.34.0/version /dependency3. 完整实现方案3.1 网关层鉴权过滤器创建GlobalFilter实现无感鉴权public class AuthFilter implements GlobalFilter { Override public MonoVoid filter(ServerWebExchange exchange, GatewayFilterChain chain) { // 1. 获取请求路径 String path exchange.getRequest().getPath().toString(); // 2. 白名单校验 if(AuthWhiteList.contains(path)){ return chain.filter(exchange); } // 3. Token校验 String token exchange.getRequest().getHeaders().getFirst(Authorization); if(!StpUtil.isLogin(token)){ return unauthorizedResponse(exchange); } // 4. 传递用户信息 ServerHttpRequest newRequest exchange.getRequest().mutate() .header(X-User-Id, StpUtil.getLoginId(token)) .build(); return chain.filter(exchange.mutate().request(newRequest).build()); } }3.2 业务服务用户信息提取通过拦截器自动注入Configuration public class UserInfoInterceptor implements HandlerInterceptor { Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) { String userId request.getHeader(X-User-Id); UserContext.set(userId); return true; } Override public void afterCompletion(...) { UserContext.clear(); } }3.3 动态权限控制方案结合Sa-Token的路由鉴权// 在网关启动时加载权限规则 PostConstruct public void initPermission() { ListPermission permissions permissionService.listAll(); permissions.forEach(p - { StpInterface.addPermission(p.getPath(), p.getRoles()); }); }4. 性能优化实践4.1 缓存策略设计采用二级缓存提升校验效率本地Caffeine缓存有效期30秒Redis分布式缓存有效期5分钟缓存击穿防护public boolean checkPermission(String token, String path) { // 1. 本地缓存检查 String cacheKey perm: token : path; Boolean result localCache.getIfPresent(cacheKey); if(result ! null) return result; // 2. Redis检查 result redisTemplate.opsForValue().get(cacheKey); if(result ! null) { localCache.put(cacheKey, result); return result; } // 3. DB查询并回填 result realCheck(token, path); redisTemplate.opsForValue().set(cacheKey, result, 5, TimeUnit.MINUTES); localCache.put(cacheKey, result); return result; }4.2 流量控制配置网关层限流保护spring: cloud: gateway: routes: - id: order-service uri: lb://order-service predicates: - Path/api/order/** filters: - name: RequestRateLimiter args: redis-rate-limiter.replenishRate: 100 redis-rate-limiter.burstCapacity: 2005. 生产环境踩坑实录5.1 Cookie跨域问题解决方案Bean public WebFilter corsFilter() { return (exchange, chain) - { ServerHttpRequest request exchange.getRequest(); if (CorsUtils.isCorsRequest(request)) { ServerHttpResponse response exchange.getResponse(); HttpHeaders headers response.getHeaders(); headers.add(Access-Control-Allow-Origin, *); headers.add(Access-Control-Allow-Methods, *); headers.add(Access-Control-Max-Age, 3600); headers.add(Access-Control-Allow-Headers, *); headers.add(Access-Control-Allow-Credentials, true); if (request.getMethod() HttpMethod.OPTIONS) { response.setStatusCode(HttpStatus.OK); return Mono.empty(); } } return chain.filter(exchange); }; }5.2 灰度发布兼容方案通过Header版本号路由public class VersionRoutePredicateFactory extends AbstractRoutePredicateFactoryVersionRoutePredicateFactory.Config { Override public PredicateServerWebExchange apply(Config config) { return exchange - { String version exchange.getRequest() .getHeaders() .getFirst(X-API-Version); return config.getVersion().equals(version); }; } }6. 监控与日志方案6.1 全链路追踪实现集成SleuthZipkinspring: sleuth: sampler: probability: 1.0 zipkin: base-url: http://zipkin:9411 sender: type: web6.2 审计日志收集AOP记录关键操作Aspect Component public class AuditLogAspect { AfterReturning(pointcut annotation(auditLog), returning result) public void afterReturning(JoinPoint joinPoint, AuditLog auditLog, Object result) { String userId UserContext.get(); String operation auditLog.value(); logService.save(userId, operation, result); } }这套方案在日活百万级的系统中稳定运行超过6个月网关层平均延迟控制在15ms以内。最让我惊喜的是Sa-Token在分布式场景下的稳定性期间经历过三次Redis集群故障都未出现会话异常。