|
@@ -0,0 +1,92 @@
|
|
|
|
+package com.caimei365.order.config;
|
|
|
|
+
|
|
|
|
+import com.caimei365.order.components.RedisService;
|
|
|
|
+import com.caimei365.order.utils.JwtUtil;
|
|
|
|
+import org.aspectj.lang.JoinPoint;
|
|
|
|
+import org.aspectj.lang.annotation.Aspect;
|
|
|
|
+import org.aspectj.lang.annotation.Before;
|
|
|
|
+import org.aspectj.lang.annotation.Pointcut;
|
|
|
|
+import org.springframework.beans.factory.annotation.Autowired;
|
|
|
|
+import org.springframework.beans.factory.annotation.Value;
|
|
|
|
+import org.springframework.stereotype.Component;
|
|
|
|
+import org.springframework.web.context.request.RequestContextHolder;
|
|
|
|
+import org.springframework.web.context.request.ServletRequestAttributes;
|
|
|
|
+
|
|
|
|
+import javax.servlet.http.HttpServletRequest;
|
|
|
|
+
|
|
|
|
+/**
|
|
|
|
+ * 把切面类加入到IOC容器中
|
|
|
|
+ * 所有请求都会更新redis存token的时间
|
|
|
|
+ *
|
|
|
|
+ * @author : Charles
|
|
|
|
+ * @date : 2020/3/17
|
|
|
|
+ */
|
|
|
|
+@Component
|
|
|
|
+@Aspect
|
|
|
|
+public class GlobalTokenAspect {
|
|
|
|
+
|
|
|
|
+ private RedisService redisService;
|
|
|
|
+ @Autowired
|
|
|
|
+ public void setRedisService(RedisService redisService) {
|
|
|
|
+ this.redisService = redisService;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * 定义一个切入点 我这里是从controller切入
|
|
|
|
+ */
|
|
|
|
+ @Pointcut("execution(public * com.caimei365.order.controller..*.*(..))")
|
|
|
|
+ public void pointCut() {
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * 前置通知
|
|
|
|
+ * 在进入方法前执行 可以对参数进行限制或者拦截
|
|
|
|
+ * 通常在这边做日志存储存到数据库中
|
|
|
|
+ * @param joinPoint
|
|
|
|
+ * @throws Throwable
|
|
|
|
+ */
|
|
|
|
+ @Before("pointCut()")
|
|
|
|
+ public void before(JoinPoint joinPoint) throws Throwable {
|
|
|
|
+ ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
|
|
|
|
+ HttpServletRequest request = attributes.getRequest();
|
|
|
|
+ String token = request.getHeader("X-Token");
|
|
|
|
+ String cacheToken = null!=token ? String.valueOf(redisService.get(token)) : null;
|
|
|
|
+ // Redis过期后会得到"null"值,所以需判断字符串"null"
|
|
|
|
+ if (cacheToken != null && cacheToken.length() != 0 && !"null".equals(cacheToken)) {
|
|
|
|
+ if (JwtUtil.isVerify(cacheToken)) {
|
|
|
|
+ int userId = JwtUtil.parseTokenUid(cacheToken);
|
|
|
|
+ // 刷新token
|
|
|
|
+ tokenRefresh(token, userId);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * JWT Token续签:
|
|
|
|
+ * 业务逻辑:登录成功后,用户在未过期时间内继续操作,续签token。
|
|
|
|
+ * 登录成功后,空闲超过过期时间,返回token已失效,重新登录。
|
|
|
|
+ * 实现逻辑:
|
|
|
|
+ * 1.登录成功后将token存储到redis里面(这时候k、v值一样都为token),并设置过期时间为token过期时间
|
|
|
|
+ * 2.当用户请求时token值还未过期,则重新设置redis里token的过期时间。
|
|
|
|
+ * 3.当用户请求时token值已过期,但redis中还在,则JWT重新生成token并覆盖v值(这时候k、v值不一样了),然后设置redis过期时间。
|
|
|
|
+ * 4.当用户请求时token值已过期,并且redis中也不存在,则用户空闲超时,返回token已失效,重新登录。
|
|
|
|
+ */
|
|
|
|
+ public boolean tokenRefresh(String token, Integer userID) {
|
|
|
|
+ String cacheToken = String.valueOf(redisService.get(token));
|
|
|
|
+ // 过期后会得到"null"值,所以需判断字符串"null"
|
|
|
|
+ if (cacheToken != null && cacheToken.length() != 0 && !"null".equals(cacheToken)) {
|
|
|
|
+ // 校验token有效性
|
|
|
|
+ if (!JwtUtil.isVerify(cacheToken)) {
|
|
|
|
+ // 生成token
|
|
|
|
+ String newToken = JwtUtil.createToken(userID);
|
|
|
|
+ // 将token存入redis,并设置超时时间
|
|
|
|
+ redisService.set(token, newToken, JwtUtil.getExpireTime());
|
|
|
|
+ } else {
|
|
|
|
+ // 重新设置超时时间
|
|
|
|
+ redisService.set(token, cacheToken, JwtUtil.getExpireTime());
|
|
|
|
+ }
|
|
|
|
+ return true;
|
|
|
|
+ }
|
|
|
|
+ return false;
|
|
|
|
+ }
|
|
|
|
+}
|