尧图网络 高端网站定制 · 原创设计
免费咨询热线
400-888-6620
免费获取方案
Spring Gateway与Sa-Token实现微服务无感鉴权实战
1. 为什么需要无感鉴权在微服务架构中鉴权一直是个让人头疼的问题。传统的做法是在每个服务里都写一遍鉴权逻辑这不仅重复劳动还容易出错。更糟的是当鉴权逻辑需要调整时你得在所有服务里都改一遍这简直就是维护的噩梦。我去年接手的一个电商项目就遇到了这种情况。最初我们用的是简单的JWT方案每个微服务都要验证token。结果随着业务扩展权限体系变得越来越复杂每次调整权限策略都得发版十几个服务运维同学都快疯了。后来我们引入了Spring Gateway Sa-Token的组合实现了真正的无感鉴权。这里的无感有两层含义对前端开发者无感 - 不需要在每次请求时手动处理token对业务服务无感 - 业务代码完全不用关心鉴权逻辑2. 技术选型为什么是Spring Gateway Sa-Token2.1 Spring Gateway的优势Spring Gateway作为Spring Cloud官方推荐的API网关相比Zuul和Nginx有几个不可替代的优势基于Spring生态与Spring Boot无缝集成支持异步非阻塞模型性能更好内置丰富的断言(Predicate)和过滤器(Filter)机制可以方便地扩展自定义逻辑特别是在鉴权场景下Gateway的GlobalFilter机制可以完美实现统一的鉴权拦截。2.2 Sa-Token的独特价值你可能听说过Shiro和Spring Security那为什么选择Sa-Token呢经过实际项目验证我发现Sa-Token有几个杀手级特性注解鉴权只需要一个SaCheckLogin就能完成方法级鉴权踢人下线内置会话管理可以强制指定用户下线多端登录轻松处理同一账号在PC、APP等多端同时登录分布式会话天然支持Redis分布式存储会话最重要的是Sa-Token的学习曲线非常平缓。相比Spring Security那复杂的配置Sa-Token几乎可以开箱即用。3. 实战搭建无感鉴权系统3.1 基础环境准备首先确保你的开发环境满足JDK 1.8Maven 3.6Redis 5.0用于会话存储Spring Boot 2.7.xSpring Cloud 2021.x在pom.xml中添加必要依赖!-- Spring Gateway -- dependency groupIdorg.springframework.cloud/groupId artifactIdspring-cloud-starter-gateway/artifactId /dependency !-- Sa-Token -- dependency groupIdcn.dev33/groupId artifactIdsa-token-spring-boot-starter/artifactId version1.34.0/version /dependency !-- Sa-Token整合Redis -- dependency groupIdcn.dev33/groupId artifactIdsa-token-dao-redis/artifactId version1.34.0/version /dependency3.2 网关鉴权过滤器实现核心逻辑在自定义的GlobalFilter中Component public class AuthFilter implements GlobalFilter, Ordered { Override public MonoVoid filter(ServerWebExchange exchange, GatewayFilterChain chain) { ServerHttpRequest request exchange.getRequest(); String path request.getURI().getPath(); // 1. 放行登录接口 if(path.contains(/auth/login)) { return chain.filter(exchange); } // 2. 获取token String token request.getHeaders().getFirst(Authorization); if(StringUtils.isEmpty(token)) { return unauthorizedResponse(exchange, 缺少token); } // 3. 校验token try { StpUtil.checkLoginByToken(token); } catch (NotLoginException e) { return unauthorizedResponse(exchange, token无效); } // 4. 将用户ID放入header传递给下游服务 String userId StpUtil.getLoginIdByToken(token).toString(); ServerHttpRequest newRequest request.mutate() .header(X-User-Id, userId) .build(); return chain.filter(exchange.mutate().request(newRequest).build()); } private MonoVoid unauthorizedResponse(ServerWebExchange exchange, String msg) { ServerHttpResponse response exchange.getResponse(); response.setStatusCode(HttpStatus.UNAUTHORIZED); response.getHeaders().add(Content-Type, application/json); MapString, Object result new HashMap(); result.put(code, 401); result.put(message, msg); DataBuffer buffer response.bufferFactory() .wrap(JSON.toJSONString(result).getBytes()); return response.writeWith(Mono.just(buffer)); } Override public int getOrder() { return -100; } }3.3 业务服务中的权限控制在业务服务中你只需要添加注解就能实现方法级权限控制RestController RequestMapping(/order) public class OrderController { SaCheckLogin GetMapping(/list) public Result getOrderList() { // 自动从header获取用户ID String userId StpUtil.getLoginIdAsString(); // 业务逻辑... } SaCheckRole(admin) PostMapping(/cancel) public Result cancelOrder(String orderId) { // 只有admin角色可以调用 } }4. 关键问题与解决方案4.1 跨服务用户信息传递在微服务架构中用户信息需要在服务间传递。我们的方案是网关验证token后将用户ID放入header通过Feign拦截器自动传递header业务服务从header获取用户信息Feign拦截器实现Configuration public class FeignConfig { Bean public RequestInterceptor requestInterceptor() { return template - { String userId StpUtil.getLoginIdDefaultNull(); if(userId ! null) { template.header(X-User-Id, userId); } }; } }4.2 权限数据缓存策略权限数据需要频繁读取我们采用多级缓存本地Caffeine缓存有效期5分钟Redis缓存有效期30分钟数据库存储缓存更新采用发布订阅模式当权限变更时通知所有节点清除缓存。4.3 网关性能优化在高并发场景下网关可能成为瓶颈。我们做了以下优化启用响应式编程避免阻塞IO对鉴权结果进行短期缓存10秒使用Hystrix进行熔断保护对频繁访问的接口路径进行特殊处理5. 生产环境踩坑记录5.1 Cookie的SameSite问题当你的前端和后端不在同一个域名下时浏览器可能会阻止Cookie的传递。解决方案显式设置SameSiteNone必须同时设置Secure属性HTTPS或者改用header传递tokenGateway中的配置示例Bean public WebFilter cookieSameSiteFilter() { return (exchange, chain) - { exchange.getResponse() .getHeaders() .add(Set-Cookie, SameSiteNone; Secure); return chain.filter(exchange); }; }5.2 长连接超时问题网关和后端服务的HTTP连接默认有超时限制。我们遇到过因为后端处理时间过长导致网关超时的情况。解决方案spring: cloud: gateway: httpclient: pool: max-idle-time: 60s httpclient: response-timeout: 30s connect-timeout: 5s5.3 文件上传的特殊处理文件上传请求的content-type是multipart/form-data这种请求的body只能读取一次。解决方案// 在过滤器中先判断是否是文件上传 if(isFileUploadRequest(request)) { return chain.filter(exchange); } // 其他请求正常处理鉴权逻辑6. 扩展功能实现6.1 接口访问频率限制防止恶意刷接口SaCheckLogin SaCheckFrequency(value5, time60) // 60秒内最多5次 GetMapping(/detail) public Result getDetail(String id) { //... }6.2 多端登录管理允许同一个账号在PC和APP同时登录但限制同一端只能一个设备在线// 登录时指定设备类型 StpUtil.login(userId, new SaLoginModel() .setDevice(APP) // 设备标识 .setIsLastingCookie(true));6.3 动态权限控制从数据库加载权限规则Component public class DynamicAuthInterceptor implements SaInterceptor { Override public boolean preHandle(Object handler) { // 从数据库查询当前接口需要的权限 String permission getRequiredPermission(handler); return StpUtil.hasPermission(permission); } }7. 监控与运维7.1 登录日志记录记录所有登录登出事件EventListener public void onLogin(StpLoginEvent event) { log.info(用户{}登录, token值:{}, 设备:{}, event.getLoginId(), event.getTokenValue(), event.getLoginModel().getDevice()); }7.2 实时会话查询管理员可以查看当前在线用户RestController RequestMapping(/admin/session) public class SessionController { GetMapping(/list) public Result getOnlineUsers() { return Result.success(StpUtil.searchSessionPage( , 1, 20, true)); } }7.3 健康检查端点添加专门的健康检查接口management: endpoints: web: exposure: include: health,info,metrics endpoint: health: show-details: always这套方案在我们多个生产环境中稳定运行超过一年日均处理请求量超过500万。最大的收获是业务开发完全从鉴权逻辑中解放出来只需要关注业务代码。当安全策略需要调整时也只需要修改网关一处即可生效。
RELATED

相关推荐

KBEngine客户端集成实战:Unity/UE4/OGRE网络游戏开发指南

KBEngine客户端集成实战:Unity/UE4/OGRE网络游戏开发指南

1. 项目概述:为什么选择KBEngine进行客户端集成?如果你正在用Unity3D、UE4或者OGRE开发一款网络游戏,并且已经受够了从零搭建服务端、设计协议、处理同步和状态管理的繁琐,那么KBEngine这个开源游戏服务端引擎,很可能就…

📅 2026/7/21 11:02:32
作业帮多端安装与使用全攻略

作业帮多端安装与使用全攻略

1. 作业帮多端下载安装全攻略刚给上初中的侄子装完作业帮,发现这个学习工具这几年功能越来越强大了。作为一款覆盖K12全学科的学习辅助应用,作业帮已经实现了从手机到平板再到电脑的全终端覆盖。不过很多家长在下载安装时还是会遇到各种问题,…

📅 2026/7/21 3:02:54
Windows平台Python开发环境配置与优化指南

Windows平台Python开发环境配置与优化指南

1. Windows平台Python开发环境搭建指南 作为在Windows平台上进行Python开发的起点,环境配置的完整性和正确性直接影响后续开发效率。不同于Linux/macOS系统,Windows需要特别注意路径管理、权限控制和工具链整合。 1.1 Python解释器安装方案对比 在Wind…

📅 2026/7/21 8:19:10
MORE NEWS

更多资讯

📰

Turso 异步 I/O 模型深入解析:协作式让出、显式状态机与 CompletionGroup 实践

Turso 异步 I/O 模型深入解析:协作式让出、显式状态机与 CompletionGroup 实践 【免费下载链接】turso A SQL database in Rust: SQLite-compatible, now also speaking Postgres (experimental). The LLVM of databases. 项目地址: https://gitcode.com/GitHub_T…

📰

界面开发1.0实战:从零搭建完整前端项目的经验与坑位复盘

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

📰

企业微信API与RPA实现CRM自动化对接实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

📰

spaCy 如何用 spacy benchmark speed 测量流水线吞吐量?

spaCy 如何用 spacy benchmark speed 测量流水线吞吐量? 【免费下载链接】spaCy 💫 Industrial-strength Natural Language Processing (NLP) in Python 项目地址: https://gitcode.com/GitHub_Trending/sp/spaCy 如果你已经训练或下载好了一个 s…

📰

树莓派Pico实战:电位器控制LED的ADC与PWM应用

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

📰

OpenClaw+优云智算+Coding Plan:实现从灵感到发布的全流程自动化

最近我把自己的内容生产流程彻底重做了一遍:从一个模糊的想法冒出来,到写成初稿,再到生成页面、推到线上,中途我不需要手动打开编辑器、不需要在不同平台之间来回复制粘贴。靠的是 OpenClaw 这个开源 AI Agent,配上优云…

TODAY

今日更新

THIS WEEK

本周精选

THIS MONTH

本月热门

读完文章,想聊聊您的网站?

告诉我们您的行业与需求,资深顾问一对一梳理方案与报价,全程免费。

📞 💬