SecurityContext 生命周期
当前运行时 SecurityContext 已经形成了明确的请求级生命周期边界,这也是框架安全文档里最需要被稳定说明的一部分。
核心原则
当前实现遵循两个约束:
SecurityContext是线程级上下文- 安全主体只能在鉴权成功后由安全链路内部设置
通用 Filter 不负责从请求中直接注入主体,它只负责在请求结束后统一清理。
请求进入时发生什么
HTTP 请求进入后,FilterConfiguration 会先注册公共 Filter,其中 SecurityContextFilter 会包裹整个请求链路。
但它并不会设置主体,只会在 finally 中执行:
SecurityContext.clear()
主体是谁来设置的
当前真正负责认证与主体写入的是 DefaultSecurityInterceptor。
它会在 preHandle() 中按顺序尝试:
- 缓存恢复
- Anonymous 认证
- JWT 认证
- 扩展键认证
- OAuth2 认证
一旦鉴权成功,才会把当前用户、租户和密码写入 SecurityContext。
为什么不能在 Filter 中设置主体
这是当前框架的硬边界:
- Filter 处于通用请求入口层
- 它不应该相信前端可以直接声明的主体信息
- 主体只能来自鉴权成功后的内部链路
因此 SecurityContextFilter 的职责被严格限制为清理,而不是注入。
请求结束时发生什么
无论请求成功还是失败,SecurityContextFilter 都会在 finally 中清理上下文,避免:
- 线程复用导致主体串用
- 异常请求遗留脏上下文
- 后续请求误读上一请求的用户信息
异步任务如何传播上下文
如果运行时异步任务需要继承当前主体,应显式使用:
SecurityContextRunnable.wrap(...)
它会在异步任务进入时恢复必要上下文,并在任务完成后再次清理。
使用建议
- 控制器和服务层只读取
SecurityContext - 不要在业务代码中自行拼装伪主体塞入
SecurityContext - 不要在通用 Filter 中解析 header 后直接写入上下文
- 如需扩展认证链路,应在 Interceptor / 安全链路内部完成