Spring Security异常:UsernamePasswordAuthenticationToken突变为AnonymousAuthenticationToken
遇到这种情况很常见,我帮你梳理几个最可能的原因,你可以逐一排查:
未登录请求触发权限校验:Spring Security默认会为未携带有效认证凭证的请求自动创建
AnonymousAuthenticationToken,并且标记其authenticated为true——这是框架的设计,目的是让匿名请求也能通过统一的认证逻辑处理,避免代码中频繁判断Authentication是否为null。如果你的权限校验是在用户未登录的场景下触发的,就会拿到这个令牌。异步/跨线程场景下SecurityContext未传递:Spring Security的
SecurityContext默认存储在ThreadLocal中,如果你是在异步方法(比如用@Async标记的方法)或者新启动的子线程里调用PermissionEvaluator,而没有配置SecurityContext的传递机制,子线程的SecurityContext会是空的,框架就会自动生成AnonymousAuthenticationToken来填充。权限校验时机早于认证过滤器执行:Spring Security的用户认证是通过过滤器链完成的,如果你的权限校验逻辑(比如在自定义过滤器中提前执行,或者在Controller方法里提前调用)在
UsernamePasswordAuthenticationFilter这类认证过滤器把用户的UsernamePasswordAuthenticationToken存入SecurityContext之前就触发了,此时SecurityContext中还没有有效的用户认证信息,就会拿到匿名令牌。请求URL被配置为permitAll但触发了权限校验:如果触发权限校验的请求对应的URL在Spring Security配置中被设置为
permitAll(),框架会跳过该请求的认证流程,直接允许访问,但此时SecurityContext中依然是AnonymousAuthenticationToken。如果在这个请求的处理逻辑中调用了PermissionEvaluator,自然会拿到匿名令牌。
排查建议
你可以通过以下步骤定位具体原因:
- 检查触发权限校验的请求是否是用户未登录时发起的;
- 确认权限校验的代码是否运行在异步线程中;
- 查看Spring Security过滤器链的执行顺序,确保认证过滤器在权限校验逻辑之前执行;
- 核对请求URL的Security配置,是否被设置为
permitAll但又触发了权限校验。
内容的提问来源于stack exchange,提问作者daniel sp

