Explorar o código

修改:<优化登陆配置流程>

张旭锋 hai 2 días
pai
achega
c800fa481f

+ 3 - 15
src/main/java/com/jtzx/crm/aspect/extension/SysLogAspectExtensionForSmsCodeLogin.java

@@ -1,20 +1,15 @@
 package com.jtzx.crm.aspect.extension;
 
 import cc.uncarbon.framework.web.model.response.ApiResult;
-import cn.hutool.core.collection.CollUtil;
-import cn.hutool.json.JSONUtil;
-import com.google.common.collect.Maps;
 import com.jtzx.crm.module.sys.annotation.SysLog;
 import com.jtzx.crm.module.sys.enums.SysLogStatusEnum;
 import com.jtzx.crm.module.sys.extension.impl.DefaultSysLogAspectExtension;
 import com.jtzx.crm.module.sys.model.request.AdminInsertSysLogDTO;
+import com.jtzx.crm.module.sys.model.request.SysUserSmsCodeDTO;
 import lombok.NoArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
 import org.aspectj.lang.JoinPoint;
 
-import java.util.Map;
-import java.util.concurrent.atomic.AtomicInteger;
-
 /**
  * SysLog 切面实现类扩展 for 登录后台用户
  */
@@ -26,20 +21,13 @@ public class SysLogAspectExtensionForSmsCodeLogin extends DefaultSysLogAspectExt
     public void beforeSaving(AdminInsertSysLogDTO insertSysLogDTO, JoinPoint joinPoint, SysLog annotation, Throwable e, Object ret) {
         log.info("短信发送拦截器执行");
         String username = null;
-        Map<String, Object> map = Maps.newHashMap();
-        AtomicInteger code = new AtomicInteger(0);
         for (Object arg : joinPoint.getArgs()) {
-            code.getAndIncrement();
-            if(code.get() == 1){
-                username = (String) arg;
+            if (arg instanceof SysUserSmsCodeDTO dto) {
+                username = dto.getUsername();
             }
-            map.put("param" + code.get(), arg);
         }
         insertSysLogDTO.setUsername(username);
         insertSysLogDTO.setLogType(1);
-        if (CollUtil.isNotEmpty(map)) {
-            insertSysLogDTO.setParams(JSONUtil.toJsonStr(map));
-        }
         if (ret != null) {
             if (ret instanceof ApiResult apiResult) {
                 insertSysLogDTO.setStatus(SysLogStatusEnum.SUCCESS);

+ 2 - 20
src/main/java/com/jtzx/crm/aspect/extension/SysLogAspectExtensionForSysUserAccountLogin.java

@@ -1,20 +1,14 @@
 package com.jtzx.crm.aspect.extension;
 
 import cc.uncarbon.framework.web.model.response.ApiResult;
-import cn.hutool.core.collection.CollUtil;
-import cn.hutool.json.JSONUtil;
-import com.google.common.collect.Maps;
 import com.jtzx.crm.module.sys.annotation.SysLog;
 import com.jtzx.crm.module.sys.enums.SysLogStatusEnum;
 import com.jtzx.crm.module.sys.extension.impl.DefaultSysLogAspectExtension;
 import com.jtzx.crm.module.sys.model.request.AdminInsertSysLogDTO;
-import com.jtzx.crm.module.sys.model.request.SysUserLoginDTO;
+import com.jtzx.crm.module.sys.model.request.SysUserAccountLoginDTO;
 import lombok.NoArgsConstructor;
 import org.aspectj.lang.JoinPoint;
 
-import java.util.Map;
-import java.util.concurrent.atomic.AtomicInteger;
-
 /**
  * SysLog 切面实现类扩展 for 登录后台用户
  */
@@ -24,25 +18,13 @@ public class SysLogAspectExtensionForSysUserAccountLogin extends DefaultSysLogAs
     @Override
     public void beforeSaving(AdminInsertSysLogDTO insertSysLogDTO, JoinPoint joinPoint, SysLog annotation, Throwable e, Object ret) {
         String username = null;
-        Map<String, Object> map = Maps.newHashMap();
-        AtomicInteger code = new AtomicInteger(0);
         for (Object arg : joinPoint.getArgs()) {
-            if (arg instanceof SysUserLoginDTO dto) {
+            if (arg instanceof SysUserAccountLoginDTO dto) {
                 username = dto.getUsername();
-
-            } else {
-                code.getAndIncrement();
-                if(code.get() == 1){
-                    username = (String) arg;
-                }
-                map.put("param" + code.get(), arg);
             }
         }
         insertSysLogDTO.setUsername(username);
         insertSysLogDTO.setLogType(0);
-        if (CollUtil.isNotEmpty(map)) {
-            insertSysLogDTO.setParams(JSONUtil.toJsonStr(map));
-        }
         if (ret != null) {
             if (ret instanceof ApiResult apiResult) {
                 insertSysLogDTO.setStatus(SysLogStatusEnum.SUCCESS);

+ 15 - 15
src/main/java/com/jtzx/crm/aspect/extension/SysLogAspectExtensionForSysUserSmsCodeLogin.java

@@ -1,19 +1,14 @@
 package com.jtzx.crm.aspect.extension;
 
 import cc.uncarbon.framework.web.model.response.ApiResult;
-import cn.hutool.core.collection.CollUtil;
-import cn.hutool.json.JSONUtil;
-import com.google.common.collect.Maps;
 import com.jtzx.crm.module.sys.annotation.SysLog;
 import com.jtzx.crm.module.sys.enums.SysLogStatusEnum;
 import com.jtzx.crm.module.sys.extension.impl.DefaultSysLogAspectExtension;
 import com.jtzx.crm.module.sys.model.request.AdminInsertSysLogDTO;
+import com.jtzx.crm.module.sys.model.request.SysUserSmsCodeLoginDTO;
 import lombok.NoArgsConstructor;
 import org.aspectj.lang.JoinPoint;
 
-import java.util.Map;
-import java.util.concurrent.atomic.AtomicInteger;
-
 /**
  * SysLog 切面实现类扩展 for 登录后台用户
  */
@@ -23,20 +18,25 @@ public class SysLogAspectExtensionForSysUserSmsCodeLogin extends DefaultSysLogAs
     @Override
     public void beforeSaving(AdminInsertSysLogDTO insertSysLogDTO, JoinPoint joinPoint, SysLog annotation, Throwable e, Object ret) {
         String username = null;
-        Map<String, Object> map = Maps.newHashMap();
-        AtomicInteger code = new AtomicInteger(0);
+//        Map<String, Object> map = Maps.newHashMap();
+//        AtomicInteger code = new AtomicInteger(0);
         for (Object arg : joinPoint.getArgs()) {
-            code.getAndIncrement();
-            if(code.get() == 1){
-                username = (String) arg;
+
+            if (arg instanceof SysUserSmsCodeLoginDTO dto) {
+                username = dto.getUsername();
             }
-            map.put("param" + code.get(), arg);
+
+//            code.getAndIncrement();
+//            if(code.get() == 1){
+//                username = (String) arg;
+//            }
+//            map.put("param" + code.get(), arg);
         }
         insertSysLogDTO.setUsername(username);
         insertSysLogDTO.setLogType(2);
-        if (CollUtil.isNotEmpty(map)) {
-            insertSysLogDTO.setParams(JSONUtil.toJsonStr(map));
-        }
+//        if (CollUtil.isNotEmpty(map)) {
+//            insertSysLogDTO.setParams(JSONUtil.toJsonStr(map));
+//        }
         if (ret != null) {
             if (ret instanceof ApiResult apiResult) {
                 insertSysLogDTO.setStatus(SysLogStatusEnum.SUCCESS);

+ 2 - 2
src/main/java/com/jtzx/crm/module/api/constant/ApiConstant.java

@@ -14,8 +14,8 @@ public final class ApiConstant {
     public static final String HTTP_API_URL_PREFIX = "/admin";
 
     /**
-     * 账号密码登陆之后用户信息在redis中保存的时长
+     * 账号密码登陆之后用户信息在redis中保存的时长,2个小时
      */
-    public static final Integer LOGIN_USER_INFO = 1800;
+    public static final Integer LOGIN_USER_INFO = 7200;
 
 }

+ 5 - 1
src/main/java/com/jtzx/crm/module/api/constant/LimitConstant.java

@@ -18,12 +18,15 @@ public final class LimitConstant {
     public static final Integer login_fail_count=3;
 
 
+
     //短信封禁时长
     public static final Integer smsCode_duration = 300;
     //对应的提示信息
     public static final String  smsCode_Str = "5分钟";
     //短信的最大次数
     public static final Integer smsCode_max = 3;
+    public static final Integer sms_code_fail_count=1;
+
 
 
     //短信登录封禁时长
@@ -31,5 +34,6 @@ public final class LimitConstant {
     //对应的提示信息
     public static final String  smsCode_login_Str = "10分钟";
     //短信登录的最大次数
-    public static final Integer smsCode_login_max = 3;
+    public static final Integer smsCode_login_max = 5;
+    public static final Integer sms_code_login_fail_count=3;
 }

+ 1 - 1
src/main/java/com/jtzx/crm/module/api/constant/RedisConstant.java

@@ -24,7 +24,7 @@ public final class RedisConstant {
 
 
     public static final String SMS_CODE_PREFIX = "sms-code:";
-    public static final String LOGIN_USER_INFO_PREFIX = "userInfo:";
+//    public static final String LOGIN_USER_INFO_PREFIX = "userInfo:";
 
     /**
      * 短信验证码的长度

+ 22 - 20
src/main/java/com/jtzx/crm/module/api/ratelimit/AccountRateLimitStrategy.java

@@ -5,12 +5,11 @@ import cc.uncarbon.framework.ratelimit.annotation.UseRateLimit;
 import cc.uncarbon.framework.ratelimit.constant.RateLimitConstant;
 import cc.uncarbon.framework.ratelimit.stratrgy.RateLimitStrategy;
 import cn.hutool.core.date.DateTime;
-import cn.hutool.core.date.DateUnit;
 import cn.hutool.core.date.DateUtil;
 import cn.hutool.core.text.CharSequenceUtil;
 import com.jtzx.crm.module.api.constant.LimitConstant;
 import com.jtzx.crm.module.api.constant.RedisConstant;
-import com.jtzx.crm.module.sys.model.request.SysUserLoginDTO;
+import com.jtzx.crm.module.sys.model.request.SysUserAccountLoginDTO;
 import lombok.extern.slf4j.Slf4j;
 import org.aspectj.lang.JoinPoint;
 import org.springframework.data.redis.core.RedisTemplate;
@@ -32,27 +31,30 @@ public class AccountRateLimitStrategy extends CustomRedisBasedRateLimitStrategy
     }
 
 
-    public RateLimitedException rateLimitedExceptionSupplier(String dateTimeStr) {
-//        return new RateLimitedException(429,"发送次数已超"+ LimitConstant.login_max +"次限制,请"+LimitConstant.login_Str+"之后再试!");
+    public RateLimitedException rateLimitedExceptionSupplier(UseRateLimit annotation, JoinPoint point,String username) {
+        String redisKey = determineRedisKey(annotation, point);
+        //获取请求的次数
+        Object object = stringSetRedisTemplate.opsForValue().get(redisKey);
+        DateTime dateTime = DateUtil.offsetSecond(DateUtil.date(), annotation.duration());
+        String dateTimeStr = DateUtil.formatDateTime(dateTime);
+        if(object!=null){
+            Integer commitCount = (Integer) object;
+            if(commitCount>=annotation.max()){
+                // 获取有效期内最后一次请求时间
+                Object object1 = stringSetRedisTemplate.opsForValue().get(RedisConstant.ACCOUNT_LOGIN_FAILED_END_DATETIME_PREFIX + username);
+                if(object1 != null){
+                    dateTimeStr = (String) object1;
+                }
+            }
+        }
         return new RateLimitedException(429, "操作频率过快,已超" + LimitConstant.login_max + "次限制,请在" + dateTimeStr + "之后再试!");
     }
 
     @Override
     public void performRateLimitCheck(UseRateLimit annotation, JoinPoint point) {
-        String username = currentClientAccount(point);
-        Object object = stringSetRedisTemplate.opsForValue()
-                .get(RedisConstant.ACCOUNT_LOGIN_FAILED_END_DATETIME_PREFIX + username);
-        Integer customDruation = null;
-        String dateTimeStr;
-        if (object != null) {
-            dateTimeStr = (String) object;
-            customDruation = Math.toIntExact(DateUtil.between(DateUtil.date(), DateUtil.parseDate(dateTimeStr), DateUnit.SECOND));
-        } else {
-            //计算结束时间
-            DateTime dateTime = DateUtil.offsetSecond(DateUtil.date(), LimitConstant.login_duration);
-            dateTimeStr = DateUtil.formatDateTime(dateTime);
-        }
-        super.performRateLimitCheck(annotation, point, customDruation, this.rateLimitedExceptionSupplier(dateTimeStr));
+        String username = this.currentClientAccount(point);
+        super.performRateLimitCheck(annotation, point,username,  this.rateLimitedExceptionSupplier(annotation,point,username),
+                RedisConstant.ACCOUNT_LOGIN_FAILED_END_DATETIME_PREFIX + username);
     }
 
 
@@ -71,9 +73,9 @@ public class AccountRateLimitStrategy extends CustomRedisBasedRateLimitStrategy
             if (args != null) {
                 for (Object arg : args) {
                     // 精准匹配你的登录 DTO 对象的类名
-                    if (arg != null && arg.getClass().getName().contains("SysUserLoginDTO")) {
+                    if (arg != null && arg.getClass().getName().contains("SysUserAccountLoginDTO")) {
                         // 方案 A:如果引入了该 DTO 的包,可以直接强转
-                        SysUserLoginDTO dto = (SysUserLoginDTO) arg;
+                        SysUserAccountLoginDTO dto = (SysUserAccountLoginDTO) arg;
                         return dto.getUsername();
 
                         // 方案 B:为了绝对防止包引用和编译报错,用 Jackson 将其无缝转成 JsonNode 读取

+ 14 - 8
src/main/java/com/jtzx/crm/module/api/ratelimit/CustomRedisBasedRateLimitStrategy.java

@@ -3,6 +3,8 @@ package com.jtzx.crm.module.api.ratelimit;
 import cc.uncarbon.framework.core.exception.RateLimitStrategyException;
 import cc.uncarbon.framework.core.exception.RateLimitedException;
 import cc.uncarbon.framework.ratelimit.annotation.UseRateLimit;
+import cn.hutool.core.date.DateTime;
+import cn.hutool.core.date.DateUtil;
 import cn.hutool.core.text.CharSequenceUtil;
 import cn.hutool.core.text.StrPool;
 import lombok.RequiredArgsConstructor;
@@ -55,21 +57,25 @@ public abstract class CustomRedisBasedRateLimitStrategy {
     private static final RedisScript<Long> REDIS_SCRIPT = RedisScript.of(LUA_SCRIPT, Long.class);
 
 
-    protected void performRateLimitCheck(UseRateLimit annotation, JoinPoint point, Integer customDruation,
-                                         RateLimitedException rateLimitedExceptionSupplier)
+    protected void performRateLimitCheck(UseRateLimit annotation, JoinPoint point,String account,
+                                         RateLimitedException rateLimitedExceptionSupplier,String customRedisKey)
             throws RateLimitStrategyException {
         int duration = annotation.duration();
-        if (customDruation != null) {
-            duration = customDruation;
-        }
         int max = annotation.max();
         String redisKey = determineRedisKey(annotation, point);
         Long current = objectRedisTemplate.execute(REDIS_SCRIPT, Collections.singletonList(redisKey), max, duration);
-        //新增操作保存请求次数到redis中,这和框架中没有的
-        objectRedisTemplate.opsForValue().setIfAbsent(redisKey, current, duration, TimeUnit.SECONDS);
-        if (current > max) {
+        if (current.intValue() > max) {
             log.info("[UseRateLimit][{}] 键名 {} 已触达限流阈值 {}", realClassName, redisKey, max);
             throw rateLimitedExceptionSupplier;
+        }else {
+            //新增操作保存请求次数到redis中,这和框架中没有的
+            objectRedisTemplate.opsForValue().set(redisKey, current, duration, TimeUnit.SECONDS);
+            DateTime dateTime = DateUtil.offsetSecond(DateUtil.date(), annotation.duration());
+            objectRedisTemplate.opsForValue()
+                .set(customRedisKey,
+                        DateUtil.formatDateTime(dateTime),
+                        annotation.duration(),
+                        TimeUnit.SECONDS);
         }
         log.info("[UseRateLimit][{}] 键名 {} 限流进度 {}/{}", realClassName, redisKey, current, max);
     }

+ 45 - 34
src/main/java/com/jtzx/crm/module/api/ratelimit/SmsCodeLoginRateLimitStrategy.java

@@ -1,22 +1,19 @@
 package com.jtzx.crm.module.api.ratelimit;
 
-import cc.uncarbon.framework.core.context.UserContextHolder;
 import cc.uncarbon.framework.core.exception.RateLimitedException;
 import cc.uncarbon.framework.ratelimit.annotation.UseRateLimit;
 import cc.uncarbon.framework.ratelimit.constant.RateLimitConstant;
 import cc.uncarbon.framework.ratelimit.stratrgy.RateLimitStrategy;
 import cn.hutool.core.date.DateTime;
-import cn.hutool.core.date.DateUnit;
 import cn.hutool.core.date.DateUtil;
 import cn.hutool.core.text.CharSequenceUtil;
 import com.jtzx.crm.module.api.constant.LimitConstant;
 import com.jtzx.crm.module.api.constant.RedisConstant;
+import com.jtzx.crm.module.sys.model.request.SysUserSmsCodeLoginDTO;
 import lombok.extern.slf4j.Slf4j;
 import org.aspectj.lang.JoinPoint;
 import org.springframework.data.redis.core.RedisTemplate;
 import org.springframework.stereotype.Component;
-import org.springframework.web.context.request.RequestContextHolder;
-import org.springframework.web.context.request.ServletRequestAttributes;
 
 
 /**
@@ -30,53 +27,67 @@ public class SmsCodeLoginRateLimitStrategy extends CustomRedisBasedRateLimitStra
 
     public SmsCodeLoginRateLimitStrategy(RedisTemplate<String, Object> objectRedisTemplate) {
         super(objectRedisTemplate, "SmsCodeLoginRateLimitStrategy");
-        this.stringSetRedisTemplate=objectRedisTemplate;
+        this.stringSetRedisTemplate = objectRedisTemplate;
     }
 
-    public RateLimitedException rateLimitedExceptionSupplier(String dateTimeStr) {
-//        return new RateLimitedException("发送次数已超"+ LimitConstant.smsCode_login_max +"次限制,请"+LimitConstant.smsCode_login_Str+"之后再试!");
-        return new RateLimitedException("操作频率过快,已超"+ LimitConstant.smsCode_login_max +"次限制,请在"+dateTimeStr+"之后再试!");
+    public RateLimitedException rateLimitedExceptionSupplier(UseRateLimit annotation, JoinPoint point,String mobile) {
+        String redisKey = determineRedisKey(annotation, point);
+        //获取请求的次数
+        Object object = stringSetRedisTemplate.opsForValue().get(redisKey);
+        DateTime dateTime = DateUtil.offsetSecond(DateUtil.date(), LimitConstant.login_duration);
+        String dateTimeStr = DateUtil.formatDateTime(dateTime);
+        if(object!=null){
+            Integer commitCount = (Integer) object;
+            if(commitCount>=annotation.max()){
+                // 获取有效期内最后一次请求时间
+                Object object1 = stringSetRedisTemplate.opsForValue().get(RedisConstant.SMS_CODE_LOGIN_FAILED_END_DATETIME_PREFIX + mobile);
+                if(object1 != null){
+                    dateTimeStr = (String) object1;
+                }
+            }
+        }
+        return new RateLimitedException("操作频率过快,已超" + LimitConstant.smsCode_login_max + "次限制,请在" + dateTimeStr + "之后再试!");
     }
 
     @Override
     public void performRateLimitCheck(UseRateLimit annotation, JoinPoint point) {
-        String mobile = currentClientMobile();
-        Object object = stringSetRedisTemplate.opsForValue()
-                .get(RedisConstant.SMS_CODE_LOGIN_FAILED_END_DATETIME_PREFIX + mobile);
-        Integer customDruation = null;
-        String dateTimeStr;
-        if (object != null) {
-            dateTimeStr = (String) object;
-            customDruation = Math.toIntExact(DateUtil.between(DateUtil.date(), DateUtil.parseDate(dateTimeStr), DateUnit.SECOND));
-        }else {
-            //计算结束时间
-            DateTime dateTime = DateUtil.offsetSecond(DateUtil.date(), LimitConstant.smsCode_login_duration);
-            dateTimeStr = DateUtil.formatDateTime(dateTime);
-        }
-        super.performRateLimitCheck(annotation, point, customDruation,this.rateLimitedExceptionSupplier(dateTimeStr));
+        String mobile = this.currentClientMobile(point);
+        super.performRateLimitCheck(annotation, point,mobile,this.rateLimitedExceptionSupplier(),
+                RedisConstant.SMS_CODE_LOGIN_FAILED_END_DATETIME_PREFIX + mobile);
     }
 
 
     @Override
     protected String determineRedisKey(UseRateLimit annotation, JoinPoint point) {
-        String redisKey = CharSequenceUtil.format("{}smsCodeLogin:{}:{}", RedisConstant.SMS_CODE_LOGIN_REDIS_KEY_PREFIX, currentClientMobile(), determineMark(annotation, point));
+        String redisKey = CharSequenceUtil.format("{}smsCodeLogin:{}:{}", RedisConstant.SMS_CODE_LOGIN_REDIS_KEY_PREFIX, currentClientMobile(point), determineMark(annotation, point));
         log.info("redisKey--------->{}", redisKey);
         return redisKey;
     }
 
 
-    protected String currentClientMobile() {
-        String clientIP = UserContextHolder.getClientIP();
-        if (CharSequenceUtil.isNotEmpty(clientIP)) {
-            return clientIP;
-        }
+    protected String currentClientMobile(JoinPoint point) {
         try {
-            ServletRequestAttributes servletRequestAttributes =
-                    (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
-            if (servletRequestAttributes != null) {
-//                String smsCode = servletRequestAttributes.getRequest().getParameter("smsCode");
-//                return servletRequestAttributes.getRequest().getParameter("mobile")+"-"+smsCode;
-                return servletRequestAttributes.getRequest().getParameter("mobile");
+            // 从 AOP 的 JoinPoint 中直接获取接口的入参列表
+            Object[] args = point.getArgs();
+            if (args != null) {
+                for (Object arg : args) {
+                    // 精准匹配你的登录 DTO 对象的类名
+                    if (arg != null && arg.getClass().getName().contains("SysUserSmsCodeLoginDTO")) {
+                        // 方案 A:如果引入了该 DTO 的包,可以直接强转
+                        SysUserSmsCodeLoginDTO dto = (SysUserSmsCodeLoginDTO) arg;
+                        return dto.getMobile();
+
+                        // 方案 B:为了绝对防止包引用和编译报错,用 Jackson 将其无缝转成 JsonNode 读取
+//                        ObjectMapper objectMapper = new ObjectMapper();
+//                        JsonNode jsonNode = objectMapper.valueToTree(arg);
+//                        if (jsonNode != null && jsonNode.has("username")) {
+//                            String username = jsonNode.get("username").asText();
+//                            if (CharSequenceUtil.isNotBlank(username)) {
+//                                return username;
+//                            }
+//                        }
+                    }
+                }
             }
         } catch (Exception | NoClassDefFoundError e) {
             // 可能没有引入 helio-starter-web 依赖,忽略

+ 53 - 33
src/main/java/com/jtzx/crm/module/api/ratelimit/SmsCodeRateLimitStrategy.java

@@ -1,22 +1,19 @@
 package com.jtzx.crm.module.api.ratelimit;
 
-import cc.uncarbon.framework.core.context.UserContextHolder;
 import cc.uncarbon.framework.core.exception.RateLimitedException;
 import cc.uncarbon.framework.ratelimit.annotation.UseRateLimit;
 import cc.uncarbon.framework.ratelimit.constant.RateLimitConstant;
 import cc.uncarbon.framework.ratelimit.stratrgy.RateLimitStrategy;
 import cn.hutool.core.date.DateTime;
-import cn.hutool.core.date.DateUnit;
 import cn.hutool.core.date.DateUtil;
 import cn.hutool.core.text.CharSequenceUtil;
 import com.jtzx.crm.module.api.constant.LimitConstant;
 import com.jtzx.crm.module.api.constant.RedisConstant;
+import com.jtzx.crm.module.sys.model.request.SysUserSmsCodeDTO;
 import lombok.extern.slf4j.Slf4j;
 import org.aspectj.lang.JoinPoint;
 import org.springframework.data.redis.core.RedisTemplate;
 import org.springframework.stereotype.Component;
-import org.springframework.web.context.request.RequestContextHolder;
-import org.springframework.web.context.request.ServletRequestAttributes;
 
 
 /**
@@ -32,50 +29,73 @@ public class SmsCodeRateLimitStrategy extends CustomRedisBasedRateLimitStrategy
         this.stringSetRedisTemplate = objectRedisTemplate;
     }
 
-    public RateLimitedException rateLimitedExceptionSupplier(String dateTimeStr) {
-//        return new RateLimitedException("发送次数已超"+ LimitConstant.smsCode_max +"次限制,请"+LimitConstant.smsCode_Str+"之后再试!");
+    public RateLimitedException rateLimitedExceptionSupplier(UseRateLimit annotation, JoinPoint point,String mobile) {
+        String redisKey = determineRedisKey(annotation, point);
+        //获取请求的次数
+        Object object = stringSetRedisTemplate.opsForValue().get(redisKey);
+        DateTime dateTime = DateUtil.offsetSecond(DateUtil.date(), LimitConstant.login_duration);
+        String dateTimeStr = DateUtil.formatDateTime(dateTime);
+        if(object!=null){
+            Integer commitCount = (Integer) object;
+            if(commitCount>=annotation.max()){
+                // 获取有效期内最后一次请求时间
+                Object object1 = stringSetRedisTemplate.opsForValue().get(RedisConstant.SMS_CODE_FAILED_END_DATETIME_PREFIX + mobile);
+                if(object1 != null){
+                    dateTimeStr = (String) object1;
+                }
+            }
+        }
         return new RateLimitedException("操作频率过快,已超" + LimitConstant.smsCode_max + "次限制,请在" + dateTimeStr + "之后再试!");
     }
 
     @Override
     public void performRateLimitCheck(UseRateLimit annotation, JoinPoint point) {
-        String mobile = currentClientMobile();
-        Object object = stringSetRedisTemplate.opsForValue()
-                .get(RedisConstant.SMS_CODE_FAILED_END_DATETIME_PREFIX + mobile);
-        Integer customDruation = null;
-        String dateTimeStr;
-        if (object != null) {
-            dateTimeStr = (String) object;
-            customDruation = Math.toIntExact(DateUtil.between(DateUtil.date(), DateUtil.parseDate(dateTimeStr), DateUnit.SECOND));
-        } else {
-            //计算结束时间
-            DateTime dateTime = DateUtil.offsetSecond(DateUtil.date(), LimitConstant.smsCode_duration);
-            dateTimeStr = DateUtil.formatDateTime(dateTime);
-        }
-        super.performRateLimitCheck(annotation, point, customDruation, this.rateLimitedExceptionSupplier(dateTimeStr));
+        String mobile = this.currentClientMobile(point);
+        super.performRateLimitCheck(annotation, point, mobile, this.rateLimitedExceptionSupplier(annotation,point,mobile),
+                RedisConstant.SMS_CODE_FAILED_END_DATETIME_PREFIX + mobile);
     }
 
 
     @Override
     protected String determineRedisKey(UseRateLimit annotation, JoinPoint point) {
-        return CharSequenceUtil.format("{}mobile:{}:{}", RedisConstant.SMS_CODE_REDIS_KEY_PREFIX, currentClientMobile(), determineMark(annotation, point));
+        return CharSequenceUtil.format("{}mobile:{}:{}", RedisConstant.SMS_CODE_REDIS_KEY_PREFIX, currentClientMobile(point), determineMark(annotation, point));
     }
 
 
-    protected String currentClientMobile() {
-        String clientIP = UserContextHolder.getClientIP();
-        if (CharSequenceUtil.isNotEmpty(clientIP)) {
-            return clientIP;
-        }
+    protected String currentClientMobile(JoinPoint point) {
         try {
-            ServletRequestAttributes servletRequestAttributes =
-                    (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
-            if (servletRequestAttributes != null) {
-                // 尝试从request中得到用户的手机号
-                // 尝试从request中得到未登录用户的IP地址
-//                String ip = IPUtil.getClientIPAddress(servletRequestAttributes.getRequest(), 0);
-                return servletRequestAttributes.getRequest().getParameter("mobile");
+            // 从 AOP 的 JoinPoint 中直接获取接口的入参列表
+            Object[] args = point.getArgs();
+            if (args != null) {
+                for (Object arg : args) {
+                    // 精准匹配你的登录 DTO 对象的类名
+                    if (arg != null && arg.getClass().getName().contains("SysUserSmsCodeDTO")) {
+                        // 方案 A:如果引入了该 DTO 的包,可以直接强转
+                        SysUserSmsCodeDTO dto = (SysUserSmsCodeDTO) arg;
+                        return dto.getMobile();
+
+                        // 方案 B:为了绝对防止包引用和编译报错,用 Jackson 将其无缝转成 JsonNode 读取
+//                        ObjectMapper objectMapper = new ObjectMapper();
+//                        JsonNode jsonNode = objectMapper.valueToTree(arg);
+//                        if (jsonNode != null && jsonNode.has("username")) {
+//                            String username = jsonNode.get("username").asText();
+//                            if (CharSequenceUtil.isNotBlank(username)) {
+//                                return username;
+//                            }
+//                        }
+                    }
+                }
             }
+
+
+//            ServletRequestAttributes servletRequestAttributes =
+//                    (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
+//            if (servletRequestAttributes != null) {
+//                // 尝试从request中得到用户的手机号
+//                // 尝试从request中得到未登录用户的IP地址
+////                String ip = IPUtil.getClientIPAddress(servletRequestAttributes.getRequest(), 0);
+////                return servletRequestAttributes.getRequest().getParameter("mobile");
+//            }
         } catch (Exception | NoClassDefFoundError e) {
             // 可能没有引入 helio-starter-web 依赖,忽略
         }

+ 113 - 106
src/main/java/com/jtzx/crm/module/api/web/auth/AdminAuthController.java

@@ -1,15 +1,12 @@
 package com.jtzx.crm.module.api.web.auth;
 
 
-import cc.uncarbon.framework.core.constant.HelioConstant;
 import cc.uncarbon.framework.core.context.UserContext;
 import cc.uncarbon.framework.core.context.UserContextHolder;
-import cc.uncarbon.framework.core.exception.BusinessException;
 import cc.uncarbon.framework.ratelimit.annotation.UseRateLimit;
 import cc.uncarbon.framework.web.model.response.ApiResult;
 import cn.dev33.satoken.annotation.SaCheckLogin;
 import cn.dev33.satoken.spring.SpringMVCUtil;
-import cn.hutool.core.collection.CollUtil;
 import cn.hutool.core.date.DateTime;
 import cn.hutool.core.date.DateUtil;
 import cn.hutool.core.lang.Dict;
@@ -21,7 +18,6 @@ import com.jtzx.crm.aspect.extension.SysLogAspectExtensionForSysUserSmsCodeLogin
 import com.jtzx.crm.helper.CaptchaHelper;
 import com.jtzx.crm.helper.RolePermissionCacheHelper;
 import com.jtzx.crm.module.api.constant.ApiConstant;
-import com.jtzx.crm.module.api.constant.CommitConstant;
 import com.jtzx.crm.module.api.constant.LimitConstant;
 import com.jtzx.crm.module.api.constant.RedisConstant;
 import com.jtzx.crm.module.api.model.interior.AdminCaptchaContainer;
@@ -34,14 +30,15 @@ import com.jtzx.crm.module.api.util.CrmAdminStpUtil;
 import com.jtzx.crm.module.api.util.ReqIpUtil;
 import com.jtzx.crm.module.sms.service.SmsService;
 import com.jtzx.crm.module.sys.annotation.SysLog;
-import com.jtzx.crm.module.sys.model.request.SysUserLoginDTO;
+import com.jtzx.crm.module.sys.model.request.SysUserAccountLoginDTO;
+import com.jtzx.crm.module.sys.model.request.SysUserSmsCodeDTO;
+import com.jtzx.crm.module.sys.model.request.SysUserSmsCodeLoginDTO;
 import com.jtzx.crm.module.sys.model.response.SysUserLoginBO;
 import com.jtzx.crm.module.sys.model.response.SysUserLoginVO;
 import com.jtzx.crm.module.sys.service.SysUserService;
 import io.swagger.v3.oas.annotations.Operation;
 import io.swagger.v3.oas.annotations.tags.Tag;
 import jakarta.validation.Valid;
-import jakarta.validation.constraints.Pattern;
 import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
 import org.dromara.sms4j.api.entity.SmsResponse;
@@ -51,7 +48,6 @@ import org.springframework.data.redis.core.RedisTemplate;
 import org.springframework.web.bind.annotation.*;
 
 import java.util.LinkedHashMap;
-import java.util.concurrent.TimeUnit;
 
 
 @Tag(name = "后台管理-鉴权接口")
@@ -81,7 +77,7 @@ public class AdminAuthController {
     @Operation(summary = "第一步账号密码登录")
     @UseRateLimit(duration = 300, max = 5, strategy = AccountRateLimitStrategy.class, mark = "AccountLogin")
     @PostMapping(value = "/auth/accountlogin")
-    public ApiResult<LoginRespDataVO> login(@RequestBody @Valid SysUserLoginDTO dto) {
+    public ApiResult<LoginRespDataVO> accountlogin(@RequestBody @Valid SysUserAccountLoginDTO dto) {
         //限流时redis中的key
         String key = RedisConstant.ACCOUNT_LOGIN_REDIS_KEY_PREFIX + "account:" + dto.getUsername() + ":AccountLogin";
         LoginRespDataVO resultData = this.getResultData(null, key);
@@ -89,152 +85,172 @@ public class AdminAuthController {
         //计算封禁结束时间
         DateTime dateTime = DateUtil.offsetSecond(DateUtil.date(), LimitConstant.login_duration);
         resultData.setEndDateTime(DateUtil.formatDateTime(dateTime));
-        //封禁结束时间存入redis中
-        stringSetRedisTemplate.opsForValue()
-                .set(RedisConstant.ACCOUNT_LOGIN_FAILED_END_DATETIME_PREFIX + dto.getUsername(),
-                        resultData.getEndDateTime(),
-                        LimitConstant.login_duration,
-                        TimeUnit.SECONDS);
-
-        // RPC调用, 失败抛异常, 成功返回用户信息
-        SysUserLoginBO userInfo = sysUserService.adminLogin(dto);
-        if (userInfo == null) {
-            resultData.setStatus(false);
-            //前两次不返回图形验证码,如果第三次还失败,就返回图形验证码
-            if (LimitConstant.login_fail_count > 0 && resultData.getCommitCount() >= LimitConstant.login_fail_count) {
-                resultData.setShowPicCode(true);
-                //返回图形验证码
-                resultData.setCaptchaVO(new AdminCaptchaVO(captchaHelper.generate()));
-                if (resultData.getCommitCount() > LimitConstant.login_fail_count) {
-                    if (StrUtil.isEmpty(dto.getCaptchaId()) || StrUtil.isEmpty(dto.getCaptchaAnswer())) {
-                        return ApiResult.data("图形验证码未填写", resultData).setCode(406);
-                    }
-                    // 登录验证码核验
-                    if (!captchaHelper.validate(dto.getCaptchaId(), dto.getCaptchaAnswer())) {
-                        return ApiResult.data("图形验证码错误", resultData).setCode(406);
-                    }
+        //前两次不返回图形验证码,如果第三次还失败,就返回图形验证码
+        if (LimitConstant.login_fail_count > 0 && resultData.getCommitCount() >= LimitConstant.login_fail_count) {
+            resultData.setShowPicCode(true);
+            //返回图形验证码
+            resultData.setCaptchaVO(new AdminCaptchaVO(captchaHelper.generate()));
+            if (resultData.getCommitCount() > LimitConstant.login_fail_count) {
+                if (StrUtil.isEmpty(dto.getCaptchaId()) || StrUtil.isEmpty(dto.getCaptchaAnswer())) {
+                    return ApiResult.data("图形验证码未填写", resultData).setCode(401);
+                }
+                // 登录验证码核验
+                if (!captchaHelper.validate(dto.getCaptchaId(), dto.getCaptchaAnswer())) {
+                    return ApiResult.data("图形验证码错误", resultData).setCode(401);
                 }
             }
-            return ApiResult.data("账号或密码错误", resultData).setCode(406);
         }
 
-        if (StrUtil.isEmpty(userInfo.getPhoneNo())) {
+        // RPC调用, 失败抛异常, 成功返回用户信息
+        SysUserLoginBO userInfo = sysUserService.adminLogin(dto.getUsername(), dto.getPassword());
+        if (userInfo == null || StrUtil.isEmpty(userInfo.getPhoneNo())) {
             resultData.setStatus(false);
-            return ApiResult.data("账号未设置手机号", resultData).setCode(406);
+            if(userInfo == null){
+                return ApiResult.data("账号或密码错误", resultData).setCode(401);
+            }
+            if (StrUtil.isEmpty(userInfo.getPhoneNo())) {
+                return ApiResult.data("账号未设置手机号", resultData).setCode(401);
+            }
         }
         //设置手机号
         resultData.setMobile(userInfo.getPhoneNo());
-        //记住我
-        userInfo.setRememberMe(dto.getRememberMe());
 
-        //用户数据存入redis,有效期为 5 分钟,超时未进行 发送手机验证码,将无效
-        stringSetRedisTemplate.opsForValue()
-                .set(RedisConstant.LOGIN_USER_INFO_PREFIX + userInfo.getPhoneNo(), userInfo, ApiConstant.LOGIN_USER_INFO, TimeUnit.SECONDS);
         return ApiResult.data("登录成功", resultData);
     }
 
     /**
      * 发送登陆验证码,5分钟最多发3次
-     * @param mobile
      * @return
      */
     @SysLog(value = "第二步后台用户登录发送登录短信验证码", extension = SysLogAspectExtensionForSmsCodeLogin.class, queryIPLocation = true)
     @Operation(summary = "第二步发送登录短信验证码")
-    @GetMapping(value = "/auth/sms/smsCode")
-    @UseRateLimit(duration = 300, max = 3, strategy = SmsCodeRateLimitStrategy.class,mark = "smsCode")
-    public ApiResult<String> smsCode(@RequestParam String username,
-                                     @Pattern(message = "手机号格式有误", regexp = HelioConstant.Regex.CHINA_MAINLAND_PHONE_NO)
-                                     @RequestParam(name = "mobile") String mobile) {
-
-        //限流时redis中的key,这是没啥用
-        String key = RedisConstant.SMS_CODE_REDIS_KEY_PREFIX + "smsCode:" + mobile + ":smsCode";
+    @PostMapping(value = "/auth/sms/smsCode")
+    @UseRateLimit(duration = 300, max = 3, strategy = SmsCodeRateLimitStrategy.class, mark = "smsCode")
+    public ApiResult<LoginRespDataVO> smsCode(@RequestBody SysUserSmsCodeDTO dto) {
+
+        //限流时redis中的key
+        String key = RedisConstant.SMS_CODE_REDIS_KEY_PREFIX + "smsCode:" + dto.getMobile() + ":smsCode";
+        //获取返回对象
+        LoginRespDataVO resultData = this.getResultData(dto.getMobile(), key);
+        resultData.setCommitTopCount(LimitConstant.smsCode_login_max);
         //计算封禁结束时间
         DateTime dateTime = DateUtil.offsetSecond(DateUtil.date(), LimitConstant.smsCode_duration);
         String formatDateTime = DateUtil.formatDateTime(dateTime);
-        //封禁结束时间存入redis中
-        stringSetRedisTemplate.opsForValue()
-                .set(RedisConstant.SMS_CODE_FAILED_END_DATETIME_PREFIX + mobile,
-                        formatDateTime,
-                        LimitConstant.smsCode_duration,
-                        TimeUnit.SECONDS);
-
-        //账号密码redis中存储的用户信息
-        SysUserLoginBO userInfo = this.getLoginUserInfo(mobile);
-        if (!userInfo.getPhoneNo().equals(mobile)) {
-            throw new BusinessException(406, "手机号错误");
+        resultData.setEndDateTime(formatDateTime);
+        if (LimitConstant.sms_code_fail_count > 0 && resultData.getCommitCount() >= LimitConstant.sms_code_fail_count) {
+            resultData.setShowPicCode(true);
+            //返回图形验证码
+            resultData.setCaptchaVO(new AdminCaptchaVO(captchaHelper.generate()));
+            if (resultData.getCommitCount() > LimitConstant.sms_code_fail_count) {
+                if (StrUtil.isEmpty(dto.getCaptchaId()) || StrUtil.isEmpty(dto.getCaptchaAnswer())) {
+                    return ApiResult.data("图形验证码未填写", resultData).setCode(406);
+                }
+                // 登录验证码核验
+                if (!captchaHelper.validate(dto.getCaptchaId(), dto.getCaptchaAnswer())) {
+                    return ApiResult.data("图形验证码错误", resultData).setCode(406);
+                }
+            }
         }
 
+        //账号密码查询用户信息
+//        SysUserLoginBO userInfo = this.getLoginUserInfo(dto.getMobile());
+        SysUserLoginBO userInfo = sysUserService.adminLogin(dto.getUsername(), dto.getPassword());
+        if (userInfo == null || StrUtil.isEmpty(userInfo.getPhoneNo()) || !userInfo.getPhoneNo().equals(dto.getMobile())) {
+            resultData.setStatus(false);
+
+            if (userInfo == null) {
+                //跳到账号密码登陆页面
+                return ApiResult.data("账号不存在", resultData).setCode(401);
+            }
+            if (StrUtil.isEmpty(userInfo.getPhoneNo())) {
+                //跳到账号密码登陆页面
+                return ApiResult.data("手机号不存在", resultData).setCode(401);
+            }
+            if (!userInfo.getPhoneNo().equals(dto.getMobile())) {
+                return ApiResult.data("手机号错误", resultData).setCode(406);
+            }
+        }
         String smsCode = SmsUtils.getRandomInt(RedisConstant.SMS_CODE_LENGTH);
         log.info("smsCode--------->{}", smsCode);
         LinkedHashMap<String, String> messages = Maps.newLinkedHashMap();
         messages.put("code", smsCode);
-        SmsResponse smsResponse = SmsFactory.getSmsBlend().sendMessage(mobile, messages);
+        SmsResponse smsResponse = SmsFactory.getSmsBlend().sendMessage(dto.getMobile(), messages);
         if (smsResponse.isSuccess()) {
-            service.set(RedisConstant.SMS_CODE_PREFIX + mobile, smsCode, RedisConstant.SMS_CODE_DURATION);
-            return ApiResult.data("发送成功");
+            service.set(RedisConstant.SMS_CODE_PREFIX + dto.getMobile(), smsCode, RedisConstant.SMS_CODE_DURATION);
+            return ApiResult.data("发送成功", resultData);
         }
-        return ApiResult.fail(406, "发送失败");
+        return ApiResult.data("发送失败", resultData).setCode(406);
+
     }
 
     @SysLog(value = "第三步后台用户手机验证码登录", extension = SysLogAspectExtensionForSysUserSmsCodeLogin.class, queryIPLocation = true)
     @Operation(summary = "第三步验证码登录")
-    @UseRateLimit(duration = 600, max = 3, strategy = SmsCodeLoginRateLimitStrategy.class, mark = "smsCodeLogin")
-    @GetMapping(value = "/auth/smsCodeLogin")
-    public ApiResult<LoginRespDataVO> smsCodeLogin(@RequestParam String username,
-                                                   @Pattern(message = "手机号格式有误", regexp = HelioConstant.Regex.CHINA_MAINLAND_PHONE_NO) @RequestParam String mobile,
-                                                   @Pattern(message = "验证码格式有误", regexp = CommitConstant.SMS_CODE) @RequestParam String smsCode) {
-
+    @UseRateLimit(duration = 600, max = 5, strategy = SmsCodeLoginRateLimitStrategy.class, mark = "smsCodeLogin")
+    @PostMapping(value = "/auth/smsCodeLogin")
+    public ApiResult<LoginRespDataVO> smsCodeLogin(@RequestBody SysUserSmsCodeLoginDTO dto) {
 
         //限流时redis中的key
-        String key = RedisConstant.SMS_CODE_LOGIN_REDIS_KEY_PREFIX + "smsCodeLogin:" + mobile + ":smsCodeLogin";
+        String key = RedisConstant.SMS_CODE_LOGIN_REDIS_KEY_PREFIX + "smsCodeLogin:" + dto.getMobile() + ":smsCodeLogin";
         LoginRespDataVO resultData = this.getResultData(null, key);
         resultData.setCommitTopCount(LimitConstant.smsCode_login_max);
-        //计算封禁限流结束时间,存入redis中,在限流拦截器中,获取到这个时间,是为了结束时间保持不变,动态修改 duration 的时间,
-        // 修改时间在 CustomRedisBasedRateLimitStrategy 的 performRateLimitCheck 方法中,然后由每一个限流拦截器,拦截之后
-        // 获取这个的结束时间之后,重新计算 duration 时间,重新设定 redis 中的时间
         DateTime dateTime = DateUtil.offsetSecond(DateUtil.date(), LimitConstant.smsCode_login_duration);
         resultData.setEndDateTime(DateUtil.formatDateTime(dateTime));
 
-        //账号密码redis中存储的用户信息
-        SysUserLoginBO userInfo = this.getLoginUserInfo(mobile);
-        //封禁结束时间存入redis中
-        stringSetRedisTemplate.opsForValue()
-                .set(RedisConstant.SMS_CODE_LOGIN_FAILED_END_DATETIME_PREFIX + mobile,
-                        resultData.getEndDateTime(),
-                        LimitConstant.smsCode_login_duration,
-                        TimeUnit.SECONDS);
-
-        if (!userInfo.getPhoneNo().equals(mobile)) {
-            throw new BusinessException(406, "手机号错误");
+        if (LimitConstant.sms_code_login_fail_count > 0 && resultData.getCommitCount() >= LimitConstant.sms_code_login_fail_count) {
+            resultData.setShowPicCode(true);
+            //返回图形验证码
+            resultData.setCaptchaVO(new AdminCaptchaVO(captchaHelper.generate()));
+            if (resultData.getCommitCount() > LimitConstant.sms_code_login_fail_count) {
+                if (StrUtil.isEmpty(dto.getCaptchaId()) || StrUtil.isEmpty(dto.getCaptchaAnswer())) {
+                    return ApiResult.data("图形验证码未填写", resultData).setCode(406);
+                }
+                // 登录验证码核验
+                if (!captchaHelper.validate(dto.getCaptchaId(), dto.getCaptchaAnswer())) {
+                    return ApiResult.data("图形验证码错误", resultData).setCode(406);
+                }
+            }
         }
 
+        SysUserLoginBO userInfo = sysUserService.adminLogin(dto.getUsername(), dto.getPassword());
 
-        Object cacheData = service.get(RedisConstant.SMS_CODE_PREFIX + userInfo.getPhoneNo());
-        if (cacheData == null) {
+        Object cacheData = service.get(RedisConstant.SMS_CODE_PREFIX + dto.getMobile());
+
+        if (userInfo == null || !userInfo.getPhoneNo().equals(dto.getMobile()) || cacheData == null) {
             resultData.setStatus(false);
-            return ApiResult.data("验证码错误", resultData).setCode(406);
+            if (userInfo == null) {
+                //跳到账号密码登陆页面
+                return ApiResult.data("账号不存在", resultData).setCode(401);
+            }
+            if (!userInfo.getPhoneNo().equals(dto.getMobile())) {
+                return ApiResult.data("手机号错误", resultData).setCode(406);
+            }
+            if (cacheData == null) {
+                return ApiResult.data("验证码已过期", resultData).setCode(406);
+            }
+            String code = (String) cacheData;
+            if (!code.equals(dto.getSmsCode())) {
+                return ApiResult.data("验证码错误", resultData).setCode(406);
+            }
         }
-        String code = (String) cacheData;
-        if (!code.equals(smsCode)) {
-            resultData.setStatus(false);
-            return ApiResult.data("验证码错误", resultData).setCode(406);
+
+        if(dto.getRememberMe()==null){
+            dto.setRememberMe(Boolean.FALSE);
         }
+
         // 构造用户上下文
         UserContext userContext = UserContext.builder()
                 .userId(userInfo.getId())
                 .userName(userInfo.getUsername())
                 .userPhoneNo(userInfo.getPhoneNo())
-                .userTypeStr("GroupManager")//分组组长
+                .userTypeStr(dto.getUserType())
                 .extraData(Dict.parse(userInfo))//用户信息保存在扩展数据中
                 .rolesIds(userInfo.getRoleIds())
                 .roles(userInfo.getRoles())
                 .clientIP(ReqIpUtil.getIpAddr(SpringMVCUtil.getRequest()))//获取用户登陆id
                 .build();
-        if (CollUtil.isNotEmpty(userInfo.getRoles()) && userInfo.getRoles().contains("Admin")) {
-            userContext.setUserTypeStr("Admin");//管理员
-        }
+
         // 将用户ID注册到 SA-Token ,并附加一些业务字段
-        CrmAdminStpUtil.login(userInfo.getId(), userInfo.getRememberMe());
+        CrmAdminStpUtil.login(userInfo.getId(), dto.getRememberMe());
         CrmAdminStpUtil.getSession().set(UserContext.CAMEL_NAME, userContext);
         // 返回登录token
         SysUserLoginVO tokenInfo = SysUserLoginVO.builder()
@@ -258,15 +274,6 @@ public class AdminAuthController {
     }
 
 
-    public SysUserLoginBO getLoginUserInfo(String mobile) throws BusinessException {
-        //获取redis中的用户信息
-        Object object = stringSetRedisTemplate.opsForValue().get(RedisConstant.LOGIN_USER_INFO_PREFIX + mobile);
-        if (object == null) {
-            throw new BusinessException(407, "登录已超时,需重新登陆");
-        }
-        return (SysUserLoginBO) object;
-    }
-
     public LoginRespDataVO getResultData(String mobile, String key) {
         LoginRespDataVO resultData = LoginRespDataVO.builder().build();
         resultData.setMobile(mobile);

+ 4 - 4
src/main/java/com/jtzx/crm/module/sys/model/request/SysUserLoginDTO.java → src/main/java/com/jtzx/crm/module/sys/model/request/SysUserAccountLoginDTO.java

@@ -20,7 +20,7 @@ import java.io.Serializable;
 @AllArgsConstructor
 @NoArgsConstructor
 @Data
-public class SysUserLoginDTO implements Serializable {
+public class SysUserAccountLoginDTO implements Serializable {
 
     @Schema(description = "账号", requiredMode = Schema.RequiredMode.REQUIRED)
     @Size(min = 5, max = 16, message = "【账号】最短5位,最长16位")
@@ -35,13 +35,13 @@ public class SysUserLoginDTO implements Serializable {
     @Schema(description = "记住我")
     private Boolean rememberMe;
 
-//    @Schema(description = "租户ID(可选,启用多租户后有效)")
-//    private Long tenantId;
-
     @Schema(description = "验证码唯一标识(可选)")
     private String captchaId;
 
     @Schema(description = "验证码答案(可选)")
     private String captchaAnswer;
 
+    @Schema(description = "前端类型,pc,h5,android,ios 等")
+    private String userType;
+
 }

+ 52 - 0
src/main/java/com/jtzx/crm/module/sys/model/request/SysUserSmsCodeDTO.java

@@ -0,0 +1,52 @@
+package com.jtzx.crm.module.sys.model.request;
+
+import cc.uncarbon.framework.core.constant.HelioConstant;
+import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.validation.constraints.NotBlank;
+import jakarta.validation.constraints.Pattern;
+import jakarta.validation.constraints.Size;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import lombok.experimental.Accessors;
+
+import java.io.Serializable;
+
+
+/**
+ * 后台管理-后台用户登录
+ */
+@Accessors(chain = true)
+@Builder
+@AllArgsConstructor
+@NoArgsConstructor
+@Data
+public class SysUserSmsCodeDTO implements Serializable {
+
+    @Schema(description = "账号", requiredMode = Schema.RequiredMode.REQUIRED)
+    @Size(min = 5, max = 16, message = "【账号】最短5位,最长16位")
+    @NotBlank(message = "账号不能为空")
+    private String username;
+
+    @Schema(description = "密码", requiredMode = Schema.RequiredMode.REQUIRED)
+    @Size(min = 5, max = 20, message = "【密码】最短5位,最长20位")
+    @NotBlank(message = "密码不能为空")
+    private String password;
+
+    @Schema(description = "手机号")
+    @Pattern(message = "手机号格式有误", regexp = HelioConstant.Regex.CHINA_MAINLAND_PHONE_NO)
+    @Size(max = 15, message = "【手机号】最长15位")
+    private String mobile;
+
+
+    @Schema(description = "验证码唯一标识(可选)")
+    private String captchaId;
+
+    @Schema(description = "验证码答案(可选)")
+    private String captchaAnswer;
+
+    @Schema(description = "前端类型,pc,h5,android,ios 等")
+    private String userType;
+
+}

+ 61 - 0
src/main/java/com/jtzx/crm/module/sys/model/request/SysUserSmsCodeLoginDTO.java

@@ -0,0 +1,61 @@
+package com.jtzx.crm.module.sys.model.request;
+
+import cc.uncarbon.framework.core.constant.HelioConstant;
+import com.jtzx.crm.module.api.constant.CommitConstant;
+import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.validation.constraints.NotBlank;
+import jakarta.validation.constraints.Pattern;
+import jakarta.validation.constraints.Size;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import lombok.experimental.Accessors;
+
+import java.io.Serializable;
+
+
+/**
+ * 后台管理-后台用户登录
+ */
+@Accessors(chain = true)
+@Builder
+@AllArgsConstructor
+@NoArgsConstructor
+@Data
+public class SysUserSmsCodeLoginDTO implements Serializable {
+
+    @Schema(description = "账号", requiredMode = Schema.RequiredMode.REQUIRED)
+    @Size(min = 5, max = 16, message = "【账号】最短5位,最长16位")
+    @NotBlank(message = "账号不能为空")
+    private String username;
+
+    @Schema(description = "密码", requiredMode = Schema.RequiredMode.REQUIRED)
+    @Size(min = 5, max = 20, message = "【密码】最短5位,最长20位")
+    @NotBlank(message = "密码不能为空")
+    private String password;
+
+    @Schema(description = "记住我")
+    private Boolean rememberMe;
+
+    @Schema(description = "手机号")
+    @Pattern(message = "手机号格式有误", regexp = HelioConstant.Regex.CHINA_MAINLAND_PHONE_NO)
+    @Size(max = 15, message = "【手机号】最长15位")
+    private String mobile;
+
+    @Schema(description = "验证码")
+    @Pattern(message = "手机号格式有误", regexp =  CommitConstant.SMS_CODE)
+    @Size(max = 6,min = 6, message = "【验证码】为6位数字")
+    private String smsCode;
+
+
+    @Schema(description = "验证码唯一标识(可选)")
+    private String captchaId;
+
+    @Schema(description = "验证码答案(可选)")
+    private String captchaAnswer;
+
+    @Schema(description = "前端类型,pc,h5,android,ios 等")
+    private String userType;
+
+}

+ 0 - 3
src/main/java/com/jtzx/crm/module/sys/model/response/SysUserLoginBO.java

@@ -50,7 +50,4 @@ public class SysUserLoginBO implements Serializable {
     @Schema(description = "角色ID-对应权限名 Map")
     private Map<Long, Set<String>> roleIdPermissionMap;
 
-    @Schema(description = "记住我", hidden = true)
-    private Boolean rememberMe;
-
 }

+ 3 - 3
src/main/java/com/jtzx/crm/module/sys/service/SysUserService.java

@@ -152,14 +152,14 @@ public class SysUserService {
     /**
      * 后台管理-登录
      */
-    public SysUserLoginBO adminLogin(SysUserLoginDTO dto) {
+    public SysUserLoginBO adminLogin(String username, String password) {
         // 不要直接提示“账号不存在”或“密码不正确”,避免撞库攻击
-        SysUserEntity sysUserEntity = this.getUserByPin(dto.getUsername());
+        SysUserEntity sysUserEntity = this.getUserByPin(username);
         if (sysUserEntity == null) {
             return null;
         }
 
-        if (!PwdUtil.encrypt(dto.getPassword(), sysUserEntity.getSalt()).equals(sysUserEntity.getPwd())) {
+        if (!PwdUtil.encrypt(password, sysUserEntity.getSalt()).equals(sysUserEntity.getPwd())) {
             return null;
         }