Spring Boot 使用 Hutool-jwt 实现 token 验证功能
作者:訾博ZiBo
JWT 就是一种网络身份认证和信息交换格式,这篇文章主要介绍了Spring Boot使用Hutool-jwt实现token验证,需要的朋友可以参考下
一、JWT 概述
1、简介
简单地说,JWT 就是一种网络身份认证和信息交换格式。
2、结构
- Header 头部信息,主要声明了 JWT 的签名算法等信息;
- Payload 载荷信息,主要承载了各种声明并传递明文数据;
- Signature 签名,拥有该部分的 JWT 被称为 JWS,也就是签了名的 JWS ,用于校验数据。
# 整体结构 header.payload.signature
3、使用
JWT模块的核心主要是两个类:
JWT
类用于链式生成、解析或验证JWT信息。JWTUtil
类主要是JWT的一些工具封装,提供更加简洁的JWT生成、解析和验证工作。
二、基本使用
逻辑较为简单,下面的代码作为参考。
0、整体思路
- 写一个工具类封装生成、校验和解析 token 的方法;
- 在注册和登录时生成 token ,生成的 token 存入 redis ,下次登录去 redis 获取,如果存在则直接返回,反之重新生成,并存入 redis;
- 在拦截器中校验和解析 token ,拿到 token 中有用的信息存入
private static final ThreadLocal<UserDto> *THREAD_LOCAL* = new ThreadLocal<>();
,以便后续取用。
1、JWT 工具类
根据需要调整 userDto
package com.zibo.common.util; import cn.hutool.jwt.JWT; import com.zibo.modules.user.dto.UserDto; import java.util.Date; import java.util.HashMap; import java.util.Map; import org.apache.commons.lang3.StringUtils; /** * JWT 工具类 * * @author zibo * @date 2023/7/2 14:36 * @slogan 慢慢学,不要停。 */ public class JWTUtility { /** * 密钥 */ private static final byte[] KEY = "zibo".getBytes(); /** * 过期时间(秒):7 天 */ public static final long EXPIRE = 7 * 24 * 60 * 60; private JWTUtility() { } /** * 根据 userDto 生成 token * * @param dto 用户信息 * @return token */ public static String generateTokenForUser(UserDto dto) { Map<String, Object> map = new HashMap<>(); map.put("id", dto.getId()); map.put("nickname", dto.getNickname()); return generateToken(map); } /** * 根据 map 生成 token 默认:HS265(HmacSHA256)算法 * * @param map 携带数据 * @return token */ public static String generateToken(Map<String, Object> map) { JWT jwt = JWT.create(); // 设置携带数据 map.forEach(jwt::setPayload); // 设置密钥 jwt.setKey(KEY); // 设置过期时间 jwt.setExpiresAt(new Date(System.currentTimeMillis() + EXPIRE * 1000)); return jwt.sign(); } /** * token 校验 * @param token token * @return 是否通过校验 */ public static boolean verify (String token) { if (StringUtils.isBlank(token)) return false; return JWT.of(token).setKey(KEY).verify(); } /** * token 校验,并获取 userDto * @param token token * @return userDto */ public static UserDto verifyAndGetUser(String token) { if(!verify(token)) return null; // 解析数据 JWT jwt = JWT.of(token); Long id = Long.valueOf(jwt.getPayload("id").toString()); String nickname = jwt.getPayload("nickname").toString(); // 返回用户信息 return new UserDto(id, nickname); } }
2、在拦截器中校验和解析 token
package com.zibo.common.config; import com.zibo.common.enums.AppCode; import com.zibo.common.pojo.ApiException; import com.zibo.common.pojo.UserHolder; import com.zibo.common.util.JWTUtility; import com.zibo.modules.user.dto.UserDto; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.springframework.stereotype.Component; import org.springframework.web.servlet.HandlerInterceptor; /** * 自定义拦截器 * @author Administrator */ @Component @Slf4j public class UserInterceptor implements HandlerInterceptor { /** * 进入controller方法之前执行。如果返回false,则不会执行 controller 的方法 * * @param request 请求 * @param response 响应 * @param handler 处理器 * @return 是否放行 */ @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) { // 获取 header 中的 Authorization 信息 String token = request.getHeader("token"); if (StringUtils.isNotBlank(token)) { UserDto dto = JWTUtility.verifyAndGetUser(token); if (dto != null) { UserHolder.setUserInfo(dto); } else { log.error("token 验证失败!token is {}, uri is {}", token, request.getRequestURI()); throw new ApiException(AppCode.TOKEN_ERROR, "token 校验不通过!"); } } else { log.error("token 验证失败!token is {}, uri is {}", token, request.getRequestURI()); throw new ApiException(AppCode.TOKEN_ERROR, "token 为空!"); } return true; } /** * 响应结束之前 * @param request 请求 * @param response 响应 * @param handler 处理器 */ @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) { // 清理掉当前线程中的数据,防止内存泄漏 UserHolder.remove(); } }
3、在注册和登录时签发 token
@PostMapping("/loginOrRegister") public UserDto loginOrRegister(@RequestBody @Validated UserDto dto) { // 通过手机号查询 UserDto byPhone = service.findByPhone(dto.getPhone()); // 如果操作标记为空,则报错 if (ObjectUtils.isEmpty(dto.getOperation())) { throw new ApiException("操作标记不能为空!"); } // 如果是注册 if (dto.getOperation() == 0) { // 如果用户已经存在,则报错 if (ObjectUtils.isNotEmpty(byPhone)) { throw new ApiException("注册失败!账号已存在!"); } // 创建用户 Long save = service.save(dto); // 返回用户信息 UserDto userDto = service.findById(save); // 根据用户生成 token String token = JWTUtility.generateTokenForUser(userDto); // 保存到 redis service.saveToken(userDto.getId(), token); // 设置 token userDto.setToken(token); LogUtility.baseInfoWith("注册成功!" + userDto); return userDto; } // 登录 if (ObjectUtils.isEmpty(byPhone)) { log.error("登录失败!账号不存在!" + dto); // 账号不存在 throw new ApiException("登录失败!账号不存在!com/zibo/controller/user/UserController.java:62"); } else { // 比较密码是否一致 if (!byPhone.getPassword().equals(dto.getPassword())) { throw new ApiException("登录失败!账号或密码错误!"); } // 更新最后登录时间 byPhone.setLastLoginTime(LocalDateTime.now()); service.save(byPhone); // 从 redis 获取 token String token = service.getToken(byPhone.getId()); if (StringUtils.isBlank(token)) { // 根据用户生成 token token = JWTUtility.generateTokenForUser(byPhone); log.info("用户登录,并生成token,id 为:{}, 昵称为:{},token 为:{}", byPhone.getId(), byPhone.getNickname(), token); // 保存到 redis service.saveToken(byPhone.getId(), token); } // 设置 token byPhone.setToken(token); LogUtility.baseInfoWith("登录成功!" + byPhone); return byPhone; } }
4、用户信息 UserHolder
package com.zibo.common.pojo; import com.zibo.modules.user.dto.UserDto; /** * 存放用户信息的容器 * @author Administrator */ public class UserHolder { private static final ThreadLocal<UserDto> THREAD_LOCAL = new ThreadLocal<>(); private UserHolder() { } /** * 获取线程中的用户 * @return 用户信息 */ public static UserDto getUserInfo() { return THREAD_LOCAL.get(); } /** * 设置当前线程中的用户 * @param info 用户信息 */ public static void setUserInfo(UserDto info) { THREAD_LOCAL.set(info); } public static Long getUserId() { UserDto dto = THREAD_LOCAL.get(); if (dto != null) { return dto.getId(); } else { // 注册或登录时没有,返回 0 return 0L; } } public static void remove() { THREAD_LOCAL.remove(); } }
到此这篇关于Spring Boot 使用 Hutool-jwt 实现 token 验证的文章就介绍到这了,更多相关Spring Boot token 验证内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!