chao 3 роки тому
батько
коміт
d9d4e82b3b

+ 38 - 0
src/main/java/com/caimei365/order/config/ApiConfig.java

@@ -0,0 +1,38 @@
+package com.caimei365.order.config;
+
+import org.springframework.context.annotation.Configuration;
+import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
+import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
+
+import javax.annotation.Resource;
+
+/**
+ * Description
+ *
+ * @author : Charles
+ * @date : 2020/3/12
+ */
+@Configuration
+public class ApiConfig implements WebMvcConfigurer {
+    @Resource
+    private ApiInterceptor apiInterceptor;
+
+    @Override
+    public void addInterceptors(InterceptorRegistry registry) {
+        //addPathPatterns 用于添加拦截规则
+        //excludePathPatterns 用于排除拦截
+        registry.addInterceptor(apiInterceptor)
+                .addPathPatterns("/order/club/cart/**")
+                .addPathPatterns("/order/seller/cart/**")
+                .addPathPatterns("/order/submit/**")
+                .addPathPatterns("/order/club/confirm")
+                .addPathPatterns("/order/club/receive")
+                .addPathPatterns("/order/club/cancel")
+                .addPathPatterns("/order/club/delete")
+                .addPathPatterns("/order/club/second/payment/confirm")
+                .addPathPatterns("/order/pay/balance/deduction");
+//                .excludePathPatterns("/order/detail")
+//                .excludePathPatterns("/order/commodityData")
+//                .excludePathPatterns("/order/shareCode");
+    }
+}

+ 45 - 0
src/main/java/com/caimei365/order/config/ApiInterceptor.java

@@ -0,0 +1,45 @@
+package com.caimei365.order.config;
+
+import com.caimei365.order.components.RedisService;
+import com.caimei365.order.utils.JwtUtil;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Component;
+import org.springframework.web.servlet.HandlerInterceptor;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+/**
+ * Description
+ *
+ * @author : Charles
+ * @date : 2020/3/12
+ */
+@Component
+public class ApiInterceptor implements HandlerInterceptor {
+
+    private RedisService redisService;
+
+    @Autowired
+    public void setRedisService(RedisService redisService) {
+        this.redisService = redisService;
+    }
+
+    @Override
+    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
+        String token = request.getHeader("X-Token");
+        if (!JwtUtil.isVerify(token)) {
+            String cacheToken = null != token ? String.valueOf(redisService.get(token)) : null;
+            if (null == cacheToken || !JwtUtil.isVerify(cacheToken)) {
+                // Token失效
+                StringBuffer url = request.getRequestURL();
+                String serverName = url.delete(url.length() - request.getRequestURI().length(), url.length()).toString();
+                response.sendRedirect(serverName + "/order/unauthorized");
+                return false;
+            }
+        }
+        return true;
+    }
+
+}

+ 92 - 0
src/main/java/com/caimei365/order/config/GlobalTokenAspect.java

@@ -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;
+    }
+}

+ 9 - 0
src/main/java/com/caimei365/order/controller/BaseApi.java

@@ -1,5 +1,6 @@
 package com.caimei365.order.controller;
 
+import com.caimei365.order.model.ResponseJson;
 import lombok.RequiredArgsConstructor;
 import org.springframework.beans.factory.annotation.Value;
 import org.springframework.web.bind.annotation.GetMapping;
@@ -27,5 +28,13 @@ public class BaseApi {
         }
         return "欢迎使用!";
     }
+
+    /**
+     * Token失效
+     */
+    @GetMapping("/unauthorized")
+    public ResponseJson<Void> unauthorized() {
+        return ResponseJson.error(-99, "Token失效请重新登录!", null);
+    }
 }