From 4e5138fdfd60edf0d67787582905dfcc0801d667 Mon Sep 17 00:00:00 2001
From: zyy <zyy@email.com>
Date: Thu, 20 Nov 2025 17:38:10 +0800
Subject: [PATCH] 多ip地址登录

---
 trading-order-admin/src/main/java/com/yami/trading/admin/controller/AdminLoginController.java          |   12 +++++++++---
 trading-order-security-common/src/main/java/com/yami/trading/security/common/filter/AuthFilter.java    |   18 ++++++++++--------
 trading-order-common/src/main/java/com/yami/trading/common/util/RedisUtil.java                         |   21 +++++++++++++++++----
 trading-order-security-common/src/main/java/com/yami/trading/security/common/bo/UserInfoInTokenBO.java |    7 ++++++-
 trading-order-common/src/main/java/com/yami/trading/common/constants/RedisKeys.java                    |    2 +-
 5 files changed, 43 insertions(+), 17 deletions(-)

diff --git a/trading-order-admin/src/main/java/com/yami/trading/admin/controller/AdminLoginController.java b/trading-order-admin/src/main/java/com/yami/trading/admin/controller/AdminLoginController.java
index 6f8121d..99aa013 100644
--- a/trading-order-admin/src/main/java/com/yami/trading/admin/controller/AdminLoginController.java
+++ b/trading-order-admin/src/main/java/com/yami/trading/admin/controller/AdminLoginController.java
@@ -122,12 +122,18 @@
         userInfoInToken.setPerms(getUserPermissions(sysUser.getUserId()));
         userInfoInToken.setNickName(sysUser.getUsername());
         userInfoInToken.setShopId(sysUser.getShopId());
-       tokenStore.deleteAllToken(String.valueOf(SysTypeEnum.ADMIN.value()), String.valueOf(sysUser.getUserId()));
+        //tokenStore.deleteAllToken(String.valueOf(SysTypeEnum.ADMIN.value()), String.valueOf(sysUser.getUserId()));
        
         // 存储token返回vo
         TokenInfoVO tokenInfoVO = tokenStore.storeAndGetVo(userInfoInToken);
-        RedisUtil.set(RedisKeys.ACCESS_IP + sysUser.getUserId().intValue(),IPHelper.getIpAddr());
-        
+        //RedisUtil.set(RedisKeys.ACCESS_IP + sysUser.getUserId().intValue(),IPHelper.getIpAddr());
+        // 关键修改2:用Redis Set存储多个登录IP(自动去重)
+        String loginIp = IPHelper.getIpAddr();
+        String ipSetKey = RedisKeys.ACCESS_IP + sysUser.getUserId().intValue(); // 键:ACCESS_IP+用户ID
+        long tokenExpiresIn = tokenInfoVO.getExpiresIn(); // 与Token过期时间一致
+        RedisUtil.sAdd(ipSetKey, loginIp); // 存入IP到Set
+        RedisUtil.expire(ipSetKey, tokenExpiresIn); // 设置过期时间
+
         String context = MessageFormat.format("ip:{0}, 登录系统,时间[{1}]",
                 new Object[]{IPHelper.getIpAddr(),
                         DateUtils.dateToStr(new Date(),
diff --git a/trading-order-common/src/main/java/com/yami/trading/common/constants/RedisKeys.java b/trading-order-common/src/main/java/com/yami/trading/common/constants/RedisKeys.java
index c083d5f..d775f8c 100644
--- a/trading-order-common/src/main/java/com/yami/trading/common/constants/RedisKeys.java
+++ b/trading-order-common/src/main/java/com/yami/trading/common/constants/RedisKeys.java
@@ -2,7 +2,7 @@
 
 public class RedisKeys {
 
-	public final static String ACCESS_IP = "ACCESS_IP";
+	public final static String ACCESS_IP = "NEW_ACCESS_IP";
 
 	/**
 	 * item杠杆倍数
diff --git a/trading-order-common/src/main/java/com/yami/trading/common/util/RedisUtil.java b/trading-order-common/src/main/java/com/yami/trading/common/util/RedisUtil.java
index d45c0ee..9fb4139 100644
--- a/trading-order-common/src/main/java/com/yami/trading/common/util/RedisUtil.java
+++ b/trading-order-common/src/main/java/com/yami/trading/common/util/RedisUtil.java
@@ -7,11 +7,9 @@
 import org.springframework.data.redis.core.RedisTemplate;
 import org.springframework.data.redis.core.StringRedisTemplate;
 
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-import java.util.Map;
+import java.util.*;
 import java.util.concurrent.TimeUnit;
+import java.util.stream.Collectors;
 
 /**
  * @author lh
@@ -263,4 +261,19 @@
         return retList;
     }
 
+    // 向Set中添加元素(用于存储登录IP)
+    public static Long sAdd(String key, String value) {
+        return redisTemplate.opsForSet().add(key, value);
+    }
+
+    // 获取Set中的所有元素(用于获取已授权IP列表)- 兼容Java 8
+    public static Set<String> sMembers(String key) {
+        Set<Object> objectSet = redisTemplate.opsForSet().members(key);
+        if (CollectionUtil.isEmpty(objectSet)) {
+            // 替换 Set.of() 为 new HashSet<>()(Java 8支持)
+            return new HashSet<>();
+        }
+        // 转换为String类型Set
+        return objectSet.stream().map(String::valueOf).collect(Collectors.toSet());
+    }
 }
diff --git a/trading-order-security-common/src/main/java/com/yami/trading/security/common/bo/UserInfoInTokenBO.java b/trading-order-security-common/src/main/java/com/yami/trading/security/common/bo/UserInfoInTokenBO.java
index ee22b47..4576b1d 100644
--- a/trading-order-security-common/src/main/java/com/yami/trading/security/common/bo/UserInfoInTokenBO.java
+++ b/trading-order-security-common/src/main/java/com/yami/trading/security/common/bo/UserInfoInTokenBO.java
@@ -12,6 +12,7 @@
 import com.yami.trading.security.common.enums.SysTypeEnum;
 import lombok.Data;
 
+import java.io.Serializable;
 import java.util.Set;
 
 /**
@@ -21,7 +22,8 @@
  * @date 2022/3/25 17:33
  */
 @Data
-public class UserInfoInTokenBO {
+public class UserInfoInTokenBO implements Serializable { // 新增:实现Serializable接口
+    private static final long serialVersionUID = 1L;
 
     /**
      * 用户在自己系统的用户id
@@ -66,4 +68,7 @@
      */
     private Long otherId;
 
+    // 新增:无参构造函数(Jackson序列化必须,Lombok @Data不默认生成)
+    public UserInfoInTokenBO() {}
+
 }
diff --git a/trading-order-security-common/src/main/java/com/yami/trading/security/common/filter/AuthFilter.java b/trading-order-security-common/src/main/java/com/yami/trading/security/common/filter/AuthFilter.java
index 7486460..3e94f38 100644
--- a/trading-order-security-common/src/main/java/com/yami/trading/security/common/filter/AuthFilter.java
+++ b/trading-order-security-common/src/main/java/com/yami/trading/security/common/filter/AuthFilter.java
@@ -3,10 +3,7 @@
 import java.io.IOException;
 import java.net.URLDecoder;
 import java.time.ZoneId;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Objects;
+import java.util.*;
 import java.util.concurrent.ConcurrentHashMap;
 
 import javax.servlet.Filter;
@@ -203,10 +200,15 @@
                 // 如果有 token,并且解析成功,则走以下处理逻辑
                 if (userInfoInToken.getSysType().intValue() == SysTypeEnum.ADMIN.value().intValue()) {
                     if (!pathMatcher.match("/updateCheckIp", requestUri)) {
-                        Object loginIP = RedisUtil.get(RedisKeys.ACCESS_IP + userInfoInToken.getUserId());
-                        if (null != loginIP && !IPHelper.equalIpSegment(loginIP.toString(), clientIp)) {
-                            logger.error("The Login IP Is Inconsistent With The Operation IP! Login-IP:{} Access-IP:{} Servlet-Path:{}", loginIP, clientIp, servletPath);
-                            httpHandler.printServerResponseToWeb("", 1001);
+                        String ipSetKey = RedisKeys.ACCESS_IP + userInfoInToken.getUserId();
+                        // 从Redis Set中获取该用户所有已授权登录IP
+                        Set<String> authorizedIpSet = RedisUtil.sMembers(ipSetKey);
+
+                        // 校验逻辑:已授权IP列表不为空,且当前IP在列表中
+                        if (CollectionUtil.isEmpty(authorizedIpSet) || !authorizedIpSet.contains(clientIp)) {
+                            logger.error("The Operation IP Is Not Authorized! Authorized-IPs:{}, Access-IP:{}, Servlet-Path:{}",
+                                    authorizedIpSet, clientIp, servletPath);
+                            httpHandler.printServerResponseToWeb("当前IP未授权,请使用已登录设备访问或重新登录", 1001);
                             return;
                         }
                     }

--
Gitblit v1.9.3