1、相关依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.8.11</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
2、需要JWT加密的实体类
import lombok.Data;
import java.io.Serializable;
/**
* 会员
*
* @TableName member
*/
@Data
public class MemberLoginResp implements Serializable {
/**
* id
*/
private Long id;
/**
* 手机号
*/
private String mobile;
/**
* token
*/
private String token;
}
3、编写JWT工具类
public class JwtUtil {
private static final Logger LOG = LoggerFactory.getLogger(JwtUtil.class);
/**
* 盐值很重要,不能泄漏,且每个项目都应该不一样,可以放到配置文件中
*/
private static final String key = "qjb12306";
public static String createToken(Long id, String mobile) {
GlobalBouncyCastleProvider.setUseBouncyCastle(false);
DateTime now = DateTime.now();
DateTime expTime = now.offsetNew(DateField.HOUR, 24);
Map<String, Object> payload = new HashMap<>();
// 签发时间
payload.put(JWTPayload.ISSUED_AT, now);
// 过期时间
payload.put(JWTPayload.EXPIRES_AT, expTime);
// 生效时间
payload.put(JWTPayload.NOT_BEFORE, now);
// 内容
payload.put("id", id);
payload.put("mobile", mobile);
String token = JWTUtil.createToken(payload, key.getBytes());
LOG.info("生成JWT token:{}", token);
return token;
}
/**
* 该函数用于校验JWT(token)的有效性。
* 首先通过JWTUtil.parseToken解析token,然后使用setKey设置密钥
* @param token
* @return 返回校验结果
*/
public static boolean validate(String token) {
GlobalBouncyCastleProvider.setUseBouncyCastle(false);
JWT jwt = JWTUtil.parseToken(token).setKey(key.getBytes());
// validate包含了verify,通过validate方法校验token的有效性
boolean validate = jwt.validate(0);
LOG.info("JWT token校验结果:{}", validate);
return validate;
}
/**
* 根据提供的token解析并获取JSON对象。
* 该方法会去除payload中的特定字段(如颁发时间、过期时间、不可早于时间)。
*
* @param token 待解析的JWT token字符串。
* @return 解析后去除特定字段的payload内容,作为JSONObject返回。
*/
public static JSONObject getJSONObject(String token) {
GlobalBouncyCastleProvider.setUseBouncyCastle(false);
JWT jwt = JWTUtil.parseToken(token).setKey(key.getBytes());
JSONObject payloads = jwt.getPayloads();
payloads.remove(JWTPayload.ISSUED_AT);
payloads.remove(JWTPayload.EXPIRES_AT);
payloads.remove(JWTPayload.NOT_BEFORE);
LOG.info("根据token获取原始内容:{}", payloads);
return payloads;
}
// 工具测试,可删除
public static void main(String[] args) {
createToken(1L, "123");
String token = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJuYmYiOjE3MTQ1NTMxNjEsIm1vYmlsZSI6IjEyMyIsImlkIjoxLCJleHAiOjE3MTQ2Mzk1NjEsImlhdCI6MTcxNDU1MzE2MX0.P67OrNd9SCv0zOfxoLlbsooVHdxPZ-N-gCgDK1E2-F0";
validate(token);
getJSONObject(token);
}
}
在服务层 调用 JwtUtil 生成 token 并返回给前端
String token = JwtUtil.createToken(memberLoginResp.getId(), memberLoginResp.getMobile());
4、保存登录信息到本地线程
在 context
包下 创建 LoginMemberContext 类保存登录信息
public class LoginMemberContext {
private static final Logger LOG = LoggerFactory.getLogger(LoginMemberContext.class);
private static ThreadLocal<MemberLoginResp> member = new ThreadLocal<>();
public static MemberLoginResp getMember() {
return member.get();
}
public static void setMember(MemberLoginResp member) {
LoginMemberContext.member.set(member);
}
public static Long getId() {
try {
return member.get().getId();
} catch (Exception e) {
LOG.error("获取登录会员信息异常", e);
throw e;
}
}
}
5、添加拦截器
获取前端请求头中的 token 值解析 token 后传入本地线程变量中
/**
* 拦截器:Spring框架特有的,常用于登录校验,权限校验,请求日志打印
*/
@Component
public class MemberInterceptor implements HandlerInterceptor {
private static final Logger LOG = LoggerFactory.getLogger(MemberInterceptor.class);
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
//获取header的token参数
String token = request.getHeader("token");
if (StrUtil.isNotBlank(token)) {
LOG.info("获取会员登录token:{}", token);
JSONObject loginMember = JwtUtil.getJSONObject(token);
LOG.info("当前登录会员:{}", loginMember);
MemberLoginResp member = JSONUtil.toBean(loginMember, MemberLoginResp.class);
LoginMemberContext.setMember(member);
}
return true;
}
}
6、在 Spring MVC中添加拦截器
@Configuration
public class SpringMvcConfig implements WebMvcConfigurer {
@Resource
MemberInterceptor memberInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(memberInterceptor)
.addPathPatterns("/**");
}
}
7、使用
log.info("从本地线程中获取token {}", LoginMemberContext.getMember());