<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.3.7</version>
</dependency>
<!-- jwt token -->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.1</version>
</dependency>
<dependency>
<groupId>jakarta.xml.bind</groupId>
<artifactId>jakarta.xml.bind-api</artifactId>
</dependency>
1. jwt 每次解析的对象
import cn.hutool.core.util.IdUtil;
import lombok.Data;
/**
* JwtPayLoad部分
* @date 2020/3/12 17:41
*/
@Data
public class JwtPayLoad {
/**
* 用户id
*/
private Long userId;
/**
* 账号
*/
private String account;
/**
* 角色
*/
private String role;
/**
* 唯一表示id, 用于缓存登录用户的唯一凭证
*/
private String uuid;
public JwtPayLoad() {
}
public JwtPayLoad(Long userId, String account, String role) {
this.userId = userId;
this.account = account;
this.role = role;
this.uuid = IdUtil.fastUUID();
}
}
2.JwtToken工具类
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.JwtException;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import lombok.extern.slf4j.Slf4j;
import java.util.Date;
/**
* JwtToken工具类
*
* @date 2020/3/12 17:39
*/
@Slf4j
public class JwtTokenUtil {
private static final String scr="2765lov74twvr8k37j7c489ib00y2380";
/**
* 生成token
*
* @date 2020/3/12 17:52
*/
public static String generateToken(JwtPayLoad jwtPayLoad) {
DateTime expirationDate = DateUtil.offsetMillisecond(new Date(),
Convert.toInt(7200) * 1000);
String jwtSecret = scr;
return Jwts.builder()
.setClaims(BeanUtil.beanToMap(jwtPayLoad))
.setSubject(jwtPayLoad.getUserId().toString())
.setIssuedAt(new Date())
.setExpiration(expirationDate)
.signWith(SignatureAlgorithm.HS512, jwtSecret)
.compact();
}
/**
* 生成永久token
*
* @date 2020/3/12 17:52
*/
public static String generatePermanentToken(JwtPayLoad jwtPayLoad) {
String jwtSecret = scr;
return Jwts.builder()
.setClaims(BeanUtil.beanToMap(jwtPayLoad))
.setSubject(jwtPayLoad.getUserId().toString())
.setIssuedAt(new Date())
.signWith(SignatureAlgorithm.HS512, jwtSecret)
.compact();
}
/**
* 根据token获取Claims
* @date 2020/3/13 10:29
*/
private static Claims getClaimsFromToken(String token) {
String secret = scr;
return Jwts.parser()
.setSigningKey(secret)
.parseClaimsJws(token)
.getBody();
}
/**
* 获取JwtPayLoad部分
*
* @date 2020/3/12 17:53
*/
public static JwtPayLoad getJwtPayLoad(String token) {
Claims claims = getClaimsFromToken(token);
return BeanUtil.mapToBean(claims, JwtPayLoad.class, false);
}
/**
* 校验token是否正确
* @date 2020/3/13 10:36
*/
public static Boolean checkToken(String token) {
try {
getClaimsFromToken(token);
return true;
}catch (JwtException jwtException) {
return false;
}
}
/**
* 校验token是否失效
*
* @date 2020/3/13 10:30
*/
public static String isTokenExpired(String token) {
try {
Claims claims = getClaimsFromToken(token);
final Date expiration = claims.getExpiration();
log.info("校验token是否失效,token保存的expiration:"+expiration+";当前时间为:"+new Date());
Date date = new Date();
if(expiration.before(date)){
return null;
}else{
long minute=(expiration.getTime()-date.getTime())/(1000*60);//转化minute
//判断账户token小于五分钟,则重新生成
if(minute<5){
JwtPayLoad jwtPayLoad = getJwtPayLoad(token);
return generateToken(jwtPayLoad);
}
}
return token;
} catch (Exception expiredJwtException) {
return null;
}
}
}
3.用户登录方法
import cn.hutool.core.util.ObjectUtil;
@Service
public class LoginUserServiceImpl extends ServiceImpl<LoginUserMapper, LoginUser> implements LoginUserService {
@Resource
private UserCache userCache;
/**
* 根据账号密码获取用户
*
* @param loginParamVo
* @return
*/
@Override
public BaseRes<LoginRespVo> getUserByAccountAndPasswd(LoginParamVo loginParamVo) {
if(paramError(loginParamVo)){
throw new RuntimeException("请输入账号密码");
}
String passwdAES = AESUtils.encryptCbcMode(loginParamVo.getPasswd(), CommonConstant.PASSWORD_KEY, CommonConstant.PASSWORD_KEY_IV);
LambdaQueryWrapper<LoginUser> lambdaQueryWrapper = new LambdaQueryWrapper<LoginUser>();
lambdaQueryWrapper.eq(LoginUser::getAccount,loginParamVo.getAccount());
lambdaQueryWrapper.eq(LoginUser::getPasswd,passwdAES);
//1-有效
lambdaQueryWrapper.and(i -> i.eq(LoginUser::getStatus, StatusEnum.EFFECT.value()).or().eq(LoginUser::getStatus, StatusEnum.DISABLE.value()) );
LoginUser user = this.getOne(lambdaQueryWrapper);
if(user == null){
throw new RuntimeException("用户或密码错误");
}
LoginRespVo token = doLogin(user);
return BaseRes.success(token);
}
private LoginRespVo doLogin(LoginUser user){
if(StatusEnum.DISABLE.value().equals(user.getStatus())){
throw new RuntimeException("账号被冻结,请联系管理员");
}
//构造jwtPayLoad
JwtPayLoad jwtPayLoad = new JwtPayLoad(user.getId(), user.getAccount(),user.getRole());
//生成token
String token = JwtTokenUtil.generateToken(jwtPayLoad);
user.setPasswd("");
//缓存token与登录用户信息对应, 默认2个小时
this.cacheLoginUser(jwtPayLoad, user);
// Object cacheObject = userCache.get(jwtPayLoad.getUuid());
LoginRespVo result = new LoginRespVo();
result.setRole(user.getRole());
result.setToken(token);
return result;
}
/**
* 缓存token与登录用户信息对应, 默认2个小时
*
* @date 2020/3/13 14:51
*/
private void cacheLoginUser(JwtPayLoad jwtPayLoad, LoginUser loginUser) {
String redisLoginUserKey = jwtPayLoad.getUuid();
userCache.put(redisLoginUserKey, loginUser);
}
public void logout(String token) {
//如果token为空直接返回
if (ObjectUtil.isEmpty(token)) {
return;
}
//校验token,错误则抛异常,待确定
this.checkToken(token);
//根据token获取JwtPayLoad部分
JwtPayLoad jwtPayLoad = JwtTokenUtil.getJwtPayLoad(token);
//获取缓存的key
String loginUserCacheKey = jwtPayLoad.getUuid();
this.clearUser(loginUserCacheKey);
}
public void checkToken(String token) {
//校验token是否正确
Boolean tokenCorrect = JwtTokenUtil.checkToken(token);
if (!tokenCorrect) {
throw new RuntimeException("token 错误");
}
//校验token是否失效
String tokenExpired = JwtTokenUtil.isTokenExpired(token);
if (StringUtils.isBlank(tokenExpired)) {
throw new RuntimeException("token 失效");
}
}
/**
* 根据key清空登陆信息
*
* @date 2020/6/19 12:28
*/
private void clearUser(String loginUserKey) {
//获取缓存的用户
Object cacheObject = userCache.get(loginUserKey);
//如果缓存的用户存在,清除会话,否则表示该会话信息已失效,不执行任何操作
if (ObjectUtil.isNotEmpty(cacheObject)) {
//清除登录会话
userCache.remove(loginUserKey);
}
}
private Boolean paramError(LoginParamVo loginParamVo){
if (loginParamVo == null) {
return true;
}
if(StringUtils.isBlank(loginParamVo.getAccount()) || StringUtils.isBlank(loginParamVo.getPasswd())){
return true;
}
return false;
}
}
其他拓展可看
token被放入缓存(非redis) 见下篇