认证流程与 AuthenticationManager
认证把未认证凭证转换成带权限的 Authentication,再由过滤器保存成功结果或转换失败。
先给答案:Provider 链把“凭证类型差异”隔离在认证策略内部
Section titled “先给答案:Provider 链把“凭证类型差异”隔离在认证策略内部”AuthenticationManager 接收统一的认证请求,ProviderManager 再把它交给支持该 token 类型的 AuthenticationProvider。用户名密码、JWT、OAuth2、Remember-me 可以共享上层流程,却拥有不同的凭证解析和校验实现。
Provider 返回“不支持”、认证失败和认证成功的语义不同;异常处理器据此决定继续尝试、返回未认证还是拒绝。不要把“没有 provider 支持”和“凭证错误”混为一谈,它们对应不同的配置故障和安全响应。
credentials -> Authentication Filter -> AuthenticationManager -> ProviderManager -> AuthenticationProvider(s) -> SecurityContextRepositoryAbstractAuthenticationProcessingFilter#doFilter:web/.../AbstractAuthenticationProcessingFilter.java:237。- 认证 matcher:
web/.../AbstractAuthenticationProcessingFilter.java:328。 - manager 调用:
web/.../AbstractAuthenticationProcessingFilter.java:363。 - 成功处理:
web/.../AbstractAuthenticationProcessingFilter.java:392。 - 成功设置上下文:
web/.../AbstractAuthenticationProcessingFilter.java:396。 - 失败处理:
web/.../AbstractAuthenticationProcessingFilter.java:419。 - provider 遍历:
core/.../ProviderManager.java:166、:183。 - parent 回退:
core/.../ProviderManager.java:215。 - Basic 复用 manager:
web/.../BasicAuthenticationFilter.java:204。
为什么是 Provider 链
Section titled “为什么是 Provider 链”替代方案:一个 manager 写死密码、LDAP、JWT 等全部分支。
为什么不行:协议扩展会制造条件分支,应用也无法按需组合认证能力。
证据:ProviderManager 负责遍历,具体凭证逻辑下沉到 AuthenticationProvider。
| 场景 | 原因 | 规避 |
|---|---|---|
| provider 顺序错误 | 未区分不支持与失败 | 正确实现 supports |
| 成功后下一请求匿名 | 未保存 repository | 使用成功处理器 |
| 每次请求查用户 | 没有持久化策略 | 选择 Session 或 JWT |
Provider 链是能力路由模式,适合支付方式、编解码器和存储驱动选择。
面试锚点
- 三个 Authentication 抽象如何分工?
- 返回
null和抛异常的区别?- 成功身份在哪里保存?