xw 6 днів тому
батько
коміт
12598aceef
19 змінених файлів з 359 додано та 68 видалено
  1. 25 0
      fs-common/src/main/java/com/fs/common/config/TokenSecretStartupValidator.java
  2. 92 0
      fs-common/src/main/java/com/fs/common/utils/security/JwtSecretValidator.java
  3. 28 0
      fs-common/src/main/java/com/fs/common/utils/security/JwtTokenHelper.java
  4. 25 0
      fs-common/src/main/java/com/fs/common/utils/security/TokenIpBindValidator.java
  5. 17 6
      fs-company/src/main/java/com/fs/framework/service/TokenService.java
  6. 21 5
      fs-framework/src/main/java/com/fs/framework/web/service/TokenService.java
  7. 17 6
      fs-ipad-task/src/main/java/com/fs/framework/service/TokenService.java
  8. 12 4
      fs-quartz/src/main/java/com/fs/quartz/util/ScheduleUtils.java
  9. 17 6
      fs-qw-api-msg/src/main/java/com/fs/framework/service/TokenService.java
  10. 17 6
      fs-qw-api/src/main/java/com/fs/framework/service/TokenService.java
  11. 17 6
      fs-qw-company-api/src/main/java/com/fs/framework/service/TokenService.java
  12. 17 6
      fs-qw-task/src/main/java/com/fs/framework/service/TokenService.java
  13. 6 4
      fs-service/src/main/resources/application-common.yml
  14. 2 2
      fs-service/src/main/resources/application-dev.yml
  15. 1 1
      fs-service/src/main/resources/mapper/sop/QwSopMapper.xml
  16. 26 6
      fs-store/src/main/java/com/fs/framework/service/TokenService.java
  17. 16 5
      fs-store/src/main/java/com/fs/framework/service/TokenServiceScrm.java
  18. 1 1
      fs-watch/src/main/java/com/fs/app/utils/JwtUtils.java
  19. 2 4
      fs-watch/src/main/java/com/fs/framework/service/TokenService.java

+ 25 - 0
fs-common/src/main/java/com/fs/common/config/TokenSecretStartupValidator.java

@@ -0,0 +1,25 @@
+package com.fs.common.config;
+
+import com.fs.common.utils.security.JwtSecretValidator;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+import org.springframework.stereotype.Component;
+
+import javax.annotation.PostConstruct;
+
+/**
+ * 应用启动时校验 JWT 签名密钥强度,防止弱密钥上线
+ */
+@Component
+@ConditionalOnProperty(name = "token.secret")
+public class TokenSecretStartupValidator
+{
+    @Value("${token.secret}")
+    private String secret;
+
+    @PostConstruct
+    public void validate()
+    {
+        JwtSecretValidator.validate(secret, "token.secret");
+    }
+}

+ 92 - 0
fs-common/src/main/java/com/fs/common/utils/security/JwtSecretValidator.java

@@ -0,0 +1,92 @@
+package com.fs.common.utils.security;
+
+import com.fs.common.utils.StringUtils;
+
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * JWT 签名密钥强度校验
+ */
+public final class JwtSecretValidator
+{
+    private static final int MIN_LENGTH = 32;
+
+    private static final Set<String> KNOWN_WEAK_SECRETS = new HashSet<>(Arrays.asList(
+            "abcdefghijklmnopqrstuvwxyz",
+            "abcdefghijklmnopqrstuvwx",
+            "ABCDEFGHIJKLMNOPQRSTUVWXYZ",
+            "01234567890123456789012345678901",
+            "f4e2e52034348f86b67cde581c0f9eb5",
+            "3e6d9c0b4a7f1e2d5c4e0d3c6b9a2f5e"
+    ));
+
+    private JwtSecretValidator()
+    {
+    }
+
+    public static void validate(String secret, String propertyName)
+    {
+        if (StringUtils.isEmpty(secret))
+        {
+            throw new IllegalStateException(propertyName + " 未配置,请通过环境变量 JWT_TOKEN_SECRET 设置长度≥32位的高复杂度随机密钥");
+        }
+        if (secret.length() < MIN_LENGTH)
+        {
+            throw new IllegalStateException(propertyName + " 长度不足 " + MIN_LENGTH + " 位,当前长度: " + secret.length());
+        }
+        if (KNOWN_WEAK_SECRETS.contains(secret))
+        {
+            throw new IllegalStateException(propertyName + " 使用了已知的弱密钥,请立即更换为高复杂度随机密钥");
+        }
+        if (isRepeatedChar(secret))
+        {
+            throw new IllegalStateException(propertyName + " 字符重复度过高,请使用高复杂度随机密钥");
+        }
+        if (isSequential(secret))
+        {
+            throw new IllegalStateException(propertyName + " 为连续字符序列,请使用高复杂度随机密钥");
+        }
+    }
+
+    private static boolean isRepeatedChar(String secret)
+    {
+        char first = secret.charAt(0);
+        for (int i = 1; i < secret.length(); i++)
+        {
+            if (secret.charAt(i) != first)
+            {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    private static boolean isSequential(String secret)
+    {
+        if (secret.length() < 4)
+        {
+            return false;
+        }
+        int ascending = 0;
+        int descending = 0;
+        for (int i = 1; i < secret.length(); i++)
+        {
+            int diff = secret.charAt(i) - secret.charAt(i - 1);
+            if (diff == 1)
+            {
+                ascending++;
+            }
+            else if (diff == -1)
+            {
+                descending++;
+            }
+            if (ascending >= secret.length() - 2 || descending >= secret.length() - 2)
+            {
+                return true;
+            }
+        }
+        return false;
+    }
+}

+ 28 - 0
fs-common/src/main/java/com/fs/common/utils/security/JwtTokenHelper.java

@@ -0,0 +1,28 @@
+package com.fs.common.utils.security;
+
+import io.jsonwebtoken.Jwts;
+import io.jsonwebtoken.SignatureAlgorithm;
+
+import java.util.Date;
+import java.util.Map;
+
+/**
+ * JWT 令牌创建工具
+ */
+public final class JwtTokenHelper
+{
+    private JwtTokenHelper()
+    {
+    }
+
+    public static String createToken(Map<String, Object> claims, String secret, long expireMillis)
+    {
+        long now = System.currentTimeMillis();
+        return Jwts.builder()
+                .setClaims(claims)
+                .setIssuedAt(new Date(now))
+                .setExpiration(new Date(now + expireMillis))
+                .signWith(SignatureAlgorithm.HS512, secret)
+                .compact();
+    }
+}

+ 25 - 0
fs-common/src/main/java/com/fs/common/utils/security/TokenIpBindValidator.java

@@ -0,0 +1,25 @@
+package com.fs.common.utils.security;
+
+import com.fs.common.utils.StringUtils;
+import com.fs.common.utils.ip.IpUtils;
+
+import javax.servlet.http.HttpServletRequest;
+
+/**
+ * 登录 IP 绑定校验
+ */
+public final class TokenIpBindValidator
+{
+    private TokenIpBindValidator()
+    {
+    }
+
+    public static boolean isIpValid(String loginIp, HttpServletRequest request, boolean ipBindEnabled)
+    {
+        if (!ipBindEnabled || StringUtils.isEmpty(loginIp))
+        {
+            return true;
+        }
+        return loginIp.equals(IpUtils.getIpAddr(request));
+    }
+}

+ 17 - 6
fs-company/src/main/java/com/fs/framework/service/TokenService.java

@@ -4,6 +4,8 @@ import com.fs.common.constant.Constants;
 import com.fs.common.core.redis.RedisCache;
 import com.fs.common.utils.ServletUtils;
 import com.fs.common.utils.StringUtils;
+import com.fs.common.utils.security.JwtTokenHelper;
+import com.fs.common.utils.security.TokenIpBindValidator;
 import com.fs.common.utils.ip.AddressUtils;
 import com.fs.common.utils.ip.IpUtils;
 import com.fs.common.utils.uuid.IdUtils;
@@ -41,6 +43,9 @@ public class TokenService
     @Value("${token.expireTime}")
     private int expireTime;
 
+    @Value("${token.ipBindEnabled:false}")
+    private boolean ipBindEnabled;
+
     protected static final long MILLIS_SECOND = 1000;
 
     protected static final long MILLIS_MINUTE = 60 * MILLIS_SECOND;
@@ -66,7 +71,7 @@ public class TokenService
             String uuid = (String) claims.get(Constants.COMPANY_LOGIN_USER_KEY);
             String userKey = getTokenKey(uuid);
             LoginUser user = redisCache.getCacheObject(userKey);
-            return user;
+            return validateLoginUser(user, request);
         }
         token=getUrlToken(request);
         if (StringUtils.isNotEmpty(token))
@@ -76,7 +81,7 @@ public class TokenService
             String uuid = (String) claims.get(Constants.COMPANY_LOGIN_USER_KEY);
             String userKey = getTokenKey(uuid);
             LoginUser user = redisCache.getCacheObject(userKey);
-            return user;
+            return validateLoginUser(user, request);
         }
 
         return null;
@@ -176,10 +181,7 @@ public class TokenService
      */
     private String createToken(Map<String, Object> claims)
     {
-        String token = Jwts.builder()
-                .setClaims(claims)
-                .signWith(SignatureAlgorithm.HS512, secret).compact();
-        return token;
+        return JwtTokenHelper.createToken(claims, secret, expireTime * MILLIS_MINUTE);
     }
 
     /**
@@ -233,4 +235,13 @@ public class TokenService
     {
         return Constants.COMPANY_LOGIN_TOKEN_KEY + uuid;
     }
+
+    private LoginUser validateLoginUser(LoginUser user, HttpServletRequest request)
+    {
+        if (user == null || !TokenIpBindValidator.isIpValid(user.getIpaddr(), request, ipBindEnabled))
+        {
+            return null;
+        }
+        return user;
+    }
 }

+ 21 - 5
fs-framework/src/main/java/com/fs/framework/web/service/TokenService.java

@@ -12,6 +12,8 @@ import com.fs.common.core.domain.model.LoginUser;
 import com.fs.common.core.redis.RedisCache;
 import com.fs.common.utils.ServletUtils;
 import com.fs.common.utils.StringUtils;
+import com.fs.common.utils.security.JwtTokenHelper;
+import com.fs.common.utils.security.TokenIpBindValidator;
 import com.fs.common.utils.ip.AddressUtils;
 import com.fs.common.utils.ip.IpUtils;
 import com.fs.common.utils.uuid.IdUtils;
@@ -40,6 +42,10 @@ public class TokenService
     @Value("${token.expireTime}")
     private int expireTime;
 
+    // 是否启用 IP 绑定校验
+    @Value("${token.ipBindEnabled:false}")
+    private boolean ipBindEnabled;
+
     protected static final long MILLIS_SECOND = 1000;
 
     protected static final long MILLIS_MINUTE = 60 * MILLIS_SECOND;
@@ -67,7 +73,7 @@ public class TokenService
                 String uuid = (String) claims.get(Constants.LOGIN_USER_KEY);
                 String userKey = getTokenKey(uuid);
                 LoginUser user = redisCache.getCacheObject(userKey);
-                return user;
+                return validateLoginUser(user, request);
             }
             catch (Exception e)
             {
@@ -170,10 +176,7 @@ public class TokenService
      */
     private String createToken(Map<String, Object> claims)
     {
-        String token = Jwts.builder()
-                .setClaims(claims)
-                .signWith(SignatureAlgorithm.HS512, secret).compact();
-        return token;
+        return JwtTokenHelper.createToken(claims, secret, expireTime * MILLIS_MINUTE);
     }
 
     /**
@@ -222,4 +225,17 @@ public class TokenService
     {
         return Constants.LOGIN_TOKEN_KEY + uuid;
     }
+
+    private LoginUser validateLoginUser(LoginUser user, HttpServletRequest request)
+    {
+        if (user == null)
+        {
+            return null;
+        }
+        if (!TokenIpBindValidator.isIpValid(user.getIpaddr(), request, ipBindEnabled))
+        {
+            return null;
+        }
+        return user;
+    }
 }

+ 17 - 6
fs-ipad-task/src/main/java/com/fs/framework/service/TokenService.java

@@ -4,6 +4,8 @@ import com.fs.common.constant.Constants;
 import com.fs.common.core.redis.RedisCache;
 import com.fs.common.utils.ServletUtils;
 import com.fs.common.utils.StringUtils;
+import com.fs.common.utils.security.JwtTokenHelper;
+import com.fs.common.utils.security.TokenIpBindValidator;
 import com.fs.common.utils.ip.AddressUtils;
 import com.fs.common.utils.ip.IpUtils;
 import com.fs.common.utils.uuid.IdUtils;
@@ -41,6 +43,9 @@ public class TokenService
     @Value("${token.expireTime}")
     private int expireTime;
 
+    @Value("${token.ipBindEnabled:false}")
+    private boolean ipBindEnabled;
+
     protected static final long MILLIS_SECOND = 1000;
 
     protected static final long MILLIS_MINUTE = 60 * MILLIS_SECOND;
@@ -66,7 +71,7 @@ public class TokenService
             String uuid = (String) claims.get(Constants.COMPANY_LOGIN_USER_KEY);
             String userKey = getTokenKey(uuid);
             LoginUser user = redisCache.getCacheObject(userKey);
-            return user;
+            return validateLoginUser(user, request);
         }
         token=getUrlToken(request);
         if (StringUtils.isNotEmpty(token))
@@ -76,7 +81,7 @@ public class TokenService
             String uuid = (String) claims.get(Constants.COMPANY_LOGIN_USER_KEY);
             String userKey = getTokenKey(uuid);
             LoginUser user = redisCache.getCacheObject(userKey);
-            return user;
+            return validateLoginUser(user, request);
         }
 
         return null;
@@ -176,10 +181,7 @@ public class TokenService
      */
     private String createToken(Map<String, Object> claims)
     {
-        String token = Jwts.builder()
-                .setClaims(claims)
-                .signWith(SignatureAlgorithm.HS512, secret).compact();
-        return token;
+        return JwtTokenHelper.createToken(claims, secret, expireTime * MILLIS_MINUTE);
     }
 
     /**
@@ -233,4 +235,13 @@ public class TokenService
     {
         return Constants.COMPANY_LOGIN_TOKEN_KEY + uuid;
     }
+
+    private LoginUser validateLoginUser(LoginUser user, HttpServletRequest request)
+    {
+        if (user == null || !TokenIpBindValidator.isIpValid(user.getIpaddr(), request, ipBindEnabled))
+        {
+            return null;
+        }
+        return user;
+    }
 }

+ 12 - 4
fs-quartz/src/main/java/com/fs/quartz/util/ScheduleUtils.java

@@ -10,6 +10,7 @@ import org.quartz.Scheduler;
 import org.quartz.SchedulerException;
 import org.quartz.TriggerBuilder;
 import org.quartz.TriggerKey;
+import org.springframework.beans.BeansException;
 import com.fs.common.constant.Constants;
 import com.fs.common.constant.ScheduleConstants;
 import com.fs.common.exception.job.TaskException;
@@ -160,9 +161,16 @@ public class ScheduleUtils
         {
             return StringUtils.startsWithAny(invokeTarget, Constants.JOB_WHITELIST_STR);
         }
-        Object obj = SpringUtils.getBean(StringUtils.split(invokeTarget, ".")[0]);
-        String beanPackageName = obj.getClass().getPackage().getName();
-        return StringUtils.startsWithAny(beanPackageName, Constants.JOB_WHITELIST_STR)
-                && !StringUtils.startsWithAny(beanPackageName, Constants.JOB_ERROR_STR);
+        try
+        {
+            Object obj = SpringUtils.getBean(StringUtils.split(invokeTarget, ".")[0]);
+            String beanPackageName = obj.getClass().getPackage().getName();
+            return StringUtils.startsWithAny(beanPackageName, Constants.JOB_WHITELIST_STR)
+                    && !StringUtils.startsWithAny(beanPackageName, Constants.JOB_ERROR_STR);
+        }
+        catch (BeansException ex)
+        {
+            return false;
+        }
     }
 }

+ 17 - 6
fs-qw-api-msg/src/main/java/com/fs/framework/service/TokenService.java

@@ -4,6 +4,8 @@ import com.fs.common.constant.Constants;
 import com.fs.common.core.redis.RedisCache;
 import com.fs.common.utils.ServletUtils;
 import com.fs.common.utils.StringUtils;
+import com.fs.common.utils.security.JwtTokenHelper;
+import com.fs.common.utils.security.TokenIpBindValidator;
 import com.fs.common.utils.ip.AddressUtils;
 import com.fs.common.utils.ip.IpUtils;
 import com.fs.common.utils.uuid.IdUtils;
@@ -41,6 +43,9 @@ public class TokenService
     @Value("${token.expireTime}")
     private int expireTime;
 
+    @Value("${token.ipBindEnabled:false}")
+    private boolean ipBindEnabled;
+
     protected static final long MILLIS_SECOND = 1000;
 
     protected static final long MILLIS_MINUTE = 60 * MILLIS_SECOND;
@@ -66,7 +71,7 @@ public class TokenService
             String uuid = (String) claims.get(Constants.COMPANY_LOGIN_USER_KEY);
             String userKey = getTokenKey(uuid);
             LoginUser user = redisCache.getCacheObject(userKey);
-            return user;
+            return validateLoginUser(user, request);
         }
         token=getUrlToken(request);
         if (StringUtils.isNotEmpty(token))
@@ -76,7 +81,7 @@ public class TokenService
             String uuid = (String) claims.get(Constants.COMPANY_LOGIN_USER_KEY);
             String userKey = getTokenKey(uuid);
             LoginUser user = redisCache.getCacheObject(userKey);
-            return user;
+            return validateLoginUser(user, request);
         }
 
         return null;
@@ -176,10 +181,7 @@ public class TokenService
      */
     private String createToken(Map<String, Object> claims)
     {
-        String token = Jwts.builder()
-                .setClaims(claims)
-                .signWith(SignatureAlgorithm.HS512, secret).compact();
-        return token;
+        return JwtTokenHelper.createToken(claims, secret, expireTime * MILLIS_MINUTE);
     }
 
     /**
@@ -233,4 +235,13 @@ public class TokenService
     {
         return Constants.COMPANY_LOGIN_TOKEN_KEY + uuid;
     }
+
+    private LoginUser validateLoginUser(LoginUser user, HttpServletRequest request)
+    {
+        if (user == null || !TokenIpBindValidator.isIpValid(user.getIpaddr(), request, ipBindEnabled))
+        {
+            return null;
+        }
+        return user;
+    }
 }

+ 17 - 6
fs-qw-api/src/main/java/com/fs/framework/service/TokenService.java

@@ -4,6 +4,8 @@ import com.fs.common.constant.Constants;
 import com.fs.common.core.redis.RedisCache;
 import com.fs.common.utils.ServletUtils;
 import com.fs.common.utils.StringUtils;
+import com.fs.common.utils.security.JwtTokenHelper;
+import com.fs.common.utils.security.TokenIpBindValidator;
 import com.fs.common.utils.ip.AddressUtils;
 import com.fs.common.utils.ip.IpUtils;
 import com.fs.common.utils.uuid.IdUtils;
@@ -41,6 +43,9 @@ public class TokenService
     @Value("${token.expireTime}")
     private int expireTime;
 
+    @Value("${token.ipBindEnabled:false}")
+    private boolean ipBindEnabled;
+
     protected static final long MILLIS_SECOND = 1000;
 
     protected static final long MILLIS_MINUTE = 60 * MILLIS_SECOND;
@@ -66,7 +71,7 @@ public class TokenService
             String uuid = (String) claims.get(Constants.COMPANY_LOGIN_USER_KEY);
             String userKey = getTokenKey(uuid);
             LoginUser user = redisCache.getCacheObject(userKey);
-            return user;
+            return validateLoginUser(user, request);
         }
         token=getUrlToken(request);
         if (StringUtils.isNotEmpty(token))
@@ -76,7 +81,7 @@ public class TokenService
             String uuid = (String) claims.get(Constants.COMPANY_LOGIN_USER_KEY);
             String userKey = getTokenKey(uuid);
             LoginUser user = redisCache.getCacheObject(userKey);
-            return user;
+            return validateLoginUser(user, request);
         }
 
         return null;
@@ -176,10 +181,7 @@ public class TokenService
      */
     private String createToken(Map<String, Object> claims)
     {
-        String token = Jwts.builder()
-                .setClaims(claims)
-                .signWith(SignatureAlgorithm.HS512, secret).compact();
-        return token;
+        return JwtTokenHelper.createToken(claims, secret, expireTime * MILLIS_MINUTE);
     }
 
     /**
@@ -233,4 +235,13 @@ public class TokenService
     {
         return Constants.COMPANY_LOGIN_TOKEN_KEY + uuid;
     }
+
+    private LoginUser validateLoginUser(LoginUser user, HttpServletRequest request)
+    {
+        if (user == null || !TokenIpBindValidator.isIpValid(user.getIpaddr(), request, ipBindEnabled))
+        {
+            return null;
+        }
+        return user;
+    }
 }

+ 17 - 6
fs-qw-company-api/src/main/java/com/fs/framework/service/TokenService.java

@@ -4,6 +4,8 @@ import com.fs.common.constant.Constants;
 import com.fs.common.core.redis.RedisCache;
 import com.fs.common.utils.ServletUtils;
 import com.fs.common.utils.StringUtils;
+import com.fs.common.utils.security.JwtTokenHelper;
+import com.fs.common.utils.security.TokenIpBindValidator;
 import com.fs.common.utils.ip.AddressUtils;
 import com.fs.common.utils.ip.IpUtils;
 import com.fs.common.utils.uuid.IdUtils;
@@ -41,6 +43,9 @@ public class TokenService
     @Value("${token.expireTime}")
     private int expireTime;
 
+    @Value("${token.ipBindEnabled:false}")
+    private boolean ipBindEnabled;
+
     protected static final long MILLIS_SECOND = 1000;
 
     protected static final long MILLIS_MINUTE = 60 * MILLIS_SECOND;
@@ -66,7 +71,7 @@ public class TokenService
             String uuid = (String) claims.get(Constants.COMPANY_LOGIN_USER_KEY);
             String userKey = getTokenKey(uuid);
             LoginUser user = redisCache.getCacheObject(userKey);
-            return user;
+            return validateLoginUser(user, request);
         }
         token=getUrlToken(request);
         if (StringUtils.isNotEmpty(token))
@@ -76,7 +81,7 @@ public class TokenService
             String uuid = (String) claims.get(Constants.COMPANY_LOGIN_USER_KEY);
             String userKey = getTokenKey(uuid);
             LoginUser user = redisCache.getCacheObject(userKey);
-            return user;
+            return validateLoginUser(user, request);
         }
 
         return null;
@@ -176,10 +181,7 @@ public class TokenService
      */
     private String createToken(Map<String, Object> claims)
     {
-        String token = Jwts.builder()
-                .setClaims(claims)
-                .signWith(SignatureAlgorithm.HS512, secret).compact();
-        return token;
+        return JwtTokenHelper.createToken(claims, secret, expireTime * MILLIS_MINUTE);
     }
 
     /**
@@ -233,4 +235,13 @@ public class TokenService
     {
         return Constants.COMPANY_LOGIN_TOKEN_KEY + uuid;
     }
+
+    private LoginUser validateLoginUser(LoginUser user, HttpServletRequest request)
+    {
+        if (user == null || !TokenIpBindValidator.isIpValid(user.getIpaddr(), request, ipBindEnabled))
+        {
+            return null;
+        }
+        return user;
+    }
 }

+ 17 - 6
fs-qw-task/src/main/java/com/fs/framework/service/TokenService.java

@@ -4,6 +4,8 @@ import com.fs.common.constant.Constants;
 import com.fs.common.core.redis.RedisCache;
 import com.fs.common.utils.ServletUtils;
 import com.fs.common.utils.StringUtils;
+import com.fs.common.utils.security.JwtTokenHelper;
+import com.fs.common.utils.security.TokenIpBindValidator;
 import com.fs.common.utils.ip.AddressUtils;
 import com.fs.common.utils.ip.IpUtils;
 import com.fs.common.utils.uuid.IdUtils;
@@ -41,6 +43,9 @@ public class TokenService
     @Value("${token.expireTime}")
     private int expireTime;
 
+    @Value("${token.ipBindEnabled:false}")
+    private boolean ipBindEnabled;
+
     protected static final long MILLIS_SECOND = 1000;
 
     protected static final long MILLIS_MINUTE = 60 * MILLIS_SECOND;
@@ -66,7 +71,7 @@ public class TokenService
             String uuid = (String) claims.get(Constants.COMPANY_LOGIN_USER_KEY);
             String userKey = getTokenKey(uuid);
             LoginUser user = redisCache.getCacheObject(userKey);
-            return user;
+            return validateLoginUser(user, request);
         }
         token=getUrlToken(request);
         if (StringUtils.isNotEmpty(token))
@@ -76,7 +81,7 @@ public class TokenService
             String uuid = (String) claims.get(Constants.COMPANY_LOGIN_USER_KEY);
             String userKey = getTokenKey(uuid);
             LoginUser user = redisCache.getCacheObject(userKey);
-            return user;
+            return validateLoginUser(user, request);
         }
 
         return null;
@@ -176,10 +181,7 @@ public class TokenService
      */
     private String createToken(Map<String, Object> claims)
     {
-        String token = Jwts.builder()
-                .setClaims(claims)
-                .signWith(SignatureAlgorithm.HS512, secret).compact();
-        return token;
+        return JwtTokenHelper.createToken(claims, secret, expireTime * MILLIS_MINUTE);
     }
 
     /**
@@ -233,4 +235,13 @@ public class TokenService
     {
         return Constants.COMPANY_LOGIN_TOKEN_KEY + uuid;
     }
+
+    private LoginUser validateLoginUser(LoginUser user, HttpServletRequest request)
+    {
+        if (user == null || !TokenIpBindValidator.isIpValid(user.getIpaddr(), request, ipBindEnabled))
+        {
+            return null;
+        }
+        return user;
+    }
 }

+ 6 - 4
fs-service/src/main/resources/application-common.yml

@@ -75,10 +75,12 @@ spring:
 token:
     # 令牌自定义标识
     header: Authorization
-    # 令牌密钥
-    secret: abcdefghijklmnopqrstuvwxyz
-    # 令牌有效期(默认30分钟)
-    expireTime: 720
+    # 令牌密钥(通过环境变量 JWT_TOKEN_SECRET 注入,长度≥32位高复杂度随机字符串,禁止硬编码)
+    secret: ${JWT_TOKEN_SECRET:}
+    # 令牌有效期(分钟)
+    expireTime: 120
+    # 是否启用 IP 绑定校验(生产环境建议开启)
+    ipBindEnabled: false
 mybatis-plus:
   # 搜索指定包别名
   typeAliasesPackage: com.fs.**.domain,com.fs.**.bo

+ 2 - 2
fs-service/src/main/resources/application-dev.yml

@@ -1,4 +1,4 @@
-# 数据源配
+# 数据源配�?
 spring:
     config:
         import:
@@ -170,7 +170,7 @@ token:
     # 令牌自定义标识
     header: Authorization
     # 令牌密钥
-    secret: abcdefghijklmnopqrstuvwxyz
+    secret: ${JWT_TOKEN_SECRET:}
     # 令牌有效期(默认30分钟)
     expireTime: 180
 openIM:

+ 1 - 1
fs-service/src/main/resources/mapper/sop/QwSopMapper.xml

@@ -455,7 +455,7 @@
 
     <select id="selectQwSopAllList" resultType="com.fs.sop.domain.QwSop">
 
-        SELECT DISTINCT qw_sop.id,qw_sop.name
+        SELECT DISTINCT qw_sop.id, qw_sop.name, qw_sop.create_time
         FROM sop_user_logs log
         RIGHT JOIN qw_sop ON log.sop_id = qw_sop.id
         <where>

+ 26 - 6
fs-store/src/main/java/com/fs/framework/service/TokenService.java

@@ -3,6 +3,8 @@ import com.fs.common.constant.Constants;
 import com.fs.common.core.redis.RedisCache;
 import com.fs.common.utils.ServletUtils;
 import com.fs.common.utils.StringUtils;
+import com.fs.common.utils.security.JwtTokenHelper;
+import com.fs.common.utils.security.TokenIpBindValidator;
 import com.fs.common.utils.ip.AddressUtils;
 import com.fs.common.utils.ip.IpUtils;
 import com.fs.common.utils.uuid.IdUtils;
@@ -49,6 +51,9 @@ public class TokenService
     @Value("${token.expireTime}")
     private int expireTime;
 
+    @Value("${token.ipBindEnabled:false}")
+    private boolean ipBindEnabled;
+
     protected static final long MILLIS_SECOND = 1000;
 
     protected static final long MILLIS_MINUTE = 60 * MILLIS_SECOND;
@@ -76,7 +81,7 @@ public class TokenService
                 String uuid = (String) claims.get(Constants.LOGIN_USER_KEY);
                 String userKey = getTokenKey(uuid);
                 StoreLoginUser user = redisCache.getCacheObject(userKey);
-                return user;
+                return validateLoginUser(user, request);
             }
             catch (Exception e)
             {
@@ -181,10 +186,7 @@ public class TokenService
      */
     private String createToken(Map<String, Object> claims)
     {
-        String token = Jwts.builder()
-                .setClaims(claims)
-                .signWith(SignatureAlgorithm.HS512, secret).compact();
-        return token;
+        return JwtTokenHelper.createToken(claims, secret, expireTime * MILLIS_MINUTE);
     }
 
     /**
@@ -252,7 +254,7 @@ public class TokenService
                 String uuid = (String) claims.get(Constants.LOGIN_USER_KEY);
                 String userKey = getTokenKey(uuid);
                 StoreLoginUserScrm user = redisCache.getCacheObject(userKey);
-                return user;
+                return validateLoginUserScrm(user, request);
             }
             catch (Exception e)
             {
@@ -335,4 +337,22 @@ public class TokenService
         loginUser.setBrowser(userAgent.getBrowser().getName());
         loginUser.setOs(userAgent.getOperatingSystem().getName());
     }
+
+    private StoreLoginUser validateLoginUser(StoreLoginUser user, HttpServletRequest request)
+    {
+        if (user == null || !TokenIpBindValidator.isIpValid(user.getIpaddr(), request, ipBindEnabled))
+        {
+            return null;
+        }
+        return user;
+    }
+
+    private StoreLoginUserScrm validateLoginUserScrm(StoreLoginUserScrm user, HttpServletRequest request)
+    {
+        if (user == null || !TokenIpBindValidator.isIpValid(user.getIpaddr(), request, ipBindEnabled))
+        {
+            return null;
+        }
+        return user;
+    }
 }

+ 16 - 5
fs-store/src/main/java/com/fs/framework/service/TokenServiceScrm.java

@@ -4,6 +4,8 @@ import com.fs.common.constant.Constants;
 import com.fs.common.core.redis.RedisCache;
 import com.fs.common.utils.ServletUtils;
 import com.fs.common.utils.StringUtils;
+import com.fs.common.utils.security.JwtTokenHelper;
+import com.fs.common.utils.security.TokenIpBindValidator;
 import com.fs.common.utils.ip.AddressUtils;
 import com.fs.common.utils.ip.IpUtils;
 import com.fs.common.utils.uuid.IdUtils;
@@ -46,6 +48,9 @@ public class TokenServiceScrm
     @Value("${token.expireTime}")
     private int expireTime;
 
+    @Value("${token.ipBindEnabled:false}")
+    private boolean ipBindEnabled;
+
     protected static final long MILLIS_SECOND = 1000;
 
     protected static final long MILLIS_MINUTE = 60 * MILLIS_SECOND;
@@ -73,7 +78,7 @@ public class TokenServiceScrm
                 String uuid = (String) claims.get(Constants.LOGIN_USER_KEY);
                 String userKey = getTokenKey(uuid);
                 StoreLoginUserScrm user = redisCache.getCacheObject(userKey);
-                return user;
+                return validateLoginUser(user, request);
             }
             catch (Exception e)
             {
@@ -178,10 +183,7 @@ public class TokenServiceScrm
      */
     private String createToken(Map<String, Object> claims)
     {
-        String token = Jwts.builder()
-                .setClaims(claims)
-                .signWith(SignatureAlgorithm.HS512, secret).compact();
-        return token;
+        return JwtTokenHelper.createToken(claims, secret, expireTime * MILLIS_MINUTE);
     }
 
     /**
@@ -230,4 +232,13 @@ public class TokenServiceScrm
     {
         return Constants.LOGIN_TOKEN_KEY + uuid;
     }
+
+    private StoreLoginUserScrm validateLoginUser(StoreLoginUserScrm user, HttpServletRequest request)
+    {
+        if (user == null || !TokenIpBindValidator.isIpValid(user.getIpaddr(), request, ipBindEnabled))
+        {
+            return null;
+        }
+        return user;
+    }
 }

+ 1 - 1
fs-watch/src/main/java/com/fs/app/utils/JwtUtils.java

@@ -19,7 +19,7 @@ public class JwtUtils {
     private Logger logger = LoggerFactory.getLogger(getClass());
 
 
-    private String secret = "f4e2e52034348f86b67cde581c0f9eb5";
+    private String secret;
     private long expire;
     //    private String header;
     private String header = "AppToken";

+ 2 - 4
fs-watch/src/main/java/com/fs/framework/service/TokenService.java

@@ -5,6 +5,7 @@ import com.fs.common.core.domain.model.LoginUser;
 import com.fs.common.core.redis.RedisCache;
 import com.fs.common.utils.ServletUtils;
 import com.fs.common.utils.StringUtils;
+import com.fs.common.utils.security.JwtTokenHelper;
 import com.fs.common.utils.ip.AddressUtils;
 import com.fs.common.utils.ip.IpUtils;
 import com.fs.common.utils.uuid.IdUtils;
@@ -225,10 +226,7 @@ public class TokenService
      */
     private String createToken(Map<String, Object> claims)
     {
-        String token = Jwts.builder()
-                .setClaims(claims)
-                .signWith(SignatureAlgorithm.HS512, secret).compact();
-        return token;
+        return JwtTokenHelper.createToken(claims, secret, expireTime * MILLIS_MINUTE);
     }
 
     /**