一句话先定调:Spring Security 到底在干嘛?
作为一个小白,我们只需要知道: Spring Security 就是一个“站在门口的保安系统”。
每一个 HTTP 请求进来,它都会问三件事:
1. 你是谁? → Authentication(身份认证)
2. 你能干什么? → Authorization(权限校验)
3. 我现在记住你了吗? → SecurityContext(身份状态)
一、整体架构鸟瞰(先有全局地图)
所有 Spring Security 的工作,都发生在 Controller 之前
流程长这样:
浏览器发请求
↓
Spring Security 过滤器链(大门口)
↓
身份认证(你是谁)
↓
权限校验(你能干啥)
↓
Controller / Service(业务代码)
二、Spring Security 本质是什么?
很多同学学习 Spring Security 的时候,都会误会:
Spring Security = 注解+配置类
其真正的本体是一整条安全过滤器链,每个过滤器只干一条小事,但连起来就是完整安全系统
三、请求是怎么进 Spring Security 的?
1. Tomcat 和 Spring 本来是两个世界
Tomcat 只认识:
- Servlet
- Filter
- Listener
它根本不知道什么是 Spring Bean。
而 Spring Security 的过滤器,全部是 Spring 管理的 Bean。
那问题来了:
Tomcat 怎么调用 Spring 的过滤器?
2. DelegatingFilterProxy(桥梁)
DelegatingFilterProxy 的结构是这样的:
Tomcat
└── DelegatingFilterProxy
└── Spring 管理的 FilterChainProxy
└── 一堆 Security Filter
你可以把它理解成:
Tomcat 把请求交给一个“中介” 这个中介再把请求转交给 Spring Security
四、真正干活是的:FilterChainProxy
Spring Security 内部大概有 20 多个过滤器,按顺序执行。
你不用全部记住,只要知道几个关键的:
| 1 | SecurityContextHolderFilter | 创建“身份存储盒” |
| 2 | UsernamePasswordAuthenticationFilter | 表单登录 |
| 3 | BearerTokenAuthenticationFilter | JWT 登录 |
| 4 | AnonymousAuthenticationFilter | 匿名用户 |
| 5 | ExceptionTranslationFilter | 捕获安全异常 |
| 6 | AuthorizationFilter | 权限校验 |
你可以理解成:
请求就像坐传送带 每个过滤器检查一次 最后决定:放行 or 拦截
五、SecurityContext 是什么?(核心中的核心)
通俗点来讲:
SecurityContext 就是“当前请求的身份盒子”
它的目标只有一个:
在任何地方,不传参数,也能知道:
- 当前用户是谁
- 有什么权限
- 是否已登录
它的结构其实非常简单
接口:
public interface SecurityContext {
Authentication getAuthentication();
void setAuthentication(Authentication authentication);
}
实现类:
public class SecurityContextImpl implements SecurityContext {
private Authentication authentication;
}
SecurityContext 只是一个盒子 真正的“用户信息”在 Authentication 里
六、Authentication 是真正的“身份对象”
它包含四样核心东西:
public interface Authentication {
Object getPrincipal(); // 你是谁
Object getCredentials(); // 你的凭证(密码 / token)
Collection<?> getAuthorities(); // 你的权限
boolean isAuthenticated(); // 是否登录成功
}
可以理解为:
| principal | 当前用户 |
| credentials | 登录凭证 |
| authorities | 权限列表 |
| authenticated | 是否认证通过 |
七、principal 到底是什么?(小白最容易卡死的点)
情况一:登录前
Authentication auth = new UsernamePasswordAuthenticationToken("zhangsan", "123456");
这时候:
principal = 用户名
credentials = 密码
authenticated = false
情况二:登录成功
principal = UserDetails对象
credentials = null
authenticated = true
为什么密码是 null?
因为密码已经用完了 再存着是安全隐患
情况三:JWT
你自己定义一个用户对象(LoginUser):
principal = LoginUser
authenticated = true
八、用户信息到底存哪了?
答案是这句:
SecurityContextHolder.getContext().setAuthentication(authentication);
用户信息不在 Controller,不在 Service, 也不在 SecurityContextHolder 这个类里, 而是存在“当前请求线程”中。
很多人看到名字会误会:
SecurityContextHolder = 保存 SecurityContext 的地方
其实不是。
来看它最核心的一行代码:
private static final ThreadLocal<SecurityContext> contextHolder
= new ThreadLocal<>();
这一行才是真相。
关于ThreadLocal,你可以这样理解:
ThreadLocal 就是“给每个线程准备的一个小抽屉”
- 同一个 ThreadLocal
- 不同的线程
- 看到的是完全不同的数据
也就是说:
线程A → 自己的 SecurityContext
线程B → 自己的 SecurityContext
互不干扰。
九、为什么这样设计?
因为:
一个 HTTP 请求 = 一个线程
所以:
- Filter
- Controller
- Service
- Repository
全部跑在同一个线程里
这就意味着:
只要线程记住了用户 整个请求链路都能拿到用户
十、一次 JWT 请求完整流程(小白必看)
1. 请求进来
线程是空的:
ThreadLocal 里什么都没有
2. JWT Filter 解析 Token
LoginUser user = jwtService.parse(token);
3. 创建 Authentication
Authentication auth =
new UsernamePasswordAuthenticationToken(
user,
null,
authorities
);
4. 放进线程
SecurityContextHolder.getContext()
.setAuthentication(auth);
此时状态是:
当前线程
└── SecurityContext
└── Authentication
└── LoginUser
5. Controller 里直接拿
Authentication auth =
SecurityContextHolder.getContext().getAuthentication();
不需要传参数,不需要查 session。
十一、请求结束后发生什么?
Spring Security 最外层会执行:
SecurityContextHolder.clearContext();
也就是:
把线程里的用户信息清掉
为什么?
因为:
线程是复用的 不清就会“用户串号” 这是严重安全事故
十二、终极总结
Spring Security 没有“全局保存用户” 它只是让“当前请求的线程记住用户”
请求结束,线程清空 下一个请求,重新来过
以上就是笔者对于 Spring Security 知识的讲解,如有错误,欢迎广大同学指正!

