GlobalTokenAspect.java 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596
  1. package com.caimei.config;
  2. import com.caimei.components.RedisService;
  3. import com.caimei.utils.JwtUtil;
  4. import org.aspectj.lang.JoinPoint;
  5. import org.aspectj.lang.annotation.Aspect;
  6. import org.aspectj.lang.annotation.Before;
  7. import org.aspectj.lang.annotation.Pointcut;
  8. import org.springframework.beans.factory.annotation.Autowired;
  9. import org.springframework.beans.factory.annotation.Value;
  10. import org.springframework.stereotype.Component;
  11. import org.springframework.web.context.request.RequestContextHolder;
  12. import org.springframework.web.context.request.ServletRequestAttributes;
  13. import javax.servlet.http.HttpServletRequest;
  14. /**
  15. * 把切面类加入到IOC容器中
  16. * 所有请求都会更新redis存token的时间
  17. *
  18. * @author : Charles
  19. * @date : 2020/3/17
  20. */
  21. @Component
  22. @Aspect
  23. public class GlobalTokenAspect {
  24. @Value("${caimei.oldapi}")
  25. private String oldApi;
  26. private RedisService redisService;
  27. @Autowired
  28. public void setRedisService(RedisService redisService) {
  29. this.redisService = redisService;
  30. }
  31. /**
  32. * 定义一个切入点 我这里是从controller切入
  33. */
  34. @Pointcut("execution(public * com.caimei.controller..*.*(..))")
  35. public void pointCut() {
  36. }
  37. /**
  38. * 前置通知
  39. * 在进入方法前执行 可以对参数进行限制或者拦截
  40. * 通常在这边做日志存储存到数据库中
  41. * @param joinPoint
  42. * @throws Throwable
  43. */
  44. @Before("pointCut()")
  45. public void before(JoinPoint joinPoint) throws Throwable {
  46. ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
  47. HttpServletRequest request = attributes.getRequest();
  48. String token = request.getHeader("X-Token");
  49. String cacheToken = null!=token ? String.valueOf(redisService.get(token)) : null;
  50. // Redis过期后会得到"null"值,所以需判断字符串"null"
  51. if (cacheToken != null && cacheToken.length() != 0 && !"null".equals(cacheToken)) {
  52. if (JwtUtil.isVerify(cacheToken)) {
  53. /* String mobileOrEmail = JwtUtil.parseTokenAud(cacheToken);*/
  54. int userId = JwtUtil.parseTokenUid(cacheToken);
  55. // 刷新token
  56. tokenRefresh(token, userId);
  57. }
  58. }
  59. }
  60. /**
  61. * JWT Token续签:
  62. * 业务逻辑:登录成功后,用户在未过期时间内继续操作,续签token。
  63. * 登录成功后,空闲超过过期时间,返回token已失效,重新登录。
  64. * 实现逻辑:
  65. * 1.登录成功后将token存储到redis里面(这时候k、v值一样都为token),并设置过期时间为token过期时间
  66. * 2.当用户请求时token值还未过期,则重新设置redis里token的过期时间。
  67. * 3.当用户请求时token值已过期,但redis中还在,则JWT重新生成token并覆盖v值(这时候k、v值不一样了),然后设置redis过期时间。
  68. * 4.当用户请求时token值已过期,并且redis中也不存在,则用户空闲超时,返回token已失效,重新登录。
  69. */
  70. public boolean tokenRefresh(String token, Integer userID) {
  71. String cacheToken = String.valueOf(redisService.get(token));
  72. // 过期后会得到"null"值,所以需判断字符串"null"
  73. if (cacheToken != null && cacheToken.length() != 0 && !"null".equals(cacheToken)) {
  74. // 校验token有效性
  75. if (!JwtUtil.isVerify(cacheToken)) {
  76. // 生成token
  77. String newToken = JwtUtil.createToken(userID);
  78. // 将token存入redis,并设置超时时间
  79. redisService.set(token, newToken, JwtUtil.getExpireTime());
  80. } else {
  81. // 重新设置超时时间
  82. redisService.set(token, cacheToken, JwtUtil.getExpireTime());
  83. }
  84. return true;
  85. }
  86. return false;
  87. }
  88. }