ChenYL 2 лет назад
Родитель
Сommit
b8ba2b7d37

+ 2 - 1
README.md

@@ -6,7 +6,6 @@ java -jar xx.jar -Dspring.profiles.active=环境 -Djasypt.encryptor.password=密
 
 待办列表
 
-* TOKEN登录拦截
 * 登录
 * 调用添加水印
 * 日志脱敏
@@ -16,6 +15,8 @@ java -jar xx.jar -Dspring.profiles.active=环境 -Djasypt.encryptor.password=密
 
 开发进度
 
+* 2023-07-30
+    - TOKEN登录拦截
 * 2023-07-28
     - 修复AOP统一返回处理在返回类型为String且具体返回值为null时报类型转换异常
     - 增加缓存caffeine的使用

+ 17 - 1
src/main/java/com/zhixinghe1/ots/atomic/service/IWechatUserService.java

@@ -12,7 +12,7 @@ public interface IWechatUserService {
      * 
      * @param openId
      */
-    void add(String openId);
+    WechatUser add(String openId);
 
     /**
      * 根据openId获取用户信息
@@ -21,4 +21,20 @@ public interface IWechatUserService {
      * @return
      */
     WechatUser getByOpenId(String openId);
+
+    /**
+     * 获取用户信息,如果不存在则新增
+     * 
+     * @param openId
+     * @return
+     */
+    WechatUser getAndAdd(String openId);
+
+    /**
+     * 根据ID获取用户信息
+     * 
+     * @param id
+     * @return
+     */
+    WechatUser getById(Long id);
 }

+ 26 - 5
src/main/java/com/zhixinghe1/ots/atomic/service/impl/WechatUserServiceImpl.java

@@ -12,14 +12,15 @@ import com.zhixinghe1.ots.atomic.service.IWechatUserService;
 public class WechatUserServiceImpl extends ServiceImpl<WeChatUserMapper, WechatUser> implements IWechatUserService {
 
     @Override
-    public void add(String openId) {
+    public WechatUser add(String openId) {
         if (openId == null) {
-            return;
+            return null;
         }
 
-        WechatUser weChatUser = new WechatUser();
-        weChatUser.setOpenId(openId);
-        save(weChatUser);
+        WechatUser wechatUser = new WechatUser();
+        wechatUser.setOpenId(openId);
+        save(wechatUser);
+        return wechatUser;
     }
 
     @Override
@@ -32,4 +33,24 @@ public class WechatUserServiceImpl extends ServiceImpl<WeChatUserMapper, WechatU
         queryWrapper.eq(WechatUser::getOpenId, openId);
         return getOne(queryWrapper);
     }
+
+    @Override
+    public WechatUser getAndAdd(String openId) {
+        WechatUser wechatUser = getByOpenId(openId);
+        if (wechatUser == null) {
+            wechatUser = add(openId);
+        }
+        return wechatUser;
+    }
+
+    @Override
+    public WechatUser getById(Long id) {
+        if (id == null) {
+            return null;
+        }
+
+        LambdaQueryWrapper<WechatUser> queryWrapper = new LambdaQueryWrapper<>();
+        queryWrapper.eq(WechatUser::getId, id);
+        return getOne(queryWrapper);
+    }
 }

+ 8 - 0
src/main/java/com/zhixinghe1/ots/common/aop/GlobalExceptionHandler.java

@@ -1,10 +1,13 @@
 package com.zhixinghe1.ots.common.aop;
 
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
 import org.springframework.web.bind.annotation.ExceptionHandler;
 import org.springframework.web.bind.annotation.RestControllerAdvice;
 
 import com.zhixinghe1.ots.common.dto.JsonResponse;
 import com.zhixinghe1.ots.common.exception.BusinessException;
+import com.zhixinghe1.ots.common.exception.LoginException;
 
 import lombok.extern.slf4j.Slf4j;
 
@@ -26,4 +29,9 @@ public class GlobalExceptionHandler {
         log.error(e.getMessage(), e);
         return JsonResponse.fail(e.getMessage());
     }
+
+    @ExceptionHandler(LoginException.class)
+    public ResponseEntity<Object> loginExceptionHandler(Exception e) {
+        return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(JsonResponse.fail(e.getMessage()));
+    }
 }

+ 3 - 1
src/main/java/com/zhixinghe1/ots/common/aop/ResponseControllerAdvice.java

@@ -2,6 +2,7 @@ package com.zhixinghe1.ots.common.aop;
 
 import org.springframework.core.MethodParameter;
 import org.springframework.http.MediaType;
+import org.springframework.http.ResponseEntity;
 import org.springframework.http.converter.HttpMessageConverter;
 import org.springframework.http.server.ServerHttpRequest;
 import org.springframework.http.server.ServerHttpResponse;
@@ -27,7 +28,7 @@ public class ResponseControllerAdvice implements ResponseBodyAdvice<Object> {
     public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType,
         Class<? extends HttpMessageConverter<?>> selectedConverterType, ServerHttpRequest request,
         ServerHttpResponse response) {
-        if (body instanceof JsonResponse) {
+        if (body instanceof JsonResponse || body instanceof ResponseEntity) {
             return body;
         }
 
@@ -40,6 +41,7 @@ public class ResponseControllerAdvice implements ResponseBodyAdvice<Object> {
                 throw new RuntimeException(e);
             }
         }
+
         return JsonResponse.success(body);
     }
 }

+ 4 - 1
src/main/java/com/zhixinghe1/ots/common/dto/JsonResponse.java

@@ -2,7 +2,9 @@ package com.zhixinghe1.ots.common.dto;
 
 import java.io.Serial;
 import java.io.Serializable;
+import java.util.Date;
 
+import com.fasterxml.jackson.annotation.JsonFormat;
 import com.zhixinghe1.ots.common.constant.BaseErrorCodeEnum;
 
 import lombok.Data;
@@ -20,7 +22,8 @@ public class JsonResponse<T> implements Serializable {
     @Serial
     private static final long serialVersionUID = -5372948141725666083L;
 
-    private long timestamp = System.currentTimeMillis();
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss:SSS", timezone = "GMT+8")
+    private Date timestamp = new Date();
 
     /**
      * 是否成功标志位 true-成功 false-失败

+ 19 - 0
src/main/java/com/zhixinghe1/ots/common/exception/LoginException.java

@@ -0,0 +1,19 @@
+package com.zhixinghe1.ots.common.exception;
+
+/**
+ * @className AuthException
+ * @description 登录校验异常
+ * @author ChenYL 
+ * @date 2023/07/29 20:27
+ * @version V1.0
+**/
+public class LoginException extends RuntimeException {
+
+    public LoginException(String message) {
+        super(message);
+    }
+
+    public static LoginException fail(String message) {
+        return new LoginException(message);
+    }
+}

+ 19 - 0
src/main/java/com/zhixinghe1/ots/common/exception/TokenException.java

@@ -0,0 +1,19 @@
+package com.zhixinghe1.ots.common.exception;
+
+/**
+ * @className AuthException
+ * @description 登录校验异常
+ * @author ChenYL
+ * @date 2023/07/29 20:27
+ * @version V1.0
+ **/
+public class TokenException extends RuntimeException {
+
+    public TokenException(String message) {
+        super(message);
+    }
+
+    public static TokenException fail(String message) {
+        return new TokenException(message);
+    }
+}

+ 23 - 2
src/main/java/com/zhixinghe1/ots/config/BizConfig.java

@@ -9,12 +9,33 @@ import lombok.Getter;
 @Configuration
 public class BizConfig {
 
+    /**
+     * 附件临时存放目录
+     */
     @Value("${biz.file.tmp.dir}")
     private String fileTmpDir;
 
+    /**
+     * 微信小程序appid
+     */
     @Value("${biz.wechat.mini-program.app-id}")
-    private String weChatMiniProgramAppId;
+    private String wechatMiniProgramAppId;
 
+    /**
+     * 微信小程序secret
+     */
     @Value("${biz.wechat.mini-program.secret}")
-    private String weChatMiniProgramSecret;
+    private String wechatMiniProgramSecret;
+
+    /**
+     * 生成token的密钥
+     */
+    @Value("${biz.token.password}")
+    private String TokenPassword;
+
+    /**
+     * token过期时间
+     */
+    @Value("${biz.token.expire:1}")
+    private Integer TokenExpire;
 }

+ 2 - 2
src/main/java/com/zhixinghe1/ots/config/FeignConfig.java

@@ -32,8 +32,8 @@ public class FeignConfig {
     @Bean("myInterceptor")
     public RequestInterceptor requestInterceptor() {
         return template -> {
-            template.query("appid", bizConfig.getWeChatMiniProgramAppId()).query("secret",
-                bizConfig.getWeChatMiniProgramSecret());
+            template.query("appid", bizConfig.getWechatMiniProgramAppId()).query("secret",
+                bizConfig.getWechatMiniProgramSecret());
         };
     }
 

+ 8 - 0
src/main/java/com/zhixinghe1/ots/config/WebMvcConfig.java

@@ -2,14 +2,22 @@ package com.zhixinghe1.ots.config;
 
 import org.springframework.context.annotation.Configuration;
 import org.springframework.format.FormatterRegistry;
+import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
 import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
 
 import com.zhixinghe1.ots.common.converter.enums.IntegerToEnumConverterFactory;
+import com.zhixinghe1.ots.interceptor.AuthInterceptor;
 
 @Configuration
 public class WebMvcConfig implements WebMvcConfigurer {
+
     @Override
     public void addFormatters(FormatterRegistry registry) {
         registry.addConverterFactory(new IntegerToEnumConverterFactory());
     }
+
+    @Override
+    public void addInterceptors(InterceptorRegistry registry) {
+        registry.addInterceptor(new AuthInterceptor()).addPathPatterns("/watermark/**", "/oss/**");
+    }
 }

+ 60 - 0
src/main/java/com/zhixinghe1/ots/interceptor/AuthInterceptor.java

@@ -0,0 +1,60 @@
+package com.zhixinghe1.ots.interceptor;
+
+import java.util.Map;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+import org.springframework.util.StringUtils;
+import org.springframework.web.servlet.HandlerInterceptor;
+
+import com.auth0.jwt.interfaces.Claim;
+import com.zhixinghe1.ots.atomic.entity.WechatUser;
+import com.zhixinghe1.ots.atomic.service.IWechatUserService;
+import com.zhixinghe1.ots.common.exception.BusinessException;
+import com.zhixinghe1.ots.common.exception.LoginException;
+import com.zhixinghe1.ots.utiis.TokenUtils;
+
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
+import lombok.extern.slf4j.Slf4j;
+
+/**
+ * @className AuthInterceptor
+ * @description 登录权限拦截
+ * @author ChenYL
+ * @date 2023/07/29 16:22
+ * @version V1.0
+ **/
+@Slf4j
+@Component
+public class AuthInterceptor implements HandlerInterceptor {
+
+    @Autowired
+    private IWechatUserService wechatUserService;
+
+    @Override
+    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
+        throws Exception {
+
+        String token = request.getHeader("Authorization");
+        if (!StringUtils.hasText(token)) {
+            throw LoginException.fail("登录校验异常,原因:没有token凭据");
+        }
+
+        Long userId = null;
+        try {
+            Map<String, Claim> verify = TokenUtils.verify(token);
+            userId = verify.get("userId").asLong();
+        } catch (Exception e) {
+            log.error("登录校验异常,token:{}", token, e);
+            throw LoginException.fail("登录校验异常");
+        }
+
+        WechatUser wechatUser = wechatUserService.getById(userId);
+        if (wechatUser == null) {
+            throw BusinessException.fail("不存在的用户");
+        }
+
+        return true;
+    }
+}

+ 16 - 6
src/main/java/com/zhixinghe1/ots/service/manager/impl/WechatMiniProgramManagerImpl.java

@@ -1,5 +1,7 @@
 package com.zhixinghe1.ots.service.manager.impl;
 
+import java.util.Calendar;
+
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.cache.CacheManager;
 import org.springframework.stereotype.Service;
@@ -7,6 +9,7 @@ import org.springframework.stereotype.Service;
 import com.zhixinghe1.ots.atomic.entity.WechatUser;
 import com.zhixinghe1.ots.atomic.service.IWechatUserService;
 import com.zhixinghe1.ots.common.exception.BusinessException;
+import com.zhixinghe1.ots.config.BizConfig;
 import com.zhixinghe1.ots.constant.CacheConstant;
 import com.zhixinghe1.ots.domain.dto.wechat.Code2SessionRequest;
 import com.zhixinghe1.ots.domain.dto.wechat.Code2SessionResponse;
@@ -14,6 +17,7 @@ import com.zhixinghe1.ots.domain.dto.wechat.LoginRequest;
 import com.zhixinghe1.ots.feign.WechatMiniProgramFeign;
 import com.zhixinghe1.ots.service.manager.IWechatMiniProgramManager;
 import com.zhixinghe1.ots.utiis.CacheUtils;
+import com.zhixinghe1.ots.utiis.TokenUtils;
 
 import lombok.extern.slf4j.Slf4j;
 
@@ -33,6 +37,9 @@ public class WechatMiniProgramManagerImpl implements IWechatMiniProgramManager {
     @Autowired
     private CacheManager cacheManager;
 
+    @Autowired
+    private BizConfig bizConfig;
+
     @Override
     public String login(LoginRequest request) {
         Code2SessionRequest code2SessionRequest = new Code2SessionRequest();
@@ -43,19 +50,22 @@ public class WechatMiniProgramManagerImpl implements IWechatMiniProgramManager {
             throw BusinessException.fail(loginResponse.getErrMsg());
         }
 
-        // 新增加用户记录
-        WechatUser wechatUser = wechatUserService.getByOpenId(loginResponse.getOpenId());
+        // 获取用户记录
+        WechatUser wechatUser = wechatUserService.getAndAdd(loginResponse.getOpenId());
         if (wechatUser == null) {
-            wechatUserService.add(loginResponse.getOpenId());
+            throw BusinessException.fail(String.format("不存在的用户,且无法新增,openid: %s", loginResponse.getOpenId()));
         }
 
         // 缓存微信用户对应的session_key
         CacheUtils.put(CacheConstant.WECHAT_MINI_PROGRAM_SESSION_KEY, loginResponse.getOpenId(),
             loginResponse.getSessionKey());
 
-        // TODO 颁发token
-        CacheUtils.put(CacheConstant.SYSTEM_USER_TOKEN, loginResponse.getOpenId(), loginResponse.getSessionKey());
+        // 创建token并缓存
+        Calendar instance = Calendar.getInstance();
+        instance.add(Calendar.DATE, bizConfig.getTokenExpire());
+        String token = TokenUtils.createToken(wechatUser.getId(), instance.getTime());
+        CacheUtils.put(CacheConstant.SYSTEM_USER_TOKEN, loginResponse.getOpenId(), token);
 
-        return null;
+        return token;
     }
 }

+ 57 - 0
src/main/java/com/zhixinghe1/ots/utiis/TokenUtils.java

@@ -0,0 +1,57 @@
+package com.zhixinghe1.ots.utiis;
+
+import java.util.Date;
+import java.util.Map;
+
+import com.auth0.jwt.JWT;
+import com.auth0.jwt.JWTVerifier;
+import com.auth0.jwt.algorithms.Algorithm;
+import com.auth0.jwt.interfaces.Claim;
+import com.zhixinghe1.ots.config.BizConfig;
+
+/**
+ * @className JwtUtils
+ * @description Token凭证工具
+ * @author ChenYL
+ * @date 2023/07/29 16:25
+ * @version V1.0
+ **/
+public class TokenUtils {
+
+    /**
+     * token算法
+     */
+    private static Algorithm tokenAlgorithm;
+
+    /**
+     * token校验工具
+     */
+    private static JWTVerifier jwtVerifier;
+
+    static {
+        BizConfig bizConfig = SpringUtils.getBean(BizConfig.class);
+        tokenAlgorithm = Algorithm.HMAC256(bizConfig.getTokenPassword());
+        jwtVerifier = JWT.require(tokenAlgorithm).build();
+    }
+
+    /**
+     * 创建token
+     * 
+     * @param userId
+     * @param expiresAt
+     * @return
+     */
+    public static String createToken(Long userId, Date expiresAt) {
+        return JWT.create().withClaim("userId", userId).withExpiresAt(expiresAt).sign(tokenAlgorithm);
+    }
+
+    /**
+     * 校验token
+     * 
+     * @param token
+     * @return
+     */
+    public static Map<String, Claim> verify(String token) {
+        return jwtVerifier.verify(token).getClaims();
+    }
+}

+ 3 - 0
src/main/resources/application.yml

@@ -26,3 +26,6 @@ biz:
       url: https://api.weixin.qq.com
       app-id: ENC(qw0NNe6VoMWvpoGKZLitIJ8HWMHBF+5FbCAVQyJGdxREenOAaEB1oQ==)
       secret: ENC(i2ZFfTtFRvAUqpXSW4CQd6HEAMTc0Ltc1dVbhDiIxe4a5H7BbTV3bfiGhFvtRszOMIgyQDoxeZI=)
+  token:
+    expire: 7
+    password: 12123