Spring Security 和 JWT 的关系可以用一句话概括
- Spring Security:是你 Java 后端里“谁能访问什么接口”的门卫系统(认证 + 授权)。
- JWT:是一种“证明你是谁”的通行证格式(token),常用于前后端分离、无 session 的登录态。
下面按“前后端怎么调用”的顺序,把整套链路讲清楚,并给一个最小可用例子。
1. 先分清两个概念:认证 vs 授权
- 认证(Authentication):你是谁?有没有登录?
例:用户名密码正确、token 是真的 → 认证通过。 - 授权(Authorization):你能干什么?有没有权限?
例:你是 ADMIN 才能访问/admin/**。
Spring Security 负责这两件事。JWT 通常只负责携带身份信息,让后端不需要保存 session。
2. “不用 JWT”的传统方式(理解对比)
传统网页登录:
- 前端发
POST /login(用户名密码) - 后端验证通过后,在服务器内存/Redis 保存 session
- 后端给浏览器一个
JSESSIONIDcookie - 之后每次请求浏览器自动带 cookie,后端用 session 查你是谁
缺点:服务器要保存登录状态(有状态),分布式扩容要共享 session。
3. “用 JWT”的方式(前后端分离最常见)
JWT 的核心是:登录态不存服务器,存到 token 里,由前端每次带回来。
3.1 调用流程(非常重要)
第一步:登录拿 token
前端:
POST /api/login,body 里带用户名密码
后端:
- 验证账号密码 OK
- 生成 JWT(里面写入 userId、username、roles、过期时间 exp)
- 返回给前端:
{ "token": "xxxxx.yyyyy.zzzzz" }
前端:
- 把 token 存在内存/本地存储(localStorage)或 cookie(看安全策略)
第二步:访问业务接口时带 token
前端请求业务接口时在请求头加:
Authorization: Bearer <token>
后端(Spring Security)每次请求都会经过一堆 Filter:
- 其中你写的 JWT Filter 会:
- 从请求头取 token
- 验证签名是否正确、是否过期
- 解析出用户信息和角色
- 把“当前用户是谁”塞进 Spring Security 的上下文(SecurityContext)
接着 Spring Security 才能做授权判断:
- 例如访问
/admin/**需要ROLE_ADMIN - 如果 token 里没有这个角色 → 403 Access Denied
- 如果压根没 token 或 token 无效 → 401 Unauthorized
4. Spring Security 在 JWT 模式下各组件做什么
你可以把它想成一个“安检通道”:
-
JwtAuthenticationFilter(你们自己写的)
负责“验票(token)”:- 没票/假票/过期票:不要把用户放进上下文
- 真票:创建 Authentication(里面含用户名、角色)并放入上下文
-
AuthenticationEntryPoint(没登录/没带 token 时触发)
返回 401,例如:{"code":401,"msg":"Unauthorized"} -
AccessDeniedHandler(登录了但没权限时触发)
返回 403,例如:{"code":403,"msg":"Forbidden"} -
Authorization(authorizeHttpRequests)配置
决定哪些 URL 需要登录、哪些需要某角色。
5. 一个“最小例子”把它串起来(可照着理解)
假设我们有三个接口:
POST /api/login:放行(permitAll)GET /api/user/me:登录后可访问(authenticated)GET /api/admin/ping:必须 ADMIN 角色(hasRole("ADMIN"))
5.1 Security 配置(核心思想版)
@Bean
SecurityFilterChain security(HttpSecurity http) throws Exception {
return http
.csrf(csrf -> csrf.disable())
.sessionManagement(sm -> sm.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) // JWT 必配:无 session
.authorizeHttpRequests(auth -> auth
.requestMatchers("/api/login", "/error").permitAll()
.requestMatchers("/api/admin/**").hasRole("ADMIN")
.anyRequest().authenticated()
)
.exceptionHandling(eh -> eh
.authenticationEntryPoint((req, resp, ex) -> {
resp.setStatus(401);
})
.accessDeniedHandler((req, resp, ex) -> {
resp.setStatus(403);
})
)
.addFilterBefore(jwtFilter(), UsernamePasswordAuthenticationFilter.class)
.build();
}
5.2 JWT Filter 做的事(伪代码)
String auth = request.getHeader("Authorization"); if (auth != null && auth.startsWith("Bearer ")) { String token = auth.substring(7); // 1) 校验 token(签名、过期) // 2) 解析 username/roles // 3) new Authentication(...) 放到 SecurityContext } filterChain.doFilter(request, response);
5.3 前端调用示例
登录
http
POST /api/login Content-Type: application/json {"username":"alice","password":"123456"}
返回:
json
{"token":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...."}
带 token 访问
http
GET /api/user/me Authorization: Bearer eyJhbGciOi...
- token 有效 → 200
- token 缺失/无效 → 401
- token 有效但访问 admin 接口无 admin 角色 → 403

更多推荐


所有评论(0)