原理是通过登录接口获取jwt颁发的token,颁发时候可以将想传递的用户信息加密融入token里
public static String getJsonWebToken(UserPO userPO) {
String token = Jwts.builder().setSubject(SUBJECT)
//加入账户信息
.claim("account", userPO.getAccount())
.claim("name", userPO.getUserName())
.claim("userId", userPO.getUserId())
//发出时间
.setIssuedAt(new Date())
//过期时间
.setExpiration(new Date(System.currentTimeMillis() + EXPIRE))
//加密方式,秘钥
.signWith(SignatureAlgorithm.HS256, SECRET).compact();
token = TOKEN_PREFIX + token;
return token;
解密token
/**
* 校验token方法
*
* @param token 令牌
* @return
*/
public static Claims checkJWT(String token) {
try {
//解密token,把前缀替换为空
final Claims claims = Jwts.parser().setSigningKey(SECRET).parseClaimsJws(token.replace(TOKEN_PREFIX, "")).getBody();
return claims;
} catch (Exception e) {
return null;
}
}
用户登录的时候,会在拦截器里进行权限的校验
如果token解析成功,就会将数据写入线程缓存
Claims claims = JWTUtils.checkJWT(accesToken);
if (claims == null) {
//或者给前端发送json消息
sendJsonMessage(response, ResponseVO.buildError("登录过期"));
return false;
}
String account = (String) claims.get("account");
//调用工具类 写入线程缓存
ThreadLocalUtils.set("account", account);
//刷新token过期时间
redisUtil.expire("token:" + accesToken, 30 * 60);
然后需要用到的时候就用工具类取出来:
package com.music.netsadcloudmusic.utils;
import java.util.HashMap;
import java.util.Map;
/**
* 线程缓存工具类
*
* @author Boss
*/
public class ThreadLocalUtils {
private static ThreadLocal<Map<String, Object>> cache = new ThreadLocal<Map<String, Object>>();
/**
* 向ThreadLocal缓存值
*
* @param key 要缓存的KEY
* @param value 要缓存的VALUE
*/
public static void set(String key, Object value) {
if (!isCaheIsNull()) {
cache.get().put(key, value);
} else {
Map<String, Object> vmap = new HashMap<>();
vmap.put(key, value);
cache.set(vmap);
}
}
/**
* 从ThreadLocal里获取缓存的值
*
* @param key 要获取的数据的KEY
* @return 要获取的值
*/
public static Object getCache(String key) {
Map<String, Object> map = cache.get();
if (isCaheIsNull()) {
return null;
}
if (map.containsKey(key)) {
return map.get(key);
} else {
return null;
}
}
/**
* 根据KEY移除缓存里的数据
*
* @param key
*/
public static void removeByKey(String key) {
if (isCaheIsNull()) {
return;
} else {
cache.get().remove(key);
}
}
/**
* 移除当前线程缓存
* 用于释放当前线程threadlocal资源
*/
public static void remove() {
cache.remove();
}
private static boolean isCaheIsNull() {
return cache.get() == null;
}
}
重点:一定要记住在afterCompletion里要ThreadLocalUtils.remove();否则ThreadLocal可能引起的内存泄露
threadlocal里面使用了一个存在弱引用的map,当释放掉threadlocal的强引用以后,map里面的value却没有被回收.而这块value永远不会被访问到了. 所以存在着内存泄露. 最好的做法是将调用threadlocal的remove方法.
这种方法的好处就是随用随取,减少性能的消耗
pojo:
/**
* @author: Boss
* Date: 2020/12/28
* Time: 16:44
* Description:
*/
@ApiModel("用户信息DTO")
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class UserInformationDTO {
private String name;
private String account;
private Integer userId;
}
工具类:
/**
* @author: Boss
* Date: 2020/12/28
* Time: 16:57
* Description: 从请求头的token里解析出线程用户是谁
*/
public class GetUserInformationUtils {
public static UserInformationDTO getUserBasic(){
// 获取相关对象
RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
HttpServletRequest request = ((ServletRequestAttributes) requestAttributes).getRequest();
String accesToken = request.getHeader("token");
Claims claims = JWTUtils.checkJWT(accesToken);
if (claims == null) {
//或者给前端发送json消息
throw new MyException(ErrorCode.ERROR);
}
String account = (String) claims.get("account");
String name = (String) claims.get("name");
Integer userId = (Integer) claims.get("userId");
UserInformationDTO userINformationDTO = UserInformationDTO.builder()
.userId(userId).account(account).name(name).build();
return userINformationDTO;
}
}
用法:
UserInformationDTO userBasic = GetUserInformationUtils.getUserBasic();
String account = userBasic.getAccount();