一、核心流程
- 引入依赖
- 创建JwtToken工具类
- 在SecurityConfig中配置使用successHandler自定义含JwtToken的响应数据
- 在SecurityConfig中使用addFilterBefore自定义JwtFilter过滤器
在第3步中,需要做一个登录验证成功时的拦截,拦截后生成JwtToken然后写入到响应内容里面,这样前端就能接收到通过/login请求获取到的JwtToken数据。于此同时,也需要将拦截时获取到的authentication写入到SecurityContextHolder中,为下次前端通过JwtToken验证做准备。
在第4步中,通过增加JwtFilter过滤器来验证身份,允许前端通过携带正确且未过期的JwtToken进行身份验证,验证时需要同样需要通过LoadUserByUsername方法获取UserDetails对象(Principal对象),然后创建一个UsernamePasswordAuthenticationToken对象给SecurityContextHolder验证,如果和步骤2中写入的authentication一致,即验证成功,正确获取到用户权限。
二、代码实现
2.1 引入依赖
<dependency>
<groupId>com.auth0</groupId>
<artifactId>java-jwt</artifactId>
<version>4.0.0</version>
</dependency>
2.2 创建JwtToken工具类
JwtToken工具类的核心功能包括创建token和验证token
package com.example.demo.utils.jwt;
/**
* @Author : HuangJiajian
* @create 2022/9/28 10:29
*/
public class JwtUtils {
private static final String secret = "test123456";
/**
* @Author HJJ
* @Date 2022-12-28 16:59
* @Params userId,userName
* @Return token
* @Description 创建Token,默认时间为7天
*/
public static String createToken(String userName, String authentications){
long expire = 7 * 24 * 3600 * 1000;
return JWT.create().withAudience(userName)
.withIssuedAt(new Date())
.withExpiresAt(new Date(System.currentTimeMillis()+ expire))
.withClaim("authentications", authentications)
.sign(Algorithm.HMAC256(userName+secret));
}
/**
* @Author HJJ
* @Date 2022-12-28 16:59
* @Params token, userName
* @Return boolean
* @Description 验证Token,过期或不正确时会返回false
*/
public static boolean verifyToken(String token, String userName){
DecodedJWT jwt = null;
try {
JWTVerifier verifier = JWT.require(Algorithm.HMAC256(userName+secret)).build();
jwt = verifier.verify(token);
return true;
} catch (Exception e) {
System.out.println(e);
}
return false;
}
}
3.3 配置successHandler
在SecurityConfig文件中配置登录验证成功时的拦截器,详细配置可见另一篇文章
package com.example.demo.config.security;
@Configuration
public class SecurityConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity httpSecurity) throws Exception {
// 登录
httpSecurity.formLogin()
.loginProcessingUrl("/login")
.loginPage("/login.html")
.successHandler(new AuthenticationSuccessConfig())
.failureHandler(new AuthenticationFailConfig());
}
}
自定义AuthenticationSuccessConfig
登录成功拦截,重点需要关注将authentication写入SecurityContextHolder的步骤和将JwtToken写入response的步骤
package com.example.demo.config.security;
/**
* @Author : HuangJiajian
* @create 2022/10/20 16:24
*/
public class AuthenticationSuccessConfig implements AuthenticationSuccessHandler {
/**
* @Author HJJ
* @Date 2022-12-28 17:20
* @Params
* @Return
* @Description 整合JWT,在身份验证成功时,生成JwtToken并将authentication写入SpringSecurity中
*/
@Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
UsernamePasswordAuthenticationToken user = (UsernamePasswordAuthenticationToken) authentication;
String token = JwtUtils.createToken(user.getName(), user.getAuthorities().toString());
SecurityContextHolder.getContext().setAuthentication(authentication);
response.setContentType("text/json;charset=utf-8");
PrintWriter writer = response.getWriter();
writer.write(JSON.toJSONString(new AjaxResult(AjaxResult.CODE.SUCCESS, "success", token)));
writer.flush();
writer.close();
}
}
3.4 配置addFilterBefore
在SecurityConfig文件中配置Filter,详细配置可见另一篇文章
package com.example.demo.config.security;
@Configuration
public class SecurityConfig {
@Autowired
private JwtFilter jwtFilter;
@Bean
public SecurityFilterChain filterChain(HttpSecurity httpSecurity) throws Exception {
httpSecurity.addFilterBefore(jwtFilter, UsernamePasswordAuthenticationFilter.class);
}
}
自定义JwtFilter拦截器,
package com.example.demo.config.security;
/**
* @Author : HuangJiajian
* @create 2022/12/29 9:14
*/
@Component
public class JwtFilter extends OncePerRequestFilter {
@Autowired
private UserDetailServiceImpl userDetailsService;
/**
* @Author HJJ
* @Date 2022-12-29 9:18
* @Params
* @Return
* @Description 获取请求中携带的JwtToken信息,并验证其真实性
*/
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
String token = "";
String username = "";
for (Cookie cookie : request.getCookies()) {
if (cookie.getName().equals("username")) {
username = cookie.getValue();
}
if (cookie.getName().equals("token")) {
token = cookie.getValue();
}
}
if (token.isEmpty() || username.isEmpty()){
filterChain.doFilter(request, response);
return;
}
UserDetails userDetails = userDetailsService.loadUserByUsername(username);
if (JwtUtils.verifyToken(token, username)) {
// 验证成功后,将authentication与在第一次登录时写入的权限做对比,如果成功即可跳过原本的验证了
UsernamePasswordAuthenticationToken authentication =
new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
SecurityContextHolder.getContext().setAuthentication(authentication);
} else {
System.out.println(username + "JwtToken验证失败");
}
filterChain.doFilter(request, response);
}
}
以上就是SpringSecurity整合JwtToken的全过程。