本文将介绍 Spring Security 配合 JWT 的使用。
一、JWT
具体请看:
前后端分离 JSON Web Token
二、核心要点及实现
1. 关闭 Session
(1) 说明
信息存储在用户处,服务端无需依靠 Session存储用户信息。这里的关闭 Session 是关闭 Spring Security 对 Session 的使用,而非关闭 Session。
(2) 实现
修改 Spring Security 配置类如下:
1 2 3 4 5 6 7 8 9 10
| @EnableWebSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { http .sessionManagement() .sessionCreationPolicy(SessionCreationPolicy.STATELESS); } }
|
2. 登录功能
(1) 说明
在这里,登录接口需要完成对登录请求的校验,生成 JWT 并返回。
设置登录状态(在安全上下文中放置 Authentication 对象)是没有必要的
此前之所以这样做,是为了登录状态保持。而这里我们已经禁用了 Session,不再需要登录状态保持,每一个请求都是独立的
(2) 实现
登录功能有两种实现方式:
- 自定义登录接口需要完成以下工作:
- 验证登录请求
- 若登录成功,
- 需要生成 Authentication 对象,放置到 Security 上下文中
- 需要生成 JWT,在请求响应中返回
- 修改
.formLogin().successHandler()
,在其中生成 JWT 并返回
自定义登录接口更加自由,并且可以减少诸如 “设置登录状态” 这样的多余操作,因此这里以自定义登录接口为例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| @RestController public class AccountController {
@PostMapping("/login") public String login(@RequestBody LoginParam loginParam) { return JWT; }
}
|
3. 登录状态保持
(1) 说明
在基于 Session 的身份认证方式中,登录状态保持分为以下两个部分:
在基于 JWT 的身份认证方式中,JWT 自身已经保存了用户信息,不再耗费服务器资源做登录状态保存。因此,在基于 Session 的身份认证方式中,只需要完成登录状态读取即可。
登录状态读取需要完成以下工作:
- 从请求中读取 JWT
- 校验 JWT
- 根据 JWT 创建 UsernamePasswordAuthenticationToken,设置登录状态(在安全上下文中放置 Authentication 对象)
(2) 实现
过滤器编写
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| public class JWTVerifyFilter extends GenericFilterBean {
@Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) throws IOException, ServletException { HttpServletRequest req = (HttpServletRequest) request; String jwtToken = req.getHeader("authorization"); if (!"/login".equals(req.getServletPath()) && jwtToken != null) { SecurityContextHolder.getContext().setAuthentication(···); } filterChain.doFilter(request, response); } }
|
过滤器配置
1 2 3 4 5 6 7 8 9
| @EnableWebSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { http .addFilterBefore(···, UsernamePasswordAuthenticationFilter.class) } }
|
参考