Bladeren bron

1、调整ip地址注入

yys 1 dag geleden
bovenliggende
commit
2900c98956

+ 145 - 8
fs-user-app/src/main/java/com/fs/app/controller/AppLoginController.java

@@ -18,6 +18,8 @@ import com.fs.common.core.redis.RedisCache;
 import com.fs.common.enums.BusinessType;
 import com.fs.common.exception.ServiceException;
 import com.fs.common.service.ISmsService;
+import com.fs.common.utils.ServletUtils;
+import com.fs.common.utils.ip.IpUtils;
 import com.fs.common.utils.sign.Md5Utils;
 import com.fs.core.config.WxOpenProperties;
 import com.fs.course.domain.LuckyBag;
@@ -61,6 +63,7 @@ import org.springframework.transaction.annotation.Transactional;
 import org.springframework.validation.annotation.Validated;
 import org.springframework.web.bind.annotation.*;
 
+import javax.servlet.http.HttpServletRequest;
 import java.time.LocalDateTime;
 import java.util.*;
 import java.util.concurrent.TimeUnit;
@@ -417,6 +420,7 @@ public class AppLoginController extends AppBaseController{
             }
             // 检测im是否创建用户
             openIMService. accountCheck("U"+user.getUserId(),"1");
+            recordLoginInfo(user.getUserId(), param.getLastIp(), param.getLoginDevice(), param.getSource());
             int isFirstLogin = userNewTaskService.performTaskOne(user.getUserId());
             String token = jwtUtils.generateToken(user.getUserId());
             redisCache.setCacheObject("userToken:" + user.getUserId(), token, 604800, TimeUnit.SECONDS);
@@ -491,6 +495,7 @@ public class AppLoginController extends AppBaseController{
             if (user.getStatus()==0){
                 return R.error("登录失败,账户被禁用");
             }
+            recordLoginInfo(user.getUserId(), param.getLastIp(), param.getLoginDevice(), param.getSource());
             int isFirstLogin = userNewTaskService.performTaskOne(user.getUserId());
             String token = jwtUtils.generateToken(user.getUserId());
             redisCache.setCacheObject("userToken:" + user.getUserId(), token, 604800, TimeUnit.SECONDS);
@@ -580,9 +585,12 @@ public class AppLoginController extends AppBaseController{
         }
 
         FsUser info=user.get(0);
+        String lastIp = map.get("lastIp");
+        String loginDevice = map.get("loginDevice");
+        String source = map.get("source");
 
         if (ObjectUtil.isNotEmpty(isWechatInstalled)&&isWechatInstalled && isInit()){
-            return generateTokenAndReturn(info);
+            return generateTokenAndReturn(info, lastIp, loginDevice, source);
         }
         // 与 /login 一致:无 unionId 时返回 isNew,前端强制绑定微信
         if (info.getUnionId()==null){
@@ -601,7 +609,7 @@ public class AppLoginController extends AppBaseController{
                 userService.updateFsUser(fsUser);
                 logger.info("zyp \n【手机加密】:{}",encryptPhone(phone));
             }
-            return R.ok(generateTokenAndReturn(info)).put("isNew",true).put("phone",encryptPhone(phone));
+            return R.ok(generateTokenAndReturn(info, lastIp, loginDevice, source)).put("isNew",true).put("phone",encryptPhone(phone));
         }
         FsUser userMap = new FsUser();
         userMap.setUserId(info.getUserId());
@@ -615,7 +623,7 @@ public class AppLoginController extends AppBaseController{
         userMap.setUpdateTime(new Date());
         userService.updateFsUser(userMap);
 
-        return generateTokenAndReturn(info);
+        return generateTokenAndReturn(info, lastIp, loginDevice, source);
     }
 
     @PostMapping("/resetPassword")
@@ -793,6 +801,15 @@ public class AppLoginController extends AppBaseController{
     }
 
     private R generateTokenAndReturn(FsUser user) {
+        return generateTokenAndReturn(user, null, null, null);
+    }
+
+    private R generateTokenAndReturn(FsUser user, String lastIp) {
+        return generateTokenAndReturn(user, lastIp, null, null);
+    }
+
+    private R generateTokenAndReturn(FsUser user, String lastIp, String loginDevice, String source) {
+        recordLoginInfo(user.getUserId(), lastIp, loginDevice, source);
         String token = jwtUtils.generateToken(user.getUserId());
         redisCache.setCacheObject("userToken:" + user.getUserId(), token, 604800, TimeUnit.SECONDS);
         int isFirstLogin = userNewTaskService.performTaskOne(user.getUserId());
@@ -803,6 +820,124 @@ public class AppLoginController extends AppBaseController{
         return R.ok(map);
     }
 
+    /**
+     * 解析登录IP:优先外网IP。
+     * 1. 前端传入且为外网IP时直接使用
+     * 2. 否则从请求头(X-Forwarded-For / X-Real-IP 等)中取第一个外网IP
+     * 3. 都拿不到外网IP时再回退到可用IP
+     */
+    private String resolveLoginIp(String lastIp) {
+        if (StringUtils.isNotEmpty(lastIp)) {
+            String clientIp = extractFirstIp(lastIp);
+            if (StringUtils.isNotEmpty(clientIp) && !isInternalIp(clientIp)) {
+                return clientIp;
+            }
+        }
+        try {
+            return getExternalIpFromRequest();
+        } catch (Exception e) {
+            logger.warn("获取登录IP失败:{}", e.getMessage());
+            return StringUtils.isNotEmpty(lastIp) ? extractFirstIp(lastIp) : null;
+        }
+    }
+
+    /**
+     * 从请求中尽量获取外网IP
+     */
+    private String getExternalIpFromRequest() {
+        HttpServletRequest request = ServletUtils.getRequest();
+        if (request == null) {
+            return null;
+        }
+        String[] headerNames = {
+                "X-Forwarded-For",
+                "x-forwarded-for",
+                "X-Real-IP",
+                "Proxy-Client-IP",
+                "WL-Proxy-Client-IP",
+                "HTTP_CLIENT_IP",
+                "HTTP_X_FORWARDED_FOR"
+        };
+        String fallbackIp = null;
+        for (String header : headerNames) {
+            String value = request.getHeader(header);
+            if (StringUtils.isEmpty(value) || "unknown".equalsIgnoreCase(value)) {
+                continue;
+            }
+            // X-Forwarded-For 可能为 "外网IP, 代理IP1, 代理IP2"
+            for (String part : value.split(",")) {
+                String ip = extractFirstIp(part);
+                if (StringUtils.isEmpty(ip) || "unknown".equalsIgnoreCase(ip)) {
+                    continue;
+                }
+                if (!isInternalIp(ip)) {
+                    return ip;
+                }
+                if (fallbackIp == null) {
+                    fallbackIp = ip;
+                }
+            }
+        }
+        String remoteAddr = request.getRemoteAddr();
+        if ("0:0:0:0:0:0:0:1".equals(remoteAddr)) {
+            remoteAddr = "127.0.0.1";
+        }
+        if (StringUtils.isNotEmpty(remoteAddr) && !isInternalIp(remoteAddr)) {
+            return remoteAddr;
+        }
+        return fallbackIp != null ? fallbackIp : remoteAddr;
+    }
+
+    private String extractFirstIp(String ip) {
+        if (StringUtils.isEmpty(ip)) {
+            return null;
+        }
+        String value = ip.trim();
+        if (value.contains(",")) {
+            value = value.split(",")[0].trim();
+        }
+        return value;
+    }
+
+    private boolean isInternalIp(String ip) {
+        if (StringUtils.isEmpty(ip)) {
+            return true;
+        }
+        return IpUtils.internalIp(ip)
+                || "127.0.0.1".equals(ip)
+                || "localhost".equalsIgnoreCase(ip)
+                || "0:0:0:0:0:0:0:1".equals(ip)
+                || "::1".equals(ip);
+    }
+
+    /**
+     * 记录登录信息:lastIp / loginDevice / source(有值才更新)
+     */
+    private void recordLoginInfo(Long userId, String lastIp, String loginDevice, String source) {
+        if (userId == null) {
+            return;
+        }
+        FsUser update = new FsUser();
+        update.setUserId(userId);
+        boolean needUpdate = false;
+        String ip = resolveLoginIp(lastIp);
+        if (StringUtils.isNotEmpty(ip)) {
+            update.setLastIp(ip);
+            needUpdate = true;
+        }
+        if (StringUtils.isNotEmpty(loginDevice)) {
+            update.setLoginDevice(loginDevice);
+            needUpdate = true;
+        }
+        if (StringUtils.isNotEmpty(source)) {
+            update.setSource(source);
+            needUpdate = true;
+        }
+        if (needUpdate) {
+            userService.updateFsUser(update);
+        }
+    }
+
 //    private R handleLoginType1(FsUserLoginParam param) {
 //        if (StringUtils.isEmpty(param.getPhone()) || StringUtils.isEmpty(param.getPassword())) {
 //            return R.error("账号或密码不能为空");
@@ -845,7 +980,7 @@ public class AppLoginController extends AppBaseController{
         if (usersByPhone.size()==1){
             user = usersByPhone.get(0);
             if (ObjectUtil.isNotEmpty(isWechatInstalled)&&isWechatInstalled && isInit()){
-                return generateTokenAndReturn(user);
+                return generateTokenAndReturn(user, param.getLastIp(), param.getLoginDevice(), param.getSource());
             }
             if (user.getUnionId()==null){
                 if (user.getPhone().length()<=11){
@@ -855,7 +990,7 @@ public class AppLoginController extends AppBaseController{
                     userMapper.updateFsUser(fsUser);
                     logger.info("zyp \n【手机加密】:{}",encryptPhone(param.getPhone()));
                 }
-                return R.ok(generateTokenAndReturn(user)).put("isNew",true).put("phone",encryptPhone(param.getPhone()));
+                return R.ok(generateTokenAndReturn(user, param.getLastIp(), param.getLoginDevice(), param.getSource())).put("isNew",true).put("phone",encryptPhone(param.getPhone()));
             }
             // 校验用户是否存在及账号状态
             if (user == null) {
@@ -883,7 +1018,7 @@ public class AppLoginController extends AppBaseController{
             //fsUserTalentService.addFsUserTalent(fsUser.getUserId());
             openIMService. accountCheck("U"+user.getUserId(),"1");
 
-            return generateTokenAndReturn(user);
+            return generateTokenAndReturn(user, param.getLastIp(), param.getLoginDevice(), param.getSource());
         }else {
             return R.ok().put("users",usersByPhone);
         }
@@ -1004,7 +1139,7 @@ public class AppLoginController extends AppBaseController{
         if (usersByPhone.size()==1){
             user = usersByPhone.get(0);
             if (ObjectUtil.isNotEmpty(isWechatInstalled)&&isWechatInstalled && isInit()){
-                return generateTokenAndReturn(user);
+                return generateTokenAndReturn(user, param.getLastIp(), param.getLoginDevice(), param.getSource());
             }
             if (user.getUnionId()==null){
                 if (user.getPhone().length()<=11){
@@ -1014,6 +1149,7 @@ public class AppLoginController extends AppBaseController{
                     userMapper.updateFsUser(fsUser);
                     logger.info("zyp \n【手机加密】:{}",encryptPhone(param.getPhone()));
                 }
+                recordLoginInfo(user.getUserId(), param.getLastIp(), param.getLoginDevice(), param.getSource());
                 return R.ok().put("isNew",true).put("phone",encryptPhone(param.getPhone()));
             }
             if (StringUtils.isNotEmpty(param.getJpushId())) {
@@ -1029,7 +1165,7 @@ public class AppLoginController extends AppBaseController{
             return R.ok().put("users",usersByPhone);
         }
         openIMService. accountCheck("U"+user.getUserId(),"1");
-        return generateTokenAndReturn(user);
+        return generateTokenAndReturn(user, param.getLastIp(), param.getLoginDevice(), param.getSource());
     }
 
    /* @PostMapping("/loginByUserId")
@@ -1057,6 +1193,7 @@ public class AppLoginController extends AppBaseController{
         if (StringUtils.isNotEmpty(param.getJpushId())) {
             newUser.setJpushId(param.getJpushId());
         }
+        newUser.setLastIp(resolveLoginIp(param.getLastIp()));
         userService.insertFsUser(newUser);
         return newUser;
     }

+ 3 - 0
fs-user-app/src/main/java/com/fs/app/param/FsUserLoginByAppleParam.java

@@ -18,4 +18,7 @@ public class FsUserLoginByAppleParam implements Serializable {
 
     private String appleKey;
 
+    /** 登录IP,前端可不传,未传时由后端获取 */
+    private String lastIp;
+
 }

+ 3 - 0
fs-user-app/src/main/java/com/fs/app/param/FsUserLoginByWeChatParam.java

@@ -22,4 +22,7 @@ public class FsUserLoginByWeChatParam implements Serializable {
 
     // 苹果手机微信登录传
     private String appleKey;
+
+    /** 登录IP,前端可不传,未传时由后端获取 */
+    private String lastIp;
 }

+ 3 - 0
fs-user-app/src/main/java/com/fs/app/param/FsUserLoginParam.java

@@ -23,4 +23,7 @@ public class FsUserLoginParam implements Serializable {
 
     private String loginDevice;
     private String source;
+
+    /** 登录IP,前端可不传,未传时由后端获取 */
+    private String lastIp;
 }