用户名不存在和密码错怎么区分不开?禁用的用户用错误密码登录会得到什么异常?认证成功后密码还在吗?异步线程里为什么拿不到当前用户?这篇在 Spring Boot 4.0.8、Spring Security 7 上直接调用 AuthenticationManager 逐个实测。
AuthenticationManager = ProviderManager,providers = [DaoAuthenticationProvider] authenticate(alice, pw):成功,返回 UsernamePasswordAuthenticationToken;用时 572 ms;发布的事件 [AuthenticationSuccessEvent] authenticate(alice, wrong):失败 BadCredentialsException:Bad credentials;用时 286 ms;发布的事件 [AuthenticationFailureBadCredentialsEvent] authenticate(nobody, pw):失败 BadCredentialsException:Bad credentials;用时 283 ms;发布的事件 [AuthenticationFailureBadCredentialsEvent] authenticate(bob, pw):失败 DisabledException:User is disabled;用时 290 ms;发布的事件 [AuthenticationFailureDisabledEvent] authenticate(carol, pw):失败 LockedException:User account is locked;用时 286 ms;发布的事件 [AuthenticationFailureLockedEvent] authenticate(dave, pw):失败 CredentialsExpiredException:User credentials have expired;用时 288 ms;发布的事件 [AuthenticationFailureCredentialsExpiredEvent] authenticate(bob, wrong):失败 DisabledException:User is disabled;用时 294 ms;发布的事件 [AuthenticationFailureDisabledEvent]
密码错与用户不存在对外一样;禁用、锁定、凭据过期各有异常,且先于密码检查。
传入的对象:authenticated=false,credentials=pw;返回的对象:类型=UsernamePasswordAuthenticationToken,authenticated=true,principal 类型=User,credentials=null,authorities=[ROLE_USER, FactorGrantedAuthority [authority=FACTOR_PASSWORD, issuedAt=2026-10-20T04:00:00.894720Z]];返回的与传入的是同一个对象:false
新的 Authentication,credentials 被擦掉,多了 FACTOR_PASSWORD 权限。
hideUserNotFoundExceptions=false:authenticate(nobody, pw):失败 UsernameNotFoundException:user not found;用时 576 ms
hideUserNotFoundExceptions=false 会抛 UsernameNotFoundException,暴露用户是否存在。
用 alice 登录 GET /who → 请求线程=alice;普通线程池=null;新建线程=null;DelegatingSecurityContextExecutorService=alice 用 erin 登录 GET /who → 请求线程=erin;普通线程池=null;新建线程=null;DelegatingSecurityContextExecutorService=erin -- 同样的请求,启动时设置 spring.security.strategy=MODE_INHERITABLETHREADLOCAL: 用 alice 登录 GET /who → 请求线程=alice;普通线程池=alice;新建线程=alice;DelegatingSecurityContextExecutorService=alice 用 erin 登录 GET /who → 请求线程=erin;普通线程池=alice;新建线程=erin;DelegatingSecurityContextExecutorService=erin
默认 ThreadLocal 不带到其他线程;INHERITABLE 会让线程池里一直是旧用户;用 DelegatingSecurityContextExecutorService。