Browse Source

feat: 增加jwt集成

Marko 3 years ago
parent
commit
87f4033f04

+ 18 - 5
abi-cloud-qr-platform-server/pom.xml

@@ -14,6 +14,7 @@
     <properties>
         <maven.compiler.source>8</maven.compiler.source>
         <maven.compiler.target>8</maven.compiler.target>
+        <jwt.version>3.4.0</jwt.version>
     </properties>
 
     <dependencies>
@@ -53,11 +54,11 @@
             <artifactId>javax.mail</artifactId>
             <version>1.6.0</version>
         </dependency>
-        <dependency>
-            <groupId>com.abi.sms</groupId>
-            <artifactId>sms-center-client</artifactId>
-            <version>1.0-SNAPSHOT</version>
-        </dependency>
+<!--        <dependency>-->
+<!--            <groupId>com.abi.sms</groupId>-->
+<!--            <artifactId>sms-center-client</artifactId>-->
+<!--            <version>1.0-SNAPSHOT</version>-->
+<!--        </dependency>-->
         <dependency>
             <groupId>com.abi.bees</groupId>
             <artifactId>sms-center-api</artifactId>
@@ -164,6 +165,18 @@
             <artifactId>redisson-spring-boot-starter</artifactId>
             <version> 3.15.4</version>
         </dependency>
+
+        <dependency>
+            <groupId>com.abi.base</groupId>
+            <artifactId>abi-base-foundation</artifactId>
+            <version>1.2.3.2</version>
+        </dependency>
+
+        <dependency>
+            <groupId>com.auth0</groupId>
+            <artifactId>java-jwt</artifactId>
+            <version>${jwt.version}</version>
+        </dependency>
     </dependencies>
     <build>
         <plugins>

+ 20 - 0
abi-cloud-qr-platform-server/src/main/java/com/abi/qms/platform/annotation/PassToken.java

@@ -0,0 +1,20 @@
+package com.abi.qms.platform.annotation;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * @className: com.abi.crm.annotation-> PassToken
+ * @description: 不需要Token认证
+ * @author: Marko.W
+ * @createDate: 2021-05-18 11:10
+ * @version: 1.0
+ * @todo:
+ */
+@Target({ElementType.METHOD, ElementType.TYPE})
+@Retention(RetentionPolicy.RUNTIME)
+public @interface PassToken {
+    boolean required() default true;
+}

+ 20 - 0
abi-cloud-qr-platform-server/src/main/java/com/abi/qms/platform/annotation/UserLoginToken.java

@@ -0,0 +1,20 @@
+package com.abi.qms.platform.annotation;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * @className: com.abi.crm.annotation-> UserLoginToken
+ * @description: 需要登录才能进行操作的注解
+ * @author: Marko.W
+ * @createDate: 2021-05-18 11:16
+ * @version: 1.0
+ * @todo:
+ */
+@Target({ElementType.METHOD, ElementType.TYPE})
+@Retention(RetentionPolicy.RUNTIME)
+public @interface UserLoginToken {
+    boolean required() default true;
+}

+ 2 - 1
abi-cloud-qr-platform-server/src/main/java/com/abi/qms/platform/controller/console/LoginController.java

@@ -1,5 +1,6 @@
 package com.abi.qms.platform.controller.console;
 
+import com.abi.qms.platform.annotation.PassToken;
 import com.abi.qms.platform.dto.req.LoginReq;
 import com.abi.qms.platform.dto.req.ResetPasswordReq;
 import com.abi.qms.platform.dto.req.UpdatePasswordReq;
@@ -41,7 +42,7 @@ public class LoginController {
      */
     @PostMapping("login")
     @ApiOperation("登录")
-    @IgnoreToken
+    @PassToken
     public BaseResponse loginQms(@Validated @RequestBody LoginReq req) {
         LoginRes login = loginService.login(req);
         return BaseResponse.create(login);

+ 11 - 0
abi-cloud-qr-platform-server/src/main/java/com/abi/qms/platform/infrastructure/constant/RedisKey.java

@@ -11,4 +11,15 @@ public class RedisKey {
 
     //用户key是token,value是userId
     public static final String USER_TOKEN_ID = "USER_TOKEN_ID_";
+
+    /**
+     * 用户登录token key
+     */
+    public static final String TOKEN_KEY = "qms:user:token";
+
+    /**
+     * 用户登录token userId
+     */
+    public static final String TOKEN_KEY_USER_ID = "qms:token:user:";
+
 }

+ 15 - 0
abi-cloud-qr-platform-server/src/main/java/com/abi/qms/platform/infrastructure/constant/RsaKey.java

@@ -0,0 +1,15 @@
+package com.abi.qms.platform.infrastructure.constant;
+
+/**
+ * @className: com.abi.crm.infrastructure.constant-> RsaKey
+ * @description:
+ * @author: Marko.W
+ * @createDate: 2021-05-18 15:53
+ * @version: 1.0
+ * @todo:
+ */
+public class RsaKey {
+
+    public static final String RSA_PUBLIC_KEY = "rsaPublicKey";
+    public static final String RSA_PRIVATE_KEY = "rsaPrivateKey";
+}

+ 72 - 32
abi-cloud-qr-platform-server/src/main/java/com/abi/qms/platform/infrastructure/interceptor/TokenInterceptor.java

@@ -1,62 +1,102 @@
 package com.abi.qms.platform.infrastructure.interceptor;
 
+import com.abi.base.foundation.util.RedisClient;
+import com.abi.qms.platform.annotation.PassToken;
 import com.abi.qms.platform.dao.entity.UserInfo;
-import com.abi.qms.platform.infrastructure.exception.ErrorCodeEnum;
-import com.abi.qms.platform.dto.req.TokenReq;
 import com.abi.qms.platform.infrastructure.util.UserUtil;
-import com.abi.qms.platform.service.LoginService;
-import com.abi.task.common.annotation.IgnoreToken;
 import com.abi.task.common.api.exception.BusinessException;
-import org.apache.commons.lang3.StringUtils;
+import com.auth0.jwt.interfaces.DecodedJWT;
+import com.auth0.jwt.JWT;
+import com.auth0.jwt.exceptions.JWTDecodeException;
+import com.auth0.jwt.interfaces.Claim;
+import org.apache.commons.lang3.ObjectUtils;
+import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.context.annotation.Configuration;
 import org.springframework.web.method.HandlerMethod;
+import org.springframework.web.servlet.HandlerInterceptor;
 import org.springframework.web.servlet.ModelAndView;
-import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
-
-import javax.annotation.Resource;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
+import java.lang.reflect.Method;
+import java.text.MessageFormat;
+import java.util.Map;
+import java.util.concurrent.TimeUnit;
+
+import static com.abi.qms.platform.infrastructure.constant.RedisKey.TOKEN_KEY;
+import static com.abi.qms.platform.infrastructure.util.JwtTokenUtils.checkToken;
 
 /**
  * @author zd
  * @date 2021/1/20 17:57
  */
 @Configuration
-public class TokenInterceptor extends HandlerInterceptorAdapter {
+public class TokenInterceptor implements HandlerInterceptor {
+
+    @Autowired
+    private RedisClient redisClient;
 
-    @Resource
-    private LoginService loginService;
-    
     @Override
-    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
-        IgnoreToken ignoreToken=null;
-        if (handler instanceof HandlerMethod) {
-            ignoreToken = ((HandlerMethod) handler).getMethodAnnotation(IgnoreToken.class);
-        }else {
+    public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object object) throws Exception {
+        // 如果不是映射到方法直接通过
+        if (!(object instanceof HandlerMethod)) {
             return true;
         }
-
-        if(ignoreToken != null){
-            return true;
+        HandlerMethod handlerMethod = (HandlerMethod) object;
+        Method method = handlerMethod.getMethod();
+        //检查是否有passtoken注释,有则跳过认证
+        if (method.isAnnotationPresent(PassToken.class)) {
+            PassToken passToken = method.getAnnotation(PassToken.class);
+            if (passToken.required()) {
+                return true;
+            }
         }
-        String token = request.getHeader("Authorization");
-        if(StringUtils.isBlank(token)){
-            token = request.getParameter("auth_token");
+        //检查有没有需要用户权限的注解
+        // 从 http 请求头中取出 token
+        String token = httpServletRequest.getHeader("Authorization");
+
+        // 执行认证
+        if (ObjectUtils.isEmpty(token)||!token.startsWith("Bearer ")) {
+            throw new BusinessException(401,"token 信息错误,认证失败");
         }
+        token = token.replace("Bearer ","");
+        checkToken(token);
+        // 获取 token 中的 user id
+        String userId;
+        try {
+            DecodedJWT jwt = JWT.decode(token);
+            Map<String, Claim> userMap = jwt.getClaims();
+            userId = jwt.getAudience().get(0);
+            String loginType = userMap.get("loginType").asString();
+            String redisTokenKey = MessageFormat.format("{0}:{1}",TOKEN_KEY,loginType,token);
+            Object flag = redisClient.get(redisTokenKey);
+            if (ObjectUtils.isEmpty(flag)){
+                throw new BusinessException(401,"token已过期,请重新登录");
+            }
+            redisClient.expire(redisTokenKey,2, TimeUnit.HOURS);
 
-        TokenReq tokenReq = new TokenReq();
-        tokenReq.setToken(token);
-        UserInfo userInfo = loginService.validateToken(tokenReq);
-        if(userInfo != null){
-            UserUtil.setUser(userInfo);
-        }else{
-            throw new BusinessException(ErrorCodeEnum.TOKEN_TIME_OUT.getCode(),ErrorCodeEnum.TOKEN_TIME_OUT.getMessage());
+            String miniAppCode, openId, mobile;
+            String userName = userMap.get("userName").asString();
+            UserInfo userInfo = new UserInfo();
+            userInfo.setId(Long.valueOf(userId));
+            userInfo.setUserName(userName);
+            UserUtil.setUser(new UserInfo());
+        } catch (JWTDecodeException j) {
+            throw new BusinessException(401,"用户认证失败");
         }
         return true;
+
+    }
+
+    @Override
+    public void postHandle(HttpServletRequest httpServletRequest,
+                           HttpServletResponse httpServletResponse,
+                           Object o, ModelAndView modelAndView) throws Exception {
+
     }
 
     @Override
-    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
-        UserUtil.releaseUser();
+    public void afterCompletion(HttpServletRequest httpServletRequest,
+                                HttpServletResponse httpServletResponse,
+                                Object o, Exception e) throws Exception {
     }
 }

+ 219 - 0
abi-cloud-qr-platform-server/src/main/java/com/abi/qms/platform/infrastructure/util/JwtTokenUtils.java

@@ -0,0 +1,219 @@
+package com.abi.qms.platform.infrastructure.util;
+
+
+import com.abi.base.foundation.exception.BusinessException;
+import com.abi.qms.platform.dao.entity.UserInfo;
+import com.auth0.jwt.JWT;
+import com.auth0.jwt.JWTVerifier;
+import com.auth0.jwt.algorithms.Algorithm;
+import com.auth0.jwt.exceptions.AlgorithmMismatchException;
+import com.auth0.jwt.exceptions.InvalidClaimException;
+import com.auth0.jwt.exceptions.TokenExpiredException;
+import com.auth0.jwt.interfaces.Claim;
+import com.auth0.jwt.interfaces.DecodedJWT;
+import lombok.extern.slf4j.Slf4j;
+
+import java.security.interfaces.RSAPrivateKey;
+import java.security.interfaces.RSAPublicKey;
+import java.util.*;
+
+
+import static com.abi.qms.platform.infrastructure.constant.RsaKey.RSA_PRIVATE_KEY;
+import static com.abi.qms.platform.infrastructure.constant.RsaKey.RSA_PUBLIC_KEY;
+import static com.abi.qms.platform.runner.CustomApplicationRunner.globalRsaKeyMap;
+
+/**
+ * @className: com.abi.crm.infrastructure.utils-> JwtTokenUtils
+ * @description:
+ * @author: Marko.W
+ * @createDate: 2021-05-17 18:52
+ * @version: 1.0
+ * @todo:
+ */
+@Slf4j
+public class JwtTokenUtils{
+
+    //默认两个小时过期
+    private Long expired = 7200L;
+
+
+    public static Boolean checkToken(String token){
+        try{
+            JWTVerifier jwtVerifier = JWT.require(Algorithm.RSA512((RSAPublicKey)globalRsaKeyMap.get(RSA_PUBLIC_KEY),
+                    (RSAPrivateKey)globalRsaKeyMap.get(RSA_PRIVATE_KEY))).build();
+            jwtVerifier.verify(token);
+            return true;
+        }catch (Exception e){
+            log.info("token校验失败",e);
+            throw new BusinessException(401,"token认证失败");
+        }
+    }
+
+
+    /**
+     * 根据console端user用户生成token信息
+     * @param user
+     * @return
+     */
+    public static String getToken(UserInfo user){
+        try {
+            String token = JWT.create()
+                    .withKeyId(UUIDutils.getUUID())
+                    .withExpiresAt(new Date(1721395275632L))
+                    .withAudience(user.getId().toString())
+                    .withClaim("userId",user.getId())
+                    .withClaim("userName",user.getUserName())
+                    .withClaim("roles","")
+                    .sign(Algorithm.RSA512((RSAPublicKey)globalRsaKeyMap.get(RSA_PUBLIC_KEY),
+                            (RSAPrivateKey)globalRsaKeyMap.get(RSA_PRIVATE_KEY)));
+            return token;
+        } catch (Exception e) {
+            log.warn("生成token异常",e);
+            throw new BusinessException(500,"生成token异常");
+        }
+    }
+
+
+    private static void verifyAlgorithm(DecodedJWT jwt, Algorithm expectedAlgorithm) throws AlgorithmMismatchException {
+        if (!expectedAlgorithm.getName().equals(jwt.getAlgorithm())) {
+            throw new AlgorithmMismatchException("The provided Algorithm doesn't match the one defined in the JWT's Header.");
+        }
+    }
+
+
+    private void verifyClaims(DecodedJWT jwt, Map<String, Object> claims) throws TokenExpiredException, InvalidClaimException {
+        Iterator var3 = claims.entrySet().iterator();
+
+        while(var3.hasNext()) {
+            Map.Entry<String, Object> entry = (Map.Entry)var3.next();
+            String var5 = (String)entry.getKey();
+            byte var6 = -1;
+            switch(var5.hashCode()) {
+                case 96944:
+                    if (var5.equals("aud")) {
+                        var6 = 0;
+                    }
+                    break;
+                case 100893:
+                    if (var5.equals("exp")) {
+                        var6 = 1;
+                    }
+                    break;
+                case 104028:
+                    if (var5.equals("iat")) {
+                        var6 = 2;
+                    }
+                    break;
+                case 104585:
+                    if (var5.equals("iss")) {
+                        var6 = 4;
+                    }
+                    break;
+                case 105567:
+                    if (var5.equals("jti")) {
+                        var6 = 5;
+                    }
+                    break;
+                case 108850:
+                    if (var5.equals("nbf")) {
+                        var6 = 3;
+                    }
+                    break;
+                case 114240:
+                    if (var5.equals("sub")) {
+                        var6 = 6;
+                    }
+            }
+
+            switch(var6) {
+                case 0:
+                    this.assertValidAudienceClaim(jwt.getAudience(), (List)entry.getValue());
+                    break;
+                case 1:
+                    this.assertValidDateClaim(jwt.getExpiresAt(), (Long)entry.getValue(), true);
+                    break;
+                case 2:
+                    this.assertValidDateClaim(jwt.getIssuedAt(), (Long)entry.getValue(), false);
+                    break;
+                case 3:
+                    this.assertValidDateClaim(jwt.getNotBefore(), (Long)entry.getValue(), false);
+                    break;
+                case 4:
+                    this.assertValidStringClaim((String)entry.getKey(), jwt.getIssuer(), (String)entry.getValue());
+                    break;
+                case 5:
+                    this.assertValidStringClaim((String)entry.getKey(), jwt.getId(), (String)entry.getValue());
+                    break;
+                case 6:
+                    this.assertValidStringClaim((String)entry.getKey(), jwt.getSubject(), (String)entry.getValue());
+                    break;
+                default:
+                    this.assertValidClaim(jwt.getClaim((String)entry.getKey()), (String)entry.getKey(), entry.getValue());
+            }
+        }
+
+    }
+
+    private void assertValidClaim(Claim claim, String claimName, Object value) {
+        boolean isValid = false;
+        if (value instanceof String) {
+            isValid = value.equals(claim.asString());
+        } else if (value instanceof Integer) {
+            isValid = value.equals(claim.asInt());
+        } else if (value instanceof Long) {
+            isValid = value.equals(claim.asLong());
+        } else if (value instanceof Boolean) {
+            isValid = value.equals(claim.asBoolean());
+        } else if (value instanceof Double) {
+            isValid = value.equals(claim.asDouble());
+        } else if (value instanceof Date) {
+            isValid = value.equals(claim.asDate());
+        } else if (value instanceof Object[]) {
+            List<Object> claimArr = Arrays.asList((Object[])claim.as(Object[].class));
+            List<Object> valueArr = Arrays.asList((Object[])((Object[])value));
+            isValid = claimArr.containsAll(valueArr);
+        }
+
+        if (!isValid) {
+            throw new InvalidClaimException(String.format("The Claim '%s' value doesn't match the required one.", claimName));
+        }
+    }
+
+    private void assertValidStringClaim(String claimName, String value, String expectedValue) {
+        if (!expectedValue.equals(value)) {
+            throw new InvalidClaimException(String.format("The Claim '%s' value doesn't match the required one.", claimName));
+        }
+    }
+
+    private void assertValidDateClaim(Date date, long leeway, boolean shouldBeFuture) {
+        Date today = new Date();
+        today.setTime((long)Math.floor((double)(today.getTime() / 1000L * 1000L)));
+        if (shouldBeFuture) {
+            this.assertDateIsFuture(date, leeway, today);
+        } else {
+            this.assertDateIsPast(date, leeway, today);
+        }
+
+    }
+
+    private void assertDateIsFuture(Date date, long leeway, Date today) {
+        today.setTime(today.getTime() - leeway * 1000L);
+        if (date != null && today.after(date)) {
+            throw new TokenExpiredException(String.format("The Token has expired on %s.", date));
+        }
+    }
+
+    private void assertDateIsPast(Date date, long leeway, Date today) {
+        today.setTime(today.getTime() + leeway * 1000L);
+        if (date != null && today.before(date)) {
+            throw new InvalidClaimException(String.format("The Token can't be used before %s.", date));
+        }
+    }
+
+    private void assertValidAudienceClaim(List<String> audience, List<String> value) {
+        if (audience == null || !audience.containsAll(value)) {
+            throw new InvalidClaimException("The Claim 'aud' value doesn't contain the required audience.");
+        }
+    }
+
+}

+ 118 - 0
abi-cloud-qr-platform-server/src/main/java/com/abi/qms/platform/infrastructure/util/RSAUtils.java

@@ -0,0 +1,118 @@
+package com.abi.qms.platform.infrastructure.util;
+
+import cn.hutool.core.io.FileUtil;
+import com.google.common.collect.Maps;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.codec.binary.Base64;
+import org.springframework.core.io.ClassPathResource;
+import org.springframework.security.rsa.crypto.KeyStoreKeyFactory;
+
+import javax.crypto.Cipher;
+import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+import java.nio.charset.Charset;
+import java.security.*;
+import java.security.interfaces.RSAKey;
+import java.security.interfaces.RSAPrivateKey;
+import java.security.interfaces.RSAPublicKey;
+import java.security.spec.PKCS8EncodedKeySpec;
+import java.security.spec.X509EncodedKeySpec;
+import java.util.Map;
+
+import static com.abi.qms.platform.infrastructure.constant.RsaKey.RSA_PRIVATE_KEY;
+import static com.abi.qms.platform.infrastructure.constant.RsaKey.RSA_PUBLIC_KEY;
+
+
+/**
+ * @className: com.abi.crm.infrastructure.utils-> RSAUtils
+ * @description:
+ * @author: Marko.W
+ * @createDate: 2021-05-18 14:12
+ * @version: 1.0
+ * @todo:
+ */
+@Slf4j
+public class RSAUtils {
+
+    private  final static String publicKeyPath = "static/publicKey.txt";
+    private  final static String privateKeyPath = "static/cxs-jwt.jks";
+
+
+    private static String getKey(String filename) throws IOException {
+        ClassPathResource resourcePublicKey = new ClassPathResource(filename);
+        try {
+            return FileUtil.readString(resourcePublicKey.getFile(), Charset.defaultCharset());
+        } catch (IOException e) {
+            log.warn("读取文件失败",e);
+            throw e;
+        }
+    }
+    public static Map<String, RSAKey> getKey() throws IOException, GeneralSecurityException {
+        Map<String,RSAKey> map = Maps.newHashMap();
+        ClassPathResource resource = new ClassPathResource(privateKeyPath);
+        KeyStoreKeyFactory keyStoreKeyFactory = new KeyStoreKeyFactory(resource, "1qaz#EDC".toCharArray());
+        //密钥对(公钥对应私钥)
+        KeyPair keyPair = keyStoreKeyFactory.getKeyPair("cxs-jwt");
+        map.put(RSA_PUBLIC_KEY,(RSAPublicKey)keyPair.getPublic());
+        map.put(RSA_PRIVATE_KEY,(RSAPrivateKey) keyPair.getPrivate());
+        return map;
+    }
+
+    public static RSAPrivateKey getPrivateKeyFromString(String key) throws IOException, GeneralSecurityException {
+        String privateKeyPEM = key;
+        privateKeyPEM = privateKeyPEM.replace("-----BEGIN PRIVATE KEY-----\n", "");
+        privateKeyPEM = privateKeyPEM.replace("-----END PRIVATE KEY-----", "");
+        byte[] encoded = Base64.decodeBase64(privateKeyPEM);
+        KeyFactory kf = KeyFactory.getInstance("RSA");
+        PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(encoded);
+        return (RSAPrivateKey) kf.generatePrivate(keySpec);
+    }
+
+
+    public static RSAPublicKey getPublicKey() throws IOException, GeneralSecurityException {
+        return getPublicKeyFromString(getKey(publicKeyPath));
+    }
+
+    public static RSAPublicKey getPublicKeyFromString(String key) throws IOException, GeneralSecurityException {
+        String publicKeyPEM = key;
+        publicKeyPEM = publicKeyPEM.replace("-----BEGIN PUBLIC KEY-----\n", "");
+        publicKeyPEM = publicKeyPEM.replace("-----END PUBLIC KEY-----", "");
+        byte[] encoded = Base64.decodeBase64(publicKeyPEM);
+        KeyFactory kf = KeyFactory.getInstance("RSA");
+        return (RSAPublicKey) kf.generatePublic(new X509EncodedKeySpec(encoded));
+    }
+
+
+
+    public static String sign(PrivateKey privateKey, String message) throws NoSuchAlgorithmException, InvalidKeyException, SignatureException, UnsupportedEncodingException {
+        Signature sign = Signature.getInstance("SHA1withRSA");
+        sign.initSign(privateKey);
+        sign.update(message.getBytes("UTF-8"));
+        return new String(Base64.encodeBase64(sign.sign()), "UTF-8");
+    }
+
+
+    public static boolean verify(PublicKey publicKey, String message, String signature) throws SignatureException, NoSuchAlgorithmException, UnsupportedEncodingException, InvalidKeyException {
+        Signature sign = Signature.getInstance("SHA1withRSA");
+        sign.initVerify(publicKey);
+        try {
+            sign.update(message.getBytes("UTF-8"));
+        } catch (UnsupportedEncodingException e) {
+            e.printStackTrace();
+        }
+        return sign.verify(Base64.decodeBase64(signature.getBytes("UTF-8")));
+    }
+
+    public static String encrypt(String rawText, PublicKey publicKey) throws IOException, GeneralSecurityException {
+        Cipher cipher = Cipher.getInstance("RSA");
+        cipher.init(Cipher.ENCRYPT_MODE, publicKey);
+        return Base64.encodeBase64String(cipher.doFinal(rawText.getBytes("UTF-8")));
+    }
+
+    public static String decrypt(String cipherText, PrivateKey privateKey) throws IOException, GeneralSecurityException {
+        Cipher cipher = Cipher.getInstance("RSA");
+        cipher.init(Cipher.DECRYPT_MODE, privateKey);
+        return new String(cipher.doFinal(Base64.decodeBase64(cipherText)), "UTF-8");
+    }
+
+}

+ 39 - 0
abi-cloud-qr-platform-server/src/main/java/com/abi/qms/platform/runner/CustomApplicationRunner.java

@@ -0,0 +1,39 @@
+package com.abi.qms.platform.runner;
+
+import com.abi.qms.platform.infrastructure.util.RSAUtils;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.boot.ApplicationArguments;
+import org.springframework.boot.ApplicationRunner;
+import org.springframework.stereotype.Component;
+
+import java.security.interfaces.RSAKey;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+import static com.abi.qms.platform.infrastructure.constant.RsaKey.RSA_PRIVATE_KEY;
+import static com.abi.qms.platform.infrastructure.constant.RsaKey.RSA_PUBLIC_KEY;
+
+/**
+ * @className: com.abi.crm.runner-> CustomApplicationRunner
+ * @description:
+ * @author: Marko.W
+ * @createDate: 2021-05-18 15:42
+ * @version: 1.0
+ * @todo:
+ */
+@Component
+@Slf4j
+public class CustomApplicationRunner implements ApplicationRunner {
+    public static ConcurrentHashMap<String, RSAKey> globalRsaKeyMap = new ConcurrentHashMap<>();
+    /**
+     * 项目启动时  加载公钥私钥到本地缓存
+     * @param args
+     * @throws Exception
+     */
+    @Override
+    public void run(ApplicationArguments args) throws Exception {
+        Map<String, RSAKey> keyMap = RSAUtils.getKey();
+        globalRsaKeyMap.put(RSA_PUBLIC_KEY,keyMap.get(RSA_PUBLIC_KEY));
+        globalRsaKeyMap.put(RSA_PRIVATE_KEY,keyMap.get(RSA_PRIVATE_KEY));
+    }
+}

+ 33 - 44
abi-cloud-qr-platform-server/src/main/java/com/abi/qms/platform/service/impl/LoginServiceImpl.java

@@ -1,6 +1,7 @@
 package com.abi.qms.platform.service.impl;
 
 import cn.hutool.core.util.ObjectUtil;
+import com.abi.base.foundation.util.RedisClient;
 import com.abi.qms.platform.dao.entity.UserInfo;
 import com.abi.qms.platform.dao.entity.UserRole;
 import com.abi.qms.platform.dao.mapper.UserInfoMapper;
@@ -24,8 +25,15 @@ import lombok.extern.slf4j.Slf4j;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
+import org.springframework.util.StringUtils;
 
+import java.text.MessageFormat;
 import java.util.Objects;
+import java.util.concurrent.TimeUnit;
+
+import static com.abi.qms.platform.infrastructure.constant.RedisKey.TOKEN_KEY;
+import static com.abi.qms.platform.infrastructure.constant.RedisKey.TOKEN_KEY_USER_ID;
+import static com.abi.qms.platform.infrastructure.util.JwtTokenUtils.getToken;
 
 /**
  * <p>
@@ -45,8 +53,7 @@ public class LoginServiceImpl extends ServiceImpl<UserInfoMapper, UserInfo> impl
     private UserRoleMapper userRoleMapper;
 
     @Autowired
-    private RedisUtils redisUtils;
-
+    private RedisClient redisClient;
 
     @Override
     @Transactional(rollbackFor = Exception.class)
@@ -62,24 +69,12 @@ public class LoginServiceImpl extends ServiceImpl<UserInfoMapper, UserInfo> impl
                 throw new BusinessException("该用户的角色不存在");
             }
             UserUtil.setUser(userInfo);
-            String uuid = UUIDutils.getUUID();
-            //token为key,id为value。过期时间30天
-            redisUtils.set(RedisKey.USER_TOKEN_ID + ":" + uuid, one.getId(), BaseFinal.REDIS_USER_EXPIRE_TIME);
-            //异地登录状态
-            if (redisUtils.exists(RedisKey.USER_ID_TOKEN + ":" + one.getId())) {
-                //有key,value为null的情况;就把key删掉
-                Object loginkey = redisUtils.get(RedisKey.USER_ID_TOKEN + ":" + one.getId());
-                if (Objects.nonNull(loginkey)) {
-                    redisUtils.del(RedisKey.USER_TOKEN_ID + ":" + loginkey.toString());
-                } else {
-                    redisUtils.del(RedisKey.USER_ID_TOKEN + ":" + one.getId());
-                }
-            }
-            ;
-            //登录状态
-            redisUtils.set(RedisKey.USER_ID_TOKEN + ":" + one.getId(), uuid, BaseFinal.REDIS_USER_EXPIRE_TIME);
+            String token = getToken(userInfo);
+            String redisTokenKey = MessageFormat.format("{0}:{1}",TOKEN_KEY,token);
+            redisClient.set(redisTokenKey,true,2, TimeUnit.HOURS);
+            redisClient.set(TOKEN_KEY_USER_ID+userInfo.getId(),true,2,TimeUnit.DAYS);
             LoginRes build = LoginRes.builder()
-                    .token(uuid)
+                    .token(token)
                     .id(one.getId())
                     .roleCode(one.getRoleCode())
                     .build();
@@ -98,13 +93,15 @@ public class LoginServiceImpl extends ServiceImpl<UserInfoMapper, UserInfo> impl
             userInfoMapper.updateById(userInfo);
             UserUtil.releaseUser();
             //删除redis用户token信息
-            if (redisUtils.exists(RedisKey.USER_ID_TOKEN + ":" + req.getId())) {
-                //有key,value为null的情况;就把key删掉
-                Object loginkey = redisUtils.get(RedisKey.USER_ID_TOKEN + ":" + req.getId());
-                if (Objects.nonNull(loginkey)) {
-                    redisUtils.del(RedisKey.USER_TOKEN_ID + ":" + loginkey.toString());
-                }
-                redisUtils.del(RedisKey.USER_ID_TOKEN + ":" + req.getId());
+            String userToken = TOKEN_KEY_USER_ID + userInfo.getId();
+            if (!redisClient.hasKey(userToken)) {
+                return ;
+            }
+            String token  = redisClient.get(userToken);
+            redisClient.delete(TOKEN_KEY_USER_ID + userInfo.getId());
+            if (!StringUtils.isEmpty(token)){
+                String redisTokenKey = MessageFormat.format("{0}:{1}",TOKEN_KEY,token);
+                redisClient.delete(redisTokenKey);
             }
         }
     }
@@ -117,14 +114,15 @@ public class LoginServiceImpl extends ServiceImpl<UserInfoMapper, UserInfo> impl
         if (oldPassword.equals(userInfo.getPassword())) {
             userInfo.setPassword(req.getNewPassword());
             userInfoMapper.updateById(userInfo);
-            //删除redis用户token信息
-            if (redisUtils.exists(RedisKey.USER_ID_TOKEN + ":" + req.getId())) {
-                //有key,value为null的情况;就把key删掉
-                Object loginkey = redisUtils.get(RedisKey.USER_ID_TOKEN + ":" + req.getId());
-                if (Objects.nonNull(loginkey)) {
-                    redisUtils.del(RedisKey.USER_TOKEN_ID + ":" + loginkey.toString());
-                }
-                redisUtils.del(RedisKey.USER_ID_TOKEN + ":" + req.getId());
+            String userToken = TOKEN_KEY_USER_ID + userInfo.getId();
+            if (!redisClient.hasKey(userToken)) {
+                return ;
+            }
+            String token  = redisClient.get(userToken);
+            redisClient.delete(TOKEN_KEY_USER_ID + userInfo.getId());
+            if (!StringUtils.isEmpty(token)){
+                String redisTokenKey = MessageFormat.format("{0}:{1}",TOKEN_KEY,token);
+                redisClient.delete(redisTokenKey);
             }
         } else {
             throw new BusinessException("请输入正确的密码");
@@ -133,16 +131,7 @@ public class LoginServiceImpl extends ServiceImpl<UserInfoMapper, UserInfo> impl
 
     @Override
     public UserInfo validateToken(TokenReq req) {
-        Object uid = redisUtils.get(RedisKey.USER_TOKEN_ID + ":" + req.getToken());
-        if (Objects.nonNull(uid)) {
-            UserInfo userInfo = getById(uid.toString());
-            if (userInfo != null) {
-                //刷新token
-                redisUtils.set(RedisKey.USER_TOKEN_ID + ":" + req.getToken(), userInfo.getId(), BaseFinal.REDIS_USER_EXPIRE_TIME);
-                redisUtils.set(RedisKey.USER_ID_TOKEN + ":" + userInfo.getId(), req.getToken(), BaseFinal.REDIS_USER_EXPIRE_TIME);
-            }
-            return userInfo;
-        }
+
         return null;
     }
 }

BIN
abi-cloud-qr-platform-server/src/main/resources/static/cxs-jwt.jks


+ 9 - 0
abi-cloud-qr-platform-server/src/main/resources/static/publicKey.txt

@@ -0,0 +1,9 @@
+-----BEGIN PUBLIC KEY-----
+MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAk0ZBO9cTyiNdalWm1NHB
+gpzEZaY+WeNhAKdq1ovGpopLyjNbt0nfLLQqa8duUQK5nvlI/2ZuaO8Cp/NTf1yI
+aubvvrC1YiAFY/W6Afr0IeO7h7ytP8lOmZgUadnB2gGBrL137TPrYO+Jy/I+AGjp
+YRNUliUnOKrLFSGo+xFIlj8FAH2eAZH93swx0h+8BYsHn587qB89ncn1BoLlm+GN
+FDBMA/DAO5dpNa/iZavu/FwQEElRb/Rya9mqgFt421h0KXYQC8wuJmAJveSa5kB2
+wSd8tklvNtSpM7Os5DAIGinD58hpN+XqPOFcGTKNUDnUN0ZjrchmdOhl/PwTMTp2
+GwIDAQAB
+-----END PUBLIC KEY-----

+ 1 - 1
version.txt

@@ -1 +1 @@
-feature1.0.02104221124
+feature1.0.02105201851