JwtUtil.java 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269
  1. package com.free.utils;
  2. import com.auth0.jwt.algorithms.Algorithm;
  3. import com.auth0.jwt.interfaces.Claim;
  4. import com.auth0.jwt.interfaces.DecodedJWT;
  5. import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
  6. import com.baomidou.mybatisplus.core.toolkit.StringUtils;
  7. import com.free.config.CustomizationException;
  8. import com.free.config.ExceptionEnum;
  9. import com.free.entity.system.LoginRecord;
  10. import com.free.service.system.LoginRecordService;
  11. import com.auth0.jwt.JWT;
  12. import com.auth0.jwt.JWTCreator;
  13. import java.time.LocalDateTime;
  14. import java.util.Date;
  15. import java.util.HashMap;
  16. import java.util.Map;
  17. import javax.annotation.PostConstruct;
  18. import javax.servlet.http.HttpServletRequest;
  19. import org.springframework.beans.factory.annotation.Autowired;
  20. import org.springframework.stereotype.Component;
  21. import org.springframework.web.context.request.RequestContextHolder;
  22. import org.springframework.web.context.request.ServletRequestAttributes;
  23. @Component
  24. public class JwtUtil {
  25. public static final String TOKEN_HEADER = "Authorization";
  26. public static final String TOKEN_PREFIX = "Free ";
  27. // 过期时间, 半个小时
  28. private static final long EXPIRE_MIN = 30;
  29. private static final long EXPIRE_TIME = EXPIRE_MIN * 60 * 1000;
  30. // 密钥
  31. private static final String SECRET = "Ziyouyanfa!@#";
  32. /** 特殊处理的key,id和role.因为是long类型 */
  33. private static String spcialKeyId = "id";
  34. private static String spcialKeyRole = "role";
  35. @Autowired
  36. private LoginRecordService lrs;
  37. private static JwtUtil jwtUtil = new JwtUtil();
  38. @PostConstruct
  39. public void init() {
  40. jwtUtil = this;
  41. jwtUtil.lrs = this.lrs;
  42. }
  43. /**
  44. * 生成签名
  45. *
  46. * @param map 数据
  47. * @param secret 密码
  48. * @return 加密后的token
  49. */
  50. public static String sign(Map<String, Object> map) {
  51. Date date = new Date(System.currentTimeMillis() + EXPIRE_TIME);
  52. Algorithm algorithm = Algorithm.HMAC256(SECRET); // 使用HS256算法
  53. JWTCreator.Builder builder = JWT.create();
  54. for (Map.Entry<String, Object> entry : map.entrySet()) {
  55. String k = entry.getKey();
  56. if (spcialKeyId.equals(k) || spcialKeyRole.equals(k)) {
  57. Long v = (Long) entry.getValue();
  58. builder.withClaim(k, v);
  59. } else {
  60. String v = (String) entry.getValue();
  61. builder.withClaim(k, v);
  62. }
  63. }
  64. String token = builder.withExpiresAt(date)
  65. .sign(algorithm);
  66. StringBuilder sb = new StringBuilder();
  67. sb.append(TOKEN_PREFIX);
  68. sb.append(token);
  69. String returnToken = sb.toString();
  70. // 创建登录数据
  71. createLoginRecord(token);
  72. return returnToken;
  73. }
  74. /**
  75. * 注销用户,退出登录
  76. * 将token置空,在校验的时候会处理没有token的情况
  77. *
  78. * @param token
  79. */
  80. public static void logOff(String token) {
  81. Map<String, Object> map = getDetails(token);
  82. Long user_id = (Long) map.get("id");
  83. String type = (String) map.get("type");
  84. QueryWrapper<LoginRecord> qw = new QueryWrapper<>();
  85. qw.eq("user_id", user_id);
  86. qw.eq("type", type);
  87. LoginRecord record = jwtUtil.lrs.getOne(qw);
  88. // 没找到数据不需要处理
  89. if (null == record) {
  90. return;
  91. }
  92. // token不一致,不需要处理
  93. String dbToken = record.getToken();
  94. if (null == dbToken || !dbToken.equals(token)) {
  95. return;
  96. }
  97. // token置空
  98. LoginRecord lr = new LoginRecord();
  99. lr.setToken(null);
  100. lr.setId(record.getId());
  101. jwtUtil.lrs.updateById(lr);
  102. }
  103. /**
  104. * 创建登录数据
  105. *
  106. * @param token jwt
  107. */
  108. public static void createLoginRecord(String token) {
  109. Map<String, Object> map = getDetails(token);
  110. Long user_id = (Long) map.get("id");
  111. String type = (String) map.get("type");
  112. // 第一次登录是创建,以后都是更新, 每个用户都有1个登录数据,但是能否使用要看过期时间
  113. QueryWrapper<LoginRecord> qw = new QueryWrapper<>();
  114. qw.eq("user_id", user_id);
  115. qw.eq("type", type);
  116. LoginRecord histroy = jwtUtil.lrs.getOne(qw);
  117. // 准备数据
  118. LocalDateTime last_time = LocalDateTime.now();
  119. LocalDateTime expire_time = LocalDateTime.now().plusMinutes(EXPIRE_MIN);
  120. HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
  121. String ip = request.getRemoteAddr();
  122. if (null == histroy) {
  123. // 创建数据
  124. LoginRecord lr = new LoginRecord();
  125. lr.setExpire_time(expire_time);
  126. lr.setLast_time(last_time);
  127. lr.setToken(token);
  128. lr.setType(type);
  129. lr.setUser_id(user_id);
  130. lr.setLast_ip(ip);
  131. jwtUtil.lrs.save(lr);
  132. } else {
  133. // 修改数据
  134. LoginRecord lr = new LoginRecord();
  135. lr.setExpire_time(expire_time);
  136. lr.setLast_time(last_time);
  137. lr.setToken(token);
  138. lr.setLast_ip(ip);
  139. lr.setId(histroy.getId());
  140. jwtUtil.lrs.updateById(lr);
  141. }
  142. }
  143. /**
  144. * token续期
  145. *
  146. * @param token jwt
  147. */
  148. public static void renewal(String token) {
  149. Map<String, Object> map = getDetails(token);
  150. Long user_id = (Long) map.get("id");
  151. String type = (String) map.get("type");
  152. QueryWrapper<LoginRecord> qw = new QueryWrapper<>();
  153. qw.eq("user_id", user_id).eq("type", type);
  154. LoginRecord histroy = jwtUtil.lrs.getOne(qw);
  155. // 准备数据
  156. LocalDateTime expire_time = LocalDateTime.now().plusMinutes(EXPIRE_MIN);
  157. LocalDateTime last_time = LocalDateTime.now();
  158. HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
  159. String ip = request.getRemoteAddr();
  160. if (null == histroy) {
  161. // 没有数据,转至创建
  162. createLoginRecord(token);
  163. } else {
  164. // 修改过期时间,使用时间,使用ip
  165. LoginRecord lr = new LoginRecord();
  166. lr.setExpire_time(expire_time);
  167. lr.setId(histroy.getId());
  168. lr.setLast_ip(ip);
  169. lr.setLast_time(last_time);
  170. lr.setToken(token);
  171. jwtUtil.lrs.updateById(lr);
  172. }
  173. }
  174. /**
  175. * 校验token是否正确
  176. *
  177. * @param token 令牌
  178. * @return 是否正确
  179. */
  180. public static void verify(String token) {
  181. // 解开token,查看数据
  182. Map<String, Object> map = getDetails(token);
  183. Long user_id = (Long) map.get("id");
  184. String type = (String) map.get("type");
  185. QueryWrapper<LoginRecord> qw = new QueryWrapper<>();
  186. qw.eq("user_id", user_id).eq("type", type);
  187. LoginRecord histroy = jwtUtil.lrs.getOne(qw);
  188. if (null == histroy) {
  189. throw new CustomizationException(ExceptionEnum.NO_LOGIN_RECORD);
  190. }
  191. // 获取数据库的token
  192. String dbToken = histroy.getToken();
  193. if (null == dbToken) {
  194. throw new CustomizationException(ExceptionEnum.ACCOUNT_IS_LOGOUT);
  195. }
  196. // 取出过期时间,和当前时间进行比较
  197. LocalDateTime nowTime = LocalDateTime.now();
  198. boolean is_after = histroy.getExpire_time().isAfter(nowTime);
  199. if (!is_after) {
  200. throw new CustomizationException(ExceptionEnum.TOKEN_INVALID);
  201. }
  202. // token对比,如果时间允许,但是token码不一致,则说明在其他地点登录
  203. if (!dbToken.equals(token)) {
  204. throw new CustomizationException(ExceptionEnum.OTHER_PLACE_LOGIN);
  205. }
  206. // 如果需要校准 ip,再加上即可
  207. }
  208. /**
  209. * 获得token中的信息
  210. *
  211. * @return token中包含的名称
  212. */
  213. public static Map<String, Object> getDetails(String token) {
  214. if (null == token) {
  215. token = getToken();
  216. }
  217. try {
  218. DecodedJWT jwt = JWT.decode(token);
  219. Map<String, Claim> map = jwt.getClaims();
  220. Map<String, Object> returnMap = new HashMap<>();
  221. for (Map.Entry<String, Claim> entry : map.entrySet()) {
  222. String k = entry.getKey();
  223. if (spcialKeyId.equals(k) || spcialKeyRole.equals(k)) {
  224. Long v = jwt.getClaim(k).asLong();
  225. returnMap.put(k, v);
  226. } else {
  227. String v = jwt.getClaim(k).asString();
  228. returnMap.put(k, v);
  229. }
  230. }
  231. return returnMap;
  232. } catch (Exception e) {
  233. return null;
  234. }
  235. }
  236. /**
  237. * 根据请求头获取token
  238. *
  239. * @return
  240. */
  241. public static String getToken() {
  242. HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
  243. String tokenHeader = request.getHeader(TOKEN_HEADER);
  244. if (StringUtils.isBlank(tokenHeader)) {
  245. throw new CustomizationException(ExceptionEnum.TOKEN_NOT_FOUND);
  246. }
  247. String token = tokenHeader.replace(JwtUtil.TOKEN_PREFIX, "");
  248. if (StringUtils.isBlank(tokenHeader)) {
  249. throw new CustomizationException(ExceptionEnum.TOKEN_NOT_FOUND);
  250. }
  251. return token;
  252. }
  253. }