Spring Security 配置一致,为何一台 403 一台正常?

问题背景

最近接手了一套基于 RuoYi 框架的微服务系统,架构如下:

  • 后端 A:负责认证,生成 Token。
  • 后端 B:纯业务服务,按需求所有接口均不需鉴权。
  • 前端 :请求时会统一携带后端 A 生成的 Authorization Header。
    在维护后端 B 时,我发现了一段前任留下的代码:
java 复制代码
// 后端 B 的 Security 配置
.antMatchers("/**").anonymous()

这段代码显然是有问题的。因为前端请求一定会带 Token,触发认证后用户状态会变成"已认证",而 anonymous() 规则限制"只允许匿名访问",两者冲突必然导致 403 Forbidden

诡异的现象

我也确实遇到了 403 问题。但令人费解的是,同样的代码、同样的配置 ,在另一台服务器上部署运行时,接口竟然是正常 的(返回 200)。

这就很奇怪了:

  • 如果说配置错了,那应该两边都 403。
  • 为什么一边 403,一边却能正常跑通?

深入源码与逻辑推演

既然代码层面没差异,问题一定出在运行时数据上。

我们知道 RuoYi 框架的 JwtAuthenticationTokenFilter 处理逻辑分为两步:

  1. 解析 Token :使用 secret 解析 JWT 签名。
  2. 校验状态 :根据 Token 中的 ID 去 Redis 查询用户详细信息(login_tokens:xxx)。
    此时,用户的认证状态取决于这两个步骤的结果:
  • 如果解析成功且 Redis 有数据 -> 已认证用户
  • 如果解析失败或 Redis 无数据 -> 匿名用户
    结合 anonymous() 的规则(只允许匿名用户),我突然意识到:那台"正常"的服务器,是不是因为某种原因,导致认证失败了?

真相大白:Redis 配置差异

带着这个假设,我对比了两台服务器的配置文件(application.yml),终于发现了端倪:

1. 报 403 的服务器(共享 Redis)

  • 配置的 secret 与后端 A 一致
  • 配置的 Redis 地址与后端 A 一致(共享了 Redis 库)。
  • 结果 :Token 解析成功 -> Redis 中查到了用户数据 -> 认证成功(用户变为已认证) -> 撞上 anonymous() 规则 -> 403
    2. "正常"的服务器(独立 Redis)
  • 配置的 secret 与后端 A 一致
  • 配置的 Redis 地址是连接了不同的库,里面没有后端 A 生成的用户缓存。
  • 结果 :Token 解析成功 -> 去 Redis 查用户信息查无数据 -> 抛出异常/认证失败 -> 用户状态保持为匿名 -> 符合 anonymous() 规则 -> 200 正常
  • (这是一次"负负得正"!因为 Redis 配置隔离导致认证失败,反而误打误撞绕过了错误的 anonymous() 限制。)

解决方案与反思

原因找到了,解决起来就很简单了。

1. 修正配置

既然业务需求是"接口无需鉴权",那么必须将 anonymous() 修改为 permitAll(),这才是语义正确的配置:

java 复制代码
.antMatchers("/**").permitAll()

2. 思考

这次排查给我最大的启示是:环境差异往往会掩盖代码本身的错