소스 검색

修复APP用户验证码问题

tsurumure 1 개월 전
부모
커밋
d4633a47ab
18개의 변경된 파일121개의 추가작업 그리고 97개의 파일을 삭제
  1. 0 20
      src/main/java/com/backendsys/modules/app/entity/AppAuth.java
  2. 0 14
      src/main/java/com/backendsys/modules/app/entity/AppAuthWechat.java
  3. 0 4
      src/main/java/com/backendsys/modules/app/service/AppUserService.java
  4. 6 7
      src/main/java/com/backendsys/modules/app/user/controller/AppAuthController.java
  5. 2 2
      src/main/java/com/backendsys/modules/app/user/controller/AppUserController.java
  6. 2 2
      src/main/java/com/backendsys/modules/app/user/dao/AppUserDao.java
  7. 22 0
      src/main/java/com/backendsys/modules/app/user/entity/AppAuth.java
  8. 1 2
      src/main/java/com/backendsys/modules/app/user/entity/AppUser.java
  9. 5 6
      src/main/java/com/backendsys/modules/app/user/service/AppAuthService.java
  10. 4 0
      src/main/java/com/backendsys/modules/app/user/service/AppUserService.java
  11. 67 30
      src/main/java/com/backendsys/modules/app/user/service/impl/AppAuthServiceImpl.java
  12. 2 2
      src/main/java/com/backendsys/modules/app/user/service/impl/AppUserServiceImpl.java
  13. 1 1
      src/main/java/com/backendsys/modules/app/user/utils/AuthWechat/AES_Dec.java
  14. 1 1
      src/main/java/com/backendsys/modules/app/user/utils/AuthWechat/AES_Enc.java
  15. 1 1
      src/main/java/com/backendsys/modules/app/user/utils/AuthWechat/RSA_Sign.java
  16. 1 1
      src/main/java/com/backendsys/modules/app/user/utils/AuthWechat/RSA_Verify.java
  17. 1 1
      src/main/java/com/backendsys/modules/app/user/utils/AuthWechat/WechatUtil.java
  18. 5 3
      src/main/java/com/backendsys/modules/system/service/impl/SysAuthServiceImpl.java

+ 0 - 20
src/main/java/com/backendsys/modules/app/entity/AppAuth.java

@@ -1,20 +0,0 @@
-package com.backendsys.modules.app.entity;
-
-import com.baomidou.mybatisplus.annotation.TableField;
-import jakarta.validation.constraints.NotEmpty;
-import lombok.Data;
-
-@Data
-public class AppAuth {
-
-    public static interface Login{}
-
-    @NotEmpty(message = "手机号码不能为空", groups = { Login.class })
-    private String phone;
-    @NotEmpty(message = "密码不能为空", groups = { Login.class })
-    private String password;
-    @TableField(exist = false)
-    @NotEmpty(message = "验证码不能为空", groups = { Login.class })
-    private String captcha;
-
-}

+ 0 - 14
src/main/java/com/backendsys/modules/app/entity/AppAuthWechat.java

@@ -1,14 +0,0 @@
-package com.backendsys.modules.app.entity;
-
-import jakarta.validation.constraints.NotEmpty;
-import lombok.Data;
-
-@Data
-public class AppAuthWechat {
-
-    public static interface Login{}
-
-    @NotEmpty(message = "code 不能为空", groups = { Login.class })
-    private String code;
-
-}

+ 0 - 4
src/main/java/com/backendsys/modules/app/service/AppUserService.java

@@ -1,4 +0,0 @@
-package com.backendsys.modules.app.service;
-
-public interface AppUserService {
-}

+ 6 - 7
src/main/java/com/backendsys/modules/app/controller/AppAuthController.java → src/main/java/com/backendsys/modules/app/user/controller/AppAuthController.java

@@ -1,8 +1,7 @@
-package com.backendsys.modules.app.controller;
+package com.backendsys.modules.app.user.controller;
 
-import com.backendsys.modules.app.entity.AppAuth;
-import com.backendsys.modules.app.entity.AppAuthWechat;
-import com.backendsys.modules.app.service.AppAuthService;
+import com.backendsys.modules.app.user.entity.AppAuth;
+import com.backendsys.modules.app.user.service.AppAuthService;
 import com.backendsys.modules.common.config.security.annotations.Anonymous;
 import com.backendsys.modules.common.utils.Result;
 import io.swagger.v3.oas.annotations.Operation;
@@ -35,15 +34,15 @@ public class AppAuthController {
     @Anonymous
     @Operation(summary = "前台用户登录 (手机账号)")
     @PostMapping("/api/app/auth/loginWithPhone")
-    public Result login(@Validated(AppAuth.Login.class) @RequestBody AppAuth appAuth) {
+    public Result login(@Validated(AppAuth.LoginWithPhone.class) @RequestBody AppAuth appAuth) {
         return Result.success().put("data", appAuthService.loginWithPhone(appAuth));
     }
 
     @Anonymous
     @Operation(summary = "前台用户登录 (微信小程序用户)")
     @PostMapping("/api/app/auth/loginWithWechatMiniprogram")
-    public Result loginWithWechatMiniprogram(@Validated(AppAuthWechat.Login.class) @RequestBody AppAuthWechat appAuthWechat) {
-        return Result.success().put("data", appAuthService.loginWithWechatMiniprogram(appAuthWechat));
+    public Result loginWithWechatMiniprogram(@Validated(AppAuth.LoginWithWechat.class) @RequestBody AppAuth appAuth) {
+        return Result.success().put("data", appAuthService.loginWithWechatMiniprogram(appAuth));
     }
 
 }

+ 2 - 2
src/main/java/com/backendsys/modules/app/controller/AppUserController.java → src/main/java/com/backendsys/modules/app/user/controller/AppUserController.java

@@ -1,6 +1,6 @@
-package com.backendsys.modules.app.controller;
+package com.backendsys.modules.app.user.controller;
 
-import com.backendsys.modules.app.service.AppUserService;
+import com.backendsys.modules.app.user.service.AppUserService;
 import io.swagger.v3.oas.annotations.tags.Tag;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.validation.annotation.Validated;

+ 2 - 2
src/main/java/com/backendsys/modules/app/dao/AppUserDao.java → src/main/java/com/backendsys/modules/app/user/dao/AppUserDao.java

@@ -1,6 +1,6 @@
-package com.backendsys.modules.app.dao;
+package com.backendsys.modules.app.user.dao;
 
-import com.backendsys.modules.app.entity.AppUser;
+import com.backendsys.modules.app.user.entity.AppUser;
 import com.baomidou.mybatisplus.core.mapper.BaseMapper;
 import org.apache.ibatis.annotations.Mapper;
 

+ 22 - 0
src/main/java/com/backendsys/modules/app/user/entity/AppAuth.java

@@ -0,0 +1,22 @@
+package com.backendsys.modules.app.user.entity;
+
+import com.baomidou.mybatisplus.annotation.TableField;
+import jakarta.validation.constraints.NotEmpty;
+import lombok.Data;
+
+@Data
+public class AppAuth {
+
+    public static interface LoginWithPhone{}
+    public static interface LoginWithWechat{}
+
+    @NotEmpty(message = "手机号码不能为空", groups = { LoginWithPhone.class })
+    private String phone;
+    @NotEmpty(message = "密码不能为空", groups = { LoginWithPhone.class })
+    private String password;
+    private String captcha;
+
+    @NotEmpty(message = "code 不能为空", groups = { LoginWithWechat.class })
+    private String code;
+
+}

+ 1 - 2
src/main/java/com/backendsys/modules/app/entity/AppUser.java → src/main/java/com/backendsys/modules/app/user/entity/AppUser.java

@@ -1,10 +1,9 @@
-package com.backendsys.modules.app.entity;
+package com.backendsys.modules.app.user.entity;
 
 import com.baomidou.mybatisplus.annotation.IdType;
 import com.baomidou.mybatisplus.annotation.TableField;
 import com.baomidou.mybatisplus.annotation.TableId;
 import com.baomidou.mybatisplus.annotation.TableName;
-import jakarta.validation.constraints.NotEmpty;
 import lombok.Data;
 
 @Data

+ 5 - 6
src/main/java/com/backendsys/modules/app/service/AppAuthService.java → src/main/java/com/backendsys/modules/app/user/service/AppAuthService.java

@@ -1,8 +1,7 @@
-package com.backendsys.modules.app.service;
+package com.backendsys.modules.app.user.service;
 
-import com.backendsys.modules.app.entity.AppAuth;
-import com.backendsys.modules.app.entity.AppAuthWechat;
-import com.backendsys.modules.app.entity.AppUser;
+import com.backendsys.modules.app.user.entity.AppAuth;
+import com.backendsys.modules.app.user.entity.AppUser;
 import jakarta.servlet.http.HttpServletResponse;
 
 import java.io.IOException;
@@ -12,11 +11,11 @@ public interface AppAuthService {
     // 获取图形验证码
     void renderCaptcha(HttpServletResponse response) throws IOException;
 
+
     // 前台用户登录 (手机账号)
     AppUser loginWithPhone(AppAuth appAuth);
-
     // 前台用户登录 (微信小程序用户)
-    AppUser loginWithWechatMiniprogram(AppAuthWechat appAuthWechat);
+    AppUser loginWithWechatMiniprogram(AppAuth appAuth);
 
 
     // [事件] 登录成功

+ 4 - 0
src/main/java/com/backendsys/modules/app/user/service/AppUserService.java

@@ -0,0 +1,4 @@
+package com.backendsys.modules.app.user.service;
+
+public interface AppUserService {
+}

+ 67 - 30
src/main/java/com/backendsys/modules/app/service/impl/AppAuthServiceImpl.java → src/main/java/com/backendsys/modules/app/user/service/impl/AppAuthServiceImpl.java

@@ -1,18 +1,18 @@
-package com.backendsys.modules.app.service.impl;
+package com.backendsys.modules.app.user.service.impl;
 
 import cn.hutool.core.convert.Convert;
 import cn.hutool.core.date.DateUnit;
 import cn.hutool.core.date.DateUtil;
 import cn.hutool.core.util.RandomUtil;
+import cn.hutool.core.util.StrUtil;
 import cn.hutool.json.JSONObject;
 import cn.hutool.json.JSONUtil;
 import com.backendsys.exception.CustException;
-import com.backendsys.modules.app.dao.AppUserDao;
-import com.backendsys.modules.app.entity.AppAuth;
-import com.backendsys.modules.app.entity.AppAuthWechat;
-import com.backendsys.modules.app.entity.AppUser;
-import com.backendsys.modules.app.service.AppAuthService;
-import com.backendsys.modules.app.utils.AuthWechat.WechatUtil;
+import com.backendsys.modules.app.user.dao.AppUserDao;
+import com.backendsys.modules.app.user.entity.AppAuth;
+import com.backendsys.modules.app.user.entity.AppUser;
+import com.backendsys.modules.app.user.service.AppAuthService;
+import com.backendsys.modules.app.user.utils.AuthWechat.WechatUtil;
 import com.backendsys.modules.common.config.redis.utils.RedisUtil;
 import com.backendsys.modules.common.config.security.entity.SecurityAppUserInfo;
 import com.backendsys.modules.common.config.security.utils.*;
@@ -33,6 +33,8 @@ import java.awt.image.BufferedImage;
 import java.io.ByteArrayOutputStream;
 import java.io.IOException;
 import java.util.Date;
+import java.util.Map;
+import java.util.TimeZone;
 import java.util.UUID;
 import java.util.concurrent.TimeUnit;
 
@@ -59,19 +61,12 @@ public class AppAuthServiceImpl implements AppAuthService {
     @Autowired
     private SysCommonService sysCommonService;
 
-    @Value("${tencent.sms.debug}")
-    private String SMS_DEBUG;
+    @Value("${spring.application.name}")
+    private String APPLICATION_NAME;
     @Value("${CAPTCHA_DURATION}")
     private Integer CAPTCHA_DURATION;
     @Value("${REDIS_LOGIN_TOKEN_PREFIX}")
     private String REDIS_LOGIN_TOKEN_PREFIX;
-    @Value("${spring.application.name}")
-    private String APPLICATION_NAME;
-    private String redisKeyOfLogin = APPLICATION_NAME + "-app-sms-login";
-    private String redisKeyOfRegister = APPLICATION_NAME + "-app-sms-register";
-    private String redisKeyOfLoginFail = APPLICATION_NAME + "-app-login-error";
-    private String redisKeyOfRegisterFail = APPLICATION_NAME + "-app-register-error";
-
 
     @Autowired
     private Producer captchaProducer;
@@ -107,17 +102,25 @@ public class AppAuthServiceImpl implements AppAuthService {
         } catch (IOException e) {
             response.sendError(HttpServletResponse.SC_NOT_FOUND);
         }
-
     }
 
 
     // [方法] 登录失败 (errMsg: 错误提示文本, phone: 手机号码, intercept: 是否拦截)
     public void loginFail(String errMsg, String phone, Boolean isIntercept) {
+        // 验证码是否必填
+        Boolean currentCaptchaRequired = captchaUtil.isCaptchaRequired(APPLICATION_NAME + "-app-login-required-captcha-" + phone, 3);
+        System.out.println("(loginFailByUsername) currentCaptchaRequired = " + currentCaptchaRequired);
+
         // 删除图形验证码
-        redisUtil.delete(httpRequestUtil.getKaptchaKey());
+        redisUtil.delete(httpRequestUtil.getKaptchaKey("APP"));
         // 添加登录错误的冻结标记
-        if (isIntercept) lockStatusUtil.setLockStatus(redisKeyOfLoginFail, phone);
-        throw new CustException(errMsg, ResultEnum.INVALID_CREDENTIALS.getCode());
+        if (isIntercept) lockStatusUtil.setLockStatus(APPLICATION_NAME + "-app-login-error", phone);
+
+        if (currentCaptchaRequired) {
+            throw new CustException(errMsg, ResultEnum.INVALID_CREDENTIALS.getCode(), Map.of("is_captcha_required", true));
+        } else {
+            throw new CustException(errMsg, ResultEnum.INVALID_CREDENTIALS.getCode());
+        }
     }
 
     // [方法] 登录成功
@@ -142,7 +145,17 @@ public class AppAuthServiceImpl implements AppAuthService {
         appUser.setUser_id(appUser.getId());
         appUser.setLast_login_uuid(uuid);
         appUser.setLast_login_ip(httpRequestUtil.getIpAddr());
-        appUser.setLast_login_time(DateUtil.format(new Date(), "yyyy-MM-dd HH:mm:ss"));
+
+        // 最后登录时间 (本地时间)
+        String localTimeStr = DateUtil.format(new Date(), "yyyy-MM-dd HH:mm:ss");
+        appUser.setLast_login_time(localTimeStr);
+
+//        // UTC 时间
+//        String utcTimeStr = DateUtil.date().setTimeZone(TimeZone.getTimeZone("UTC"))
+//                .toString("yyyy-MM-dd HH:mm:ss");
+//        appUser.setLast_login_time(utcTimeStr);
+
+        // [db] 更新前台用户信息
         appUserDao.updateById(appUser);
 
 
@@ -152,6 +165,7 @@ public class AppAuthServiceImpl implements AppAuthService {
         Long DEFAULT_MILLISECONDS = APP_USER_LOGIN_DURATION_DEFAULT * DateUnit.HOUR.getMillis();
         Integer token_duration_hours = Convert.toInt(DEFAULT_MILLISECONDS / 3600000L);
 
+        // Token 记录的登录时间,是本地时间
         Date token_expiration = new Date((new Date()).getTime() + DEFAULT_MILLISECONDS);
         appUser.setToken_expiration(DateUtil.format(token_expiration, "yyyy-MM-dd HH:mm:ss"));
         appUser.setRole("APP_USER");
@@ -169,6 +183,18 @@ public class AppAuthServiceImpl implements AppAuthService {
         return appUser;
     }
 
+    private void setLoginRequired(String key) {
+        Object captchaValue = redisUtil.getCacheObject(APPLICATION_NAME + "-app-login-required-captcha-" + key);
+        Integer currentErrCount = (captchaValue == null) ? 1 : (Convert.toInt(captchaValue) + 1);
+        redisUtil.setCacheObject(APPLICATION_NAME + "-app-login-required-captcha-" + key, currentErrCount, 1, TimeUnit.MINUTES);
+        System.out.println("currentErrCount: " + currentErrCount);
+    }
+    private void cleanLoginRequired(String key) {
+        redisUtil.delete(APPLICATION_NAME + "-app-login-required-captcha-" + key);
+    }
+
+
+
     /**
      * 前台用户登录 (手机账号)
      */
@@ -180,27 +206,38 @@ public class AppAuthServiceImpl implements AppAuthService {
         String captcha = appAuth.getCaptcha();
 
         // 判断是否处于登录错误的冻结状态 (2分钟内错误5次,则出现冻结提示)
-        lockStatusUtil.checkLockStatus(redisKeyOfLoginFail, phone);
-
-        // 判断图形验证码是否正确
-        if (!captchaUtil.isCaptchaValid(captcha, httpRequestUtil.getKaptchaKey("APP"))) {
-            loginFail("验证码错误", phone, false);
-            return null;
+        lockStatusUtil.checkLockStatus(APPLICATION_NAME + "-app-login-error", phone);
+
+        // -- 判断是否需要输入验证码 ----------------------------------------------------
+        // - 当输错 3 次密码时,需要输入验证码
+        // - 当输错后 1 分钟后重置
+        Boolean isCaptchaRequired = captchaUtil.isCaptchaRequired(APPLICATION_NAME + "-app-login-required-captcha-" + phone, 3);
+        if (isCaptchaRequired) {
+            Boolean isCaptchaEmpty = StrUtil.isEmpty(captcha);
+            Boolean isCpatchaValid = (captchaUtil.isCaptchaValid(captcha, httpRequestUtil.getKaptchaKey("APP")));
+            if (isCaptchaEmpty) { loginFail("验证码不能为空", phone, false); return null; }
+            if (!isCpatchaValid) { loginFail("验证码错误", phone, false); return null; }
         }
+        // --------------------------------------------------------------------------
 
         // [Method] 判断 用户 是否存在 && 密码是否正确
         AppUser appUser = appUserDao.selectOne(new LambdaQueryWrapper<AppUser>().eq(AppUser::getPhone, phone));
         if (appUser == null) {
+            // 输入错误时,计数器叠加,并且设置重置时间 (会一直叠加,直到重置 或 登录成功)
+            setLoginRequired(phone);
             // [登录失败] 用户不存在
             loginFail("手机号码或密码错误", phone, true);
             return null;
         } else {
-            // [登录失败] 密码不正确
             BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
             if (!encoder.matches(password, appUser.getPassword())) {
+                // 输入错误时,计数器叠加,并且设置重置时间 (会一直叠加,直到重置 或 登录成功)
+                setLoginRequired(phone);
+                // [登录失败] 密码不正确
                 loginFail("手机号码或密码错误", phone, true);
             }
             // [登录成功]
+            cleanLoginRequired(phone);
             return loginSuccess(appUser);
         }
 
@@ -210,9 +247,9 @@ public class AppAuthServiceImpl implements AppAuthService {
      * 前台用户登录 (微信小程序用户)
      */
     @Override
-    public AppUser loginWithWechatMiniprogram(AppAuthWechat appAuthWechat) {
+    public AppUser loginWithWechatMiniprogram(AppAuth appAuth) {
 
-        String code = appAuthWechat.getCode();
+        String code = appAuth.getCode();
         JSONObject response = wechatUtil.getCode2Session(code);
         // [失败] 返回值:{"errcode":40164,"errmsg":"invalid ip 116.31.165.86 ipv6 ::ffff:116.31.165.86, not in whitelist, rid: 67d15951-5b3f2778-79d6f047"}
         // [成功] 返回值:{"session_key":"uQAcry2PC1Lx/Krp+6rr0g==","openid":"oSB9r7T7kN1bC7PabJ7RmTUiaJmo"}

+ 2 - 2
src/main/java/com/backendsys/modules/app/service/impl/AppUserServiceImpl.java → src/main/java/com/backendsys/modules/app/user/service/impl/AppUserServiceImpl.java

@@ -1,6 +1,6 @@
-package com.backendsys.modules.app.service.impl;
+package com.backendsys.modules.app.user.service.impl;
 
-import com.backendsys.modules.app.service.AppUserService;
+import com.backendsys.modules.app.user.service.AppUserService;
 import org.springframework.stereotype.Service;
 
 @Service

+ 1 - 1
src/main/java/com/backendsys/modules/app/utils/AuthWechat/AES_Dec.java → src/main/java/com/backendsys/modules/app/user/utils/AuthWechat/AES_Dec.java

@@ -1,4 +1,4 @@
-package com.backendsys.modules.app.utils.AuthWechat;
+package com.backendsys.modules.app.user.utils.AuthWechat;
 
 import javax.crypto.Cipher;
 import javax.crypto.spec.GCMParameterSpec;

+ 1 - 1
src/main/java/com/backendsys/modules/app/utils/AuthWechat/AES_Enc.java → src/main/java/com/backendsys/modules/app/user/utils/AuthWechat/AES_Enc.java

@@ -1,4 +1,4 @@
-package com.backendsys.modules.app.utils.AuthWechat;
+package com.backendsys.modules.app.user.utils.AuthWechat;
 
 import javax.crypto.Cipher;
 import javax.crypto.spec.GCMParameterSpec;

+ 1 - 1
src/main/java/com/backendsys/modules/app/utils/AuthWechat/RSA_Sign.java → src/main/java/com/backendsys/modules/app/user/utils/AuthWechat/RSA_Sign.java

@@ -1,4 +1,4 @@
-package com.backendsys.modules.app.utils.AuthWechat;
+package com.backendsys.modules.app.user.utils.AuthWechat;
 
 import com.google.gson.JsonObject;
 import java.nio.charset.StandardCharsets;

+ 1 - 1
src/main/java/com/backendsys/modules/app/utils/AuthWechat/RSA_Verify.java → src/main/java/com/backendsys/modules/app/user/utils/AuthWechat/RSA_Verify.java

@@ -1,4 +1,4 @@
-package com.backendsys.modules.app.utils.AuthWechat;
+package com.backendsys.modules.app.user.utils.AuthWechat;
 
 import com.google.gson.JsonObject;
 import java.io.ByteArrayInputStream;

+ 1 - 1
src/main/java/com/backendsys/modules/app/utils/AuthWechat/WechatUtil.java → src/main/java/com/backendsys/modules/app/user/utils/AuthWechat/WechatUtil.java

@@ -1,4 +1,4 @@
-package com.backendsys.modules.app.utils.AuthWechat;
+package com.backendsys.modules.app.user.utils.AuthWechat;
 
 import cn.hutool.http.HttpUtil;
 import cn.hutool.json.JSONObject;

+ 5 - 3
src/main/java/com/backendsys/modules/system/service/impl/SysAuthServiceImpl.java

@@ -237,15 +237,17 @@ public class SysAuthServiceImpl implements SysAuthService {
         // [Method] 判断 用户 是否存在 && 密码是否正确
         SysUser sysUser = sysUserDao.selectOne(new LambdaQueryWrapper<SysUser>().eq(SysUser::getUsername, username));
         if (sysUser == null) {
+            // 输入错误时,计数器叠加,并且设置重置时间 (会一直叠加,直到重置 或 登录成功)
+            setLoginRequired(username);
             // [登录失败] 用户不存在
-            setLoginRequired(username); // 输入错误时,计数器叠加,并且设置重置时间 (会一直叠加,直到重置 或 登录成功)
             loginFail("用户名或密码错误", username, true);
             return null;
         } else {
-            // [登录失败] 密码不正确
             BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
             if (!encoder.matches(password, sysUser.getPassword())) {
-                setLoginRequired(username); // 输入错误时,计数器叠加,并且设置重置时间 (会一直叠加,直到重置 或 登录成功)
+                // 输入错误时,计数器叠加,并且设置重置时间 (会一直叠加,直到重置 或 登录成功)
+                setLoginRequired(username);
+                // [登录失败] 密码不正确
                 loginFail("用户名或密码错误", username, true);
             }
             // [登录成功]