package com.free.utils; import com.auth0.jwt.algorithms.Algorithm; import com.auth0.jwt.interfaces.Claim; import com.auth0.jwt.interfaces.DecodedJWT; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.core.toolkit.StringUtils; import com.free.config.CustomizationException; import com.free.config.ExceptionEnum; import com.free.entity.system.LoginRecord; import com.free.service.system.LoginRecordService; import com.auth0.jwt.JWT; import com.auth0.jwt.JWTCreator; import java.time.LocalDateTime; import java.util.Date; import java.util.HashMap; import java.util.Map; import javax.annotation.PostConstruct; import javax.servlet.http.HttpServletRequest; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.ServletRequestAttributes; @Component public class JwtUtil { public static final String TOKEN_HEADER = "Authorization"; public static final String TOKEN_PREFIX = "Free "; // 过期时间, 半个小时 private static final long EXPIRE_MIN = 30; private static final long EXPIRE_TIME = EXPIRE_MIN * 60 * 1000; // 密钥 private static final String SECRET = "Ziyouyanfa!@#"; @Autowired private LoginRecordService lrs; private static JwtUtil jwtUtil = new JwtUtil(); @PostConstruct public void init() { jwtUtil = this; jwtUtil.lrs = this.lrs; } /** * 生成签名 * * @param map 数据 * @param secret 密码 * @return 加密后的token */ public static String sign(Map map) { Date date = new Date(System.currentTimeMillis() + EXPIRE_TIME); Algorithm algorithm = Algorithm.HMAC256(SECRET); // 使用HS256算法 JWTCreator.Builder builder = JWT.create(); for (Map.Entry entry : map.entrySet()) { String k = entry.getKey(); String v = (String) entry.getValue(); builder.withClaim(k, v); } String token = builder.withExpiresAt(date) .sign(algorithm); StringBuilder sb = new StringBuilder(); sb.append(TOKEN_PREFIX); sb.append(token); String returnToken = sb.toString(); // 创建登录数据 createLoginRecord(token); return returnToken; } /** * 注销用户,退出登录 * 将token置空,在校验的时候会处理没有token的情况 * * @param token */ public static void logOff(String token) { Map map = getDetails(token); String user_id = (String) map.get("id"); String type = (String) map.get("type"); QueryWrapper qw = new QueryWrapper<>(); qw.eq("user_id", user_id); qw.eq("type", type); LoginRecord record = jwtUtil.lrs.getOne(qw); // 没找到数据不需要处理 if (null == record) { return; } // token不一致,不需要处理 String dbToken = record.getToken(); if (null == dbToken || !dbToken.equals(token)) { return; } // token置空 LoginRecord lr = new LoginRecord(); lr.setToken(null); lr.setId(record.getId()); jwtUtil.lrs.updateById(lr); } /** * 创建登录数据 * * @param token jwt */ public static void createLoginRecord(String token) { Map map = getDetails(token); String user_id = (String) map.get("id"); String type = (String) map.get("type"); // 第一次登录是创建,以后都是更新, 每个用户都有1个登录数据,但是能否使用要看过期时间 QueryWrapper qw = new QueryWrapper<>(); qw.eq("user_id", user_id); qw.eq("type", type); LoginRecord histroy = jwtUtil.lrs.getOne(qw); // 准备数据 LocalDateTime last_time = LocalDateTime.now(); LocalDateTime expire_time = LocalDateTime.now().plusMinutes(EXPIRE_MIN); HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest(); String ip = request.getRemoteAddr(); if (null == histroy) { // 创建数据 LoginRecord lr = new LoginRecord(); lr.setExpire_time(expire_time); lr.setLast_time(last_time); lr.setToken(token); lr.setType(type); lr.setUser_id(user_id); lr.setLast_ip(ip); jwtUtil.lrs.save(lr); } else { // 修改数据 LoginRecord lr = new LoginRecord(); lr.setExpire_time(expire_time); lr.setLast_time(last_time); lr.setToken(token); lr.setLast_ip(ip); lr.setId(histroy.getId()); jwtUtil.lrs.updateById(lr); } } /** * token续期 * * @param token jwt */ public static void renewal(String token) { Map map = getDetails(token); String user_id = (String) map.get("id"); String type = (String) map.get("type"); QueryWrapper qw = new QueryWrapper<>(); qw.eq("user_id", user_id).eq("type", type); LoginRecord histroy = jwtUtil.lrs.getOne(qw); // 准备数据 LocalDateTime expire_time = LocalDateTime.now().plusMinutes(EXPIRE_MIN); LocalDateTime last_time = LocalDateTime.now(); HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest(); String ip = request.getRemoteAddr(); if (null == histroy) { // 没有数据,转至创建 createLoginRecord(token); } else { // 修改过期时间,使用时间,使用ip LoginRecord lr = new LoginRecord(); lr.setExpire_time(expire_time); lr.setId(histroy.getId()); lr.setLast_ip(ip); lr.setLast_time(last_time); lr.setToken(token); jwtUtil.lrs.updateById(lr); } } /** * 校验token是否正确 * * @param token 令牌 * @return 是否正确 */ public static void verify(String token) { // 解开token,查看数据 Map map = getDetails(token); String user_id = (String) map.get("id"); String type = (String) map.get("type"); QueryWrapper qw = new QueryWrapper<>(); qw.eq("user_id", user_id).eq("type", type); LoginRecord histroy = jwtUtil.lrs.getOne(qw); if (null == histroy) { throw new CustomizationException(ExceptionEnum.NO_LOGIN_RECORD); } // 获取数据库的token String dbToken = histroy.getToken(); if (null == dbToken) { throw new CustomizationException(ExceptionEnum.ACCOUNT_IS_LOGOUT); } // 取出过期时间,和当前时间进行比较 LocalDateTime nowTime = LocalDateTime.now(); boolean is_after = histroy.getExpire_time().isAfter(nowTime); if (!is_after) { throw new CustomizationException(ExceptionEnum.TOKEN_INVALID); } // token对比,如果时间允许,但是token码不一致,则说明在其他地点登录 if (!dbToken.equals(token)) { throw new CustomizationException(ExceptionEnum.OTHER_PLACE_LOGIN); } // 如果需要校准 ip,再加上即可 } /** * 获得token中的信息 * * @return token中包含的名称 */ public static Map getDetails(String token) { if (null == token) { token = getToken(); } try { DecodedJWT jwt = JWT.decode(token); Map map = jwt.getClaims(); Map returnMap = new HashMap<>(); for (Map.Entry entry : map.entrySet()) { String k = entry.getKey(); String v = jwt.getClaim(k).asString(); returnMap.put(k, v); } return returnMap; } catch (Exception e) { return null; } } /** * 根据请求头获取token * * @return */ public static String getToken() { HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest(); String tokenHeader = request.getHeader(TOKEN_HEADER); if (StringUtils.isBlank(tokenHeader)) { throw new CustomizationException(ExceptionEnum.TOKEN_NOT_FOUND); } String token = tokenHeader.replace(JwtUtil.TOKEN_PREFIX, ""); if (StringUtils.isBlank(tokenHeader)) { throw new CustomizationException(ExceptionEnum.TOKEN_NOT_FOUND); } return token; } }