瀏覽代碼

修改为传统阻塞式编程

chao 3 年之前
父節點
當前提交
33cbec50b4

+ 1 - 1
pom.xml

@@ -32,7 +32,7 @@
     <dependencies>
         <dependency>
 			<groupId>org.springframework.boot</groupId>
-			<artifactId>spring-boot-starter-webflux</artifactId>
+			<artifactId>spring-boot-starter-web</artifactId>
 		</dependency>
 
 		<dependency>

+ 30 - 0
src/main/java/com/caimei365/user/config/ApiConfig.java

@@ -0,0 +1,30 @@
+package com.caimei365.user.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("/user/club/info/update")
+                .addPathPatterns("/user/shop/info/update")
+                .addPathPatterns("/user/club/archive/deduction");
+//                .excludePathPatterns("/order/detail");
+    }
+}

+ 47 - 0
src/main/java/com/caimei365/user/config/ApiInterceptor.java

@@ -0,0 +1,47 @@
+package com.caimei365.user.config;
+
+
+import com.caimei365.user.components.RedisService;
+import com.caimei365.user.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 {
+
+    @Value("${caimei.coreDomain}")
+    private String domain;
+
+    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失效
+                response.sendRedirect(domain + "/order/unauthorized");
+                return false;
+            }
+        }
+        return true;
+    }
+
+}

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

@@ -0,0 +1,92 @@
+package com.caimei365.user.config;
+
+
+import com.caimei365.user.components.RedisService;
+import com.caimei365.user.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.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.user.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;
+    }
+}

+ 124 - 124
src/main/java/com/caimei365/user/config/TokenFilter.java

@@ -1,124 +1,124 @@
-package com.caimei365.user.config;
-
-import com.alibaba.fastjson.JSONObject;
-import com.caimei365.user.components.RedisService;
-import com.caimei365.user.utils.JwtUtil;
-import lombok.extern.slf4j.Slf4j;
-import org.apache.commons.lang.StringUtils;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.core.io.buffer.DataBuffer;
-import org.springframework.http.HttpHeaders;
-import org.springframework.http.HttpStatus;
-import org.springframework.http.server.reactive.ServerHttpRequest;
-import org.springframework.http.server.reactive.ServerHttpResponse;
-import org.springframework.stereotype.Component;
-import org.springframework.web.server.ServerWebExchange;
-import org.springframework.web.server.WebFilter;
-import org.springframework.web.server.WebFilterChain;
-import reactor.core.publisher.Flux;
-import reactor.core.publisher.Mono;
-
-import java.nio.charset.StandardCharsets;
-import java.util.Arrays;
-
-/**
- * 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已失效,重新登录。
- *
- * @author : Charles
- * @date : 2021/3/30
- */
-@Slf4j
-@Component
-public class TokenFilter implements WebFilter {
-
-    private static final String AUTH = "X-Token";
-    /**
-     * 需要权限认证的接口路径
-     */
-    private static final String[] PERMISSION_URLS = new String[]{
-        "/user/club/info/update",
-        "/user/shop/info/update",
-        "/user/club/archive/deduction"
-    };
-    private RedisService redisService;
-    @Autowired
-    public void setRedisService(RedisService redisService) {
-        this.redisService = redisService;
-    }
-
-    @Override
-    public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
-        ServerHttpRequest request = exchange.getRequest();
-        ServerHttpResponse response = exchange.getResponse();
-
-        HttpHeaders header = request.getHeaders();
-        String token = header.getFirst(AUTH);
-        String url = request.getURI().getPath();
-
-        if (StringUtils.isBlank(token) && Arrays.asList(PERMISSION_URLS).contains(url)) {
-            log.error("未经授权,Token为空!");
-            return tokenErrorResponse(response, "未经授权,Token为空!");
-        }
-
-        if (StringUtils.isNotBlank(token)) {
-            String cacheToken = (String) redisService.get(token);
-            // token续签
-            if (StringUtils.isNotBlank(cacheToken) && !"null".equals(cacheToken) && JwtUtil.isVerify(cacheToken)) {
-                int userId = JwtUtil.parseTokenUid(cacheToken);
-                log.debug("Token续签,UserId:"+userId+",Token:"+token);
-                // 再次校验token有效性
-                if (!JwtUtil.isVerify(cacheToken)) {
-                    // 生成token
-                    String newToken = JwtUtil.createToken(userId);
-                    // 将token存入redis,并设置超时时间(秒)
-                    redisService.set(token, newToken, JwtUtil.getExpireTime());
-                } else {
-                    // 重新设置超时时间(秒)
-                    redisService.expire(token, JwtUtil.getExpireTime());
-                }
-            } else {
-                // 需要验证的路径
-                if(Arrays.asList(PERMISSION_URLS).contains(url)) {
-                    // Token失效
-                    log.error("Token失效,token:"+token+",cacheToken:"+cacheToken);
-                    return tokenErrorResponse(response, "Token失效,请重新登录!");
-                }
-            }
-        }
-        //            //TODO 将用户信息存放在请求header中传递给下游业务
-        //            ServerHttpRequest.Builder mutate = request.mutate();
-        //            mutate.header("demo-user-name", username);
-        //            ServerHttpRequest buildReuqest = mutate.build();
-        //
-        //            //todo 如果响应中需要放数据,也可以放在response的header中
-        //            response.setStatusCode(HttpStatus.OK);
-        //            response.getHeaders().add("demo-user-name",username);
-        //            return chain.filter(exchange.mutate()
-        //                    .request(buildReuqest)
-        //                    .response(response)
-        //                    .build());
-        return chain.filter(exchange);
-    }
-
-    private Mono<Void> tokenErrorResponse(ServerHttpResponse response, String responseMsg){
-        response.setStatusCode(HttpStatus.OK);
-        response.getHeaders().add("Content-Type", "application/json;charset=UTF-8");
-        JSONObject res = new JSONObject();
-        res.put("code", -99);
-        res.put("msg", responseMsg);
-        byte[] responseByte = res.toJSONString().getBytes(StandardCharsets.UTF_8);
-        DataBuffer buffer = response.bufferFactory().wrap(responseByte);
-        return response.writeWith(Flux.just(buffer));
-    }
-
-
-}
+//package com.caimei365.user.config;
+//
+//import com.alibaba.fastjson.JSONObject;
+//import com.caimei365.user.components.RedisService;
+//import com.caimei365.user.utils.JwtUtil;
+//import lombok.extern.slf4j.Slf4j;
+//import org.apache.commons.lang.StringUtils;
+//import org.springframework.beans.factory.annotation.Autowired;
+//import org.springframework.core.io.buffer.DataBuffer;
+//import org.springframework.http.HttpHeaders;
+//import org.springframework.http.HttpStatus;
+//import org.springframework.http.server.reactive.ServerHttpRequest;
+//import org.springframework.http.server.reactive.ServerHttpResponse;
+//import org.springframework.stereotype.Component;
+//import org.springframework.web.server.ServerWebExchange;
+//import org.springframework.web.server.WebFilter;
+//import org.springframework.web.server.WebFilterChain;
+//import reactor.core.publisher.Flux;
+//import reactor.core.publisher.Mono;
+//
+//import java.nio.charset.StandardCharsets;
+//import java.util.Arrays;
+//
+///**
+// * 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已失效,重新登录。
+// *
+// * @author : Charles
+// * @date : 2021/3/30
+// */
+//@Slf4j
+//@Component
+//public class TokenFilter implements WebFilter {
+//
+//    private static final String AUTH = "X-Token";
+//    /**
+//     * 需要权限认证的接口路径
+//     */
+//    private static final String[] PERMISSION_URLS = new String[]{
+//        "/user/club/info/update",
+//        "/user/shop/info/update",
+//        "/user/club/archive/deduction"
+//    };
+//    private RedisService redisService;
+//    @Autowired
+//    public void setRedisService(RedisService redisService) {
+//        this.redisService = redisService;
+//    }
+//
+//    @Override
+//    public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
+//        ServerHttpRequest request = exchange.getRequest();
+//        ServerHttpResponse response = exchange.getResponse();
+//
+//        HttpHeaders header = request.getHeaders();
+//        String token = header.getFirst(AUTH);
+//        String url = request.getURI().getPath();
+//
+//        if (StringUtils.isBlank(token) && Arrays.asList(PERMISSION_URLS).contains(url)) {
+//            log.error("未经授权,Token为空!");
+//            return tokenErrorResponse(response, "未经授权,Token为空!");
+//        }
+//
+//        if (StringUtils.isNotBlank(token)) {
+//            String cacheToken = (String) redisService.get(token);
+//            // token续签
+//            if (StringUtils.isNotBlank(cacheToken) && !"null".equals(cacheToken) && JwtUtil.isVerify(cacheToken)) {
+//                int userId = JwtUtil.parseTokenUid(cacheToken);
+//                log.debug("Token续签,UserId:"+userId+",Token:"+token);
+//                // 再次校验token有效性
+//                if (!JwtUtil.isVerify(cacheToken)) {
+//                    // 生成token
+//                    String newToken = JwtUtil.createToken(userId);
+//                    // 将token存入redis,并设置超时时间(秒)
+//                    redisService.set(token, newToken, JwtUtil.getExpireTime());
+//                } else {
+//                    // 重新设置超时时间(秒)
+//                    redisService.expire(token, JwtUtil.getExpireTime());
+//                }
+//            } else {
+//                // 需要验证的路径
+//                if(Arrays.asList(PERMISSION_URLS).contains(url)) {
+//                    // Token失效
+//                    log.error("Token失效,token:"+token+",cacheToken:"+cacheToken);
+//                    return tokenErrorResponse(response, "Token失效,请重新登录!");
+//                }
+//            }
+//        }
+//        //            //TODO 将用户信息存放在请求header中传递给下游业务
+//        //            ServerHttpRequest.Builder mutate = request.mutate();
+//        //            mutate.header("demo-user-name", username);
+//        //            ServerHttpRequest buildReuqest = mutate.build();
+//        //
+//        //            //todo 如果响应中需要放数据,也可以放在response的header中
+//        //            response.setStatusCode(HttpStatus.OK);
+//        //            response.getHeaders().add("demo-user-name",username);
+//        //            return chain.filter(exchange.mutate()
+//        //                    .request(buildReuqest)
+//        //                    .response(response)
+//        //                    .build());
+//        return chain.filter(exchange);
+//    }
+//
+//    private Mono<Void> tokenErrorResponse(ServerHttpResponse response, String responseMsg){
+//        response.setStatusCode(HttpStatus.OK);
+//        response.getHeaders().add("Content-Type", "application/json;charset=UTF-8");
+//        JSONObject res = new JSONObject();
+//        res.put("code", -99);
+//        res.put("msg", responseMsg);
+//        byte[] responseByte = res.toJSONString().getBytes(StandardCharsets.UTF_8);
+//        DataBuffer buffer = response.bufferFactory().wrap(responseByte);
+//        return response.writeWith(Flux.just(buffer));
+//    }
+//
+//
+//}

+ 8 - 0
src/main/java/com/caimei365/user/controller/BaseApi.java

@@ -36,6 +36,14 @@ public class BaseApi {
         return "欢迎使用!";
     }
 
+    /**
+     * Token失效
+     */
+    @GetMapping("/unauthorized")
+    public ResponseJson<Void> unauthorized() {
+        return ResponseJson.error(-99, "Token失效请重新登录!", null);
+    }
+
     /**
      * 获取图片验证码
      * <p>