JWT 解决的是凭证格式和签名验证,Spring Security 解决的是请求如何进入安全过滤链、身份如何放入安全上下文,以及接口怎样根据认证与权限做出决定。
在微服务认证中,两者需要配合,但不能把所有事情都归给 JWT。登录、令牌签发、网关校验、业务权限和会话撤销分别属于不同边界。
Spring Security 负责什么
Spring Security 提供一套认证与授权框架,主要关注:
- 请求经过哪些安全过滤器;
- 如何提取认证信息;
- 认证成功后怎样建立安全上下文;
- 未认证和权限不足怎样返回;
- 哪些路径公开,哪些路径受保护;
- 方法或接口怎样判断角色与权限。
它并不要求必须使用 JWT。JWT 只是其中一种身份凭证方案。
JWT 负责什么
JWT 可以携带一组声明,并通过签名防止内容被未授权修改。
Header
Payload
Signature常见声明包括签发者、主题、签发时间、过期时间和令牌类型。业务需要的用户标识或角色信息可以适量放入,但不能把密码、隐私数据和频繁变化的大量权限都塞进 Token。
签名只能证明令牌内容在持有密钥的一方签发后没有被篡改,不能证明持有者一定是本人,也不能自动处理撤销和账号状态变化。
登录发生在认证服务
登录接口通常由认证服务处理:
提交账号与凭据
-> Spring Security 认证流程
-> 查询用户与账号状态
-> 校验密码
-> 生成 Access Token
-> 生成 Refresh Token
-> 保存必要的服务端状态密码应使用合适的单向散列算法保存,不能与 JWT 签名密钥混为一谈。
认证失败时应返回统一但不过度详细的信息,避免暴露账号是否存在、密码策略或内部异常。
普通请求怎样经过网关
客户端访问业务接口时,通常携带 Access Token。
客户端
-> Gateway 安全过滤链
-> 提取 Bearer Token
-> 验证格式、签名和有效期
-> 检查必要状态
-> 清理客户端伪造身份头
-> 传递可信身份到下游网关可以完成基础身份验证和受保护路由判断,但不应该掌握所有业务数据权限。
例如“用户是否能编辑这个视频”需要结合视频所有者、审核状态和管理员权限,应该由内容服务根据业务数据判断。
Spring Security 过滤链的位置
在 Web 应用中,安全过滤器会在请求到达 Controller 前执行。
一个简化的 JWT 过滤器逻辑可以表示为:
String token = resolveToken(request);
if (token != null && tokenService.isValid(token)) {
Authentication authentication = tokenService.parseAuthentication(token);
SecurityContextHolder.getContext().setAuthentication(authentication);
}
filterChain.doFilter(request, response);这是通用示例,不是 HAOVP 的真实实现,也没有展示完整异常处理和安全配置。
过滤器需要避免:
- 对公开路径强制要求 Token;
- Token 无效后仍建立认证上下文;
- 在日志中打印完整 Token;
- 忘记清理或隔离请求安全上下文;
- 把业务权限判断塞进基础认证过滤器。
下游服务是否还要校验
如果所有外部请求都经过可信网关,下游可以接收网关传递的身份信息,但要防止服务被绕过网关直接访问。
可以考虑:
- 网络层只允许受控入口访问业务服务;
- 服务间请求使用内部身份或签名;
- 下游验证网关注入的身份头来源;
- 对敏感操作进行必要的二次校验;
- 业务服务始终校验具体资源权限。
“网关已经验过 Token”不能等同于“业务服务不需要授权”。认证回答是谁,授权回答能做什么。
Redis 在认证中的作用
JWT 可以离线验证签名,但项目仍可能需要 Redis 保存会话和撤销相关状态,例如:
- Refresh Token 标识或哈希;
- 登录会话状态;
- Token 黑名单;
- 用户令牌版本;
- 验证码;
- 失败次数和临时限制;
- 强制退出标记。
JWT:携带短期身份声明
Redis:维护需要即时变化的认证状态是否每次请求都查询 Redis,要看即时撤销要求、性能和复杂度。完全查询会增加状态依赖,完全不查询则无法立即响应部分账号变化。
账号状态变化如何生效
用户被禁用、修改密码或退出所有设备后,旧 Access Token 可能仍在有效期内。
常见处理方向包括:
- 缩短 Access Token 有效期;
- 撤销 Refresh Token,阻止续期;
- 使用用户令牌版本;
- 对敏感操作查询最新账号状态;
- 使用黑名单处理需要立即失效的令牌。
每种方式都有额外存储或查询成本,需要根据风险决定。
角色和权限放多少进 JWT
把角色放入 JWT 可以减少查询,但角色变化后旧 Token 中的信息不会立即更新。
我会区分:
- 稳定且粗粒度的身份信息;
- 频繁变化的业务权限;
- 与具体数据记录相关的资源权限。
前者可以适量放入 Token,后两者更适合由业务服务实时判断。管理操作尤其不能只依赖一个长期不变的角色声明。
错误边界要清楚
认证和授权失败需要区分:
未携带或无效凭证 -> 401
身份有效但没有权限 -> 403
业务状态不允许操作 -> 业务错误
下游服务不可用 -> 服务可用性错误如果所有情况都返回 401,客户端无法判断应该刷新凭证、重新登录还是提示无权限。
错误响应中可以带请求标识帮助排查,但不能返回签名密钥、完整 Token 和内部堆栈。
Security 配置需要显式检查
安全配置中容易遗漏:
- 健康检查和登录接口是否正确放行;
- 管理接口是否使用更严格规则;
- CSRF 是否根据实际认证方式处理;
- CORS 是否与网关配置一致;
- Session 是否无意中被创建;
- 异常处理器是否返回统一结构;
- 方法级权限是否真正启用;
- 测试环境是否错误地放宽全部接口。
配置能够启动不代表安全边界正确,需要使用不同身份和异常场景验证。
Spring Security 与 JWT 的协作方式,可以概括为:认证服务使用 Spring Security 完成登录和签发,JWT 承载短期身份,网关通过安全过滤链验证基础凭证,Redis维护需要即时变化的状态,业务服务判断具体权限。
这套分工减少了职责混杂,但仍要通过令牌失效、权限变化、绕过网关和异常响应等场景持续验证。安全不是某个依赖自动提供的结果,而是多层边界共同成立。