Ver código fonte

1、增加用户输错验证码or密码限制

rayson 8 meses atrás
pai
commit
26e4762ac6

+ 27 - 5
menduner/menduner-system-biz/src/main/java/com/citu/module/menduner/system/controller/app/common/file/AppFileController.java

@@ -3,6 +3,8 @@ package com.citu.module.menduner.system.controller.app.common.file;
 import cn.hutool.core.io.IoUtil;
 import com.citu.framework.common.pojo.CommonResult;
 import com.citu.framework.common.util.io.FileUtils;
+import com.citu.framework.idempotent.core.annotation.Idempotent;
+import com.citu.framework.idempotent.core.keyresolver.impl.UserIdempotentKeyResolver;
 import com.citu.framework.security.core.annotations.PreAuthenticated;
 import com.citu.module.infra.api.file.FileApi;
 import com.citu.module.menduner.common.util.LoginUserContext;
@@ -19,6 +21,8 @@ import org.springframework.web.bind.annotation.RestController;
 import org.springframework.web.multipart.MultipartFile;
 
 import javax.annotation.Resource;
+import java.util.Arrays;
+import java.util.List;
 
 import static com.citu.framework.common.exception.util.ServiceExceptionUtil.exception;
 import static com.citu.framework.common.pojo.CommonResult.success;
@@ -38,6 +42,7 @@ public class AppFileController {
     @PreAuthenticated
     @PostMapping("/upload")
     @Operation(summary = "上传文件")
+    @Idempotent(keyResolver = UserIdempotentKeyResolver.class)
     public CommonResult<String> uploadFile(AppFileUploadReqVO reqVO) throws Exception {
         if (null == reqVO.getPath() || !checkFileType(reqVO)) {
             throw exception(MDE_FILE_FORMAT_ERROR);
@@ -60,9 +65,6 @@ public class AppFileController {
     private boolean isSupportedDocument(MultipartFile file) throws Exception {
         // 检查文件扩展名
         String originalFilename = file.getOriginalFilename();
-        if (originalFilename == null || originalFilename.isEmpty()) {
-            return false;
-        }
 
         String extension = originalFilename.toLowerCase();
         if (!extension.endsWith(".doc")
@@ -89,15 +91,35 @@ public class AppFileController {
 
     }
 
+
+    private boolean isSupportedImage(MultipartFile file) throws Exception {
+        // 支持的图片类型列表
+        List<String> supportedImages = Arrays.asList("image/jpeg", "image/png", "image/gif","image/pjp","image/jpg","image/jfif","image/webp");
+        String mimeType = new Tika().detect(file.getInputStream(), file.getOriginalFilename());
+        return supportedImages.contains(mimeType);
+    }
+
+    private boolean isSupportedVideo(MultipartFile file) throws Exception {
+        // 支持的视频类型列表
+        List<String> supportedVideos = Arrays.asList("video/mp4", "video/x-flv", "video/x-msvideo", "video/quicktime", "video/x-matroska");
+        String mimeType = new Tika().detect(file.getInputStream(), file.getOriginalFilename());
+        return supportedVideos.contains(mimeType);
+    }
+
     private boolean checkFileType(AppFileUploadReqVO reqVO) throws Exception {
+        String originalFilename = reqVO.getFile().getOriginalFilename();
+        if (originalFilename == null || originalFilename.isEmpty()) {
+            return false;
+        }
+        // 加个公共的判断,防止什么js文件、html这种等攻击相关的
         switch (reqVO.getPath()) {
-
             case AppFileUploadReqVO.ATTACHMENT_TYPE:
                 return isSupportedDocument(reqVO.getFile());
             case AppFileUploadReqVO.IMG_TYPE:
+                return isSupportedImage(reqVO.getFile());
             case AppFileUploadReqVO.VIDEO_TYPE:
+                return isSupportedVideo(reqVO.getFile());
             case AppFileUploadReqVO.OTHER_TYPE:
-                return true;
             default:
                 return false;
         }

+ 16 - 0
menduner/menduner-system-biz/src/main/java/com/citu/module/menduner/system/dal/redis/RedisKeyConstants.java

@@ -137,4 +137,20 @@ public interface RedisKeyConstants {
      * VALUE 数据类型:Object
      */
     String JOB_SHARE = "job_share";
+
+    /**
+     * 用户验证码错误多次锁定
+     * <p>
+     * KEY 格式:mde_auth_user_sms_code_lock:{phone}
+     * VALUE 数据类型:String
+     **/
+    String MDE_AUTH_USER_SMS_CODE_LOCK = "mde_auth_user_sms_code_lock:%s";
+
+    /**
+     * 用户密码错误多次锁定
+     * <p>
+     * KEY 格式:mde_auth_user_pwd_lock:{phone}
+     * VALUE 数据类型:String
+     **/
+    String MDE_AUTH_USER_PWD_LOCK = "mde_auth_user_pwd_lock:%s";
 }

+ 59 - 9
menduner/menduner-system-biz/src/main/java/com/citu/module/menduner/system/service/auth/MdeAuthServiceImpl.java

@@ -5,6 +5,7 @@ import cn.hutool.core.util.ObjectUtil;
 import com.baomidou.dynamic.datasource.annotation.DSTransactional;
 import com.citu.framework.common.enums.TerminalEnum;
 import com.citu.framework.common.enums.UserTypeEnum;
+import com.citu.framework.common.pojo.CommonResult;
 import com.citu.framework.common.util.monitor.TracerUtils;
 import com.citu.framework.common.util.servlet.ServletUtils;
 import com.citu.framework.common.util.validation.ValidationUtils;
@@ -40,15 +41,20 @@ import com.xingyuv.captcha.model.vo.CaptchaVO;
 import com.xingyuv.captcha.service.CaptchaService;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.beans.factory.annotation.Value;
+import org.springframework.data.redis.core.StringRedisTemplate;
 import org.springframework.stereotype.Service;
 
 import javax.annotation.Resource;
 import javax.validation.Validator;
+import java.util.Collections;
 import java.util.Objects;
+import java.util.concurrent.TimeUnit;
 
 import static com.citu.framework.common.exception.util.ServiceExceptionUtil.exception;
 import static com.citu.framework.common.util.servlet.ServletUtils.getClientIP;
 import static com.citu.framework.web.core.util.WebFrameworkUtils.getTerminal;
+import static com.citu.module.menduner.system.dal.redis.RedisKeyConstants.MDE_AUTH_USER_PWD_LOCK;
+import static com.citu.module.menduner.system.dal.redis.RedisKeyConstants.MDE_AUTH_USER_SMS_CODE_LOCK;
 import static com.citu.module.menduner.system.enums.ErrorCodeConstants.*;
 
 /**
@@ -76,7 +82,6 @@ public class MdeAuthServiceImpl implements MdeAuthService {
     protected MdeEnterpriseAuthService enterpriseAuthService;
     @Resource
     protected PointOperateProducer pointOperateProducer;
-
     /**
      * 验证码的开关,默认为 true
      */
@@ -84,9 +89,10 @@ public class MdeAuthServiceImpl implements MdeAuthService {
     protected Boolean captchaEnable;
     @Resource
     protected Validator validator;
-
     @Resource
     protected CaptchaService captchaService;
+    @Resource
+    private StringRedisTemplate redisTemplate;
 
     @VisibleForTesting
     protected void validateCaptcha(AppMdeAuthLoginReqVO reqVO) {
@@ -146,8 +152,24 @@ public class MdeAuthServiceImpl implements MdeAuthService {
                 // 第一次登录
                 throw exception(MDE_USER_PHONE_INIT_PASSWORD);
             } else {
-                createLoginLog(user.getId(), account, logTypeEnum, LoginResultEnum.BAD_CREDENTIALS);
-                throw exception(MDE_AUTH_LOGIN_BAD_CREDENTIALS);
+                // 获取错误次数
+                String num =
+                        redisTemplate.opsForValue()
+                                .get(String.format(MDE_AUTH_USER_PWD_LOCK, user.getPhone()));
+                Integer numInt = Integer.parseInt(null == num ? "0" : num) + 1;
+
+                redisTemplate.opsForValue()
+                        .setIfAbsent(MDE_AUTH_USER_PWD_LOCK, String.valueOf(numInt), 8, TimeUnit.HOURS);
+                if (numInt >= 5) {
+                    // 8个小时内输错5次 锁定
+                    userService.disable(Collections.singletonList(user.getId()));
+                    createLoginLog(user.getId(),account,logTypeEnum, LoginResultEnum.USER_DISABLED);
+                    throw exception(MDE_AUTH_LOGIN_USER_DISABLED);
+                } else {
+                    // 提示账户密码错误
+                    createLoginLog(user.getId(), account, logTypeEnum, LoginResultEnum.BAD_CREDENTIALS);
+                    throw exception(MDE_AUTH_LOGIN_BAD_CREDENTIALS);
+                }
             }
         }
 
@@ -189,16 +211,15 @@ public class MdeAuthServiceImpl implements MdeAuthService {
     public AppMdeAuthLoginRespVO smsLogin(AppMdeAuthSmsLoginReqVO reqVO) {
         // 校验验证码
         String userIp = getClientIP();
-        smsCodeApi.useSmsCode(MdeAuthConvert.INSTANCE.convert(reqVO, SmsSceneEnum.MENDUNER_LOGIN.getScene(), userIp)
-                .setMobile(reqVO.getPhone())).getCheckedData();
-
+        CommonResult<Boolean> result =
+                smsCodeApi.useSmsCode(MdeAuthConvert.INSTANCE.convert(reqVO, SmsSceneEnum.MENDUNER_LOGIN.getScene(), userIp)
+                        .setMobile(reqVO.getPhone()));
 
         // 获得获得注册用户
         MdeUserDO user = null;
         if (!reqVO.isAutoRegister()) {
             user = userService.getUserByPhone(reqVO.getPhone());
             if (null == user) {
-                createLoginLog(null, reqVO.getPhone(), LoginLogTypeEnum.LOGIN_MOBILE, LoginResultEnum.BAD_CREDENTIALS);
                 throw exception(MDE_USER_MOBILE_NOT_EXISTS);
             }
         } else {
@@ -206,13 +227,42 @@ public class MdeAuthServiceImpl implements MdeAuthService {
             user = userService.createUserIfAbsent(reqVO.getPhone(), userIp, getTerminal().toString());
             Assert.notNull(user, "获取用户失败,结果为空");
         }
+
+        if (result.isError()) {
+            // 获取错误次数
+            String num =
+                    redisTemplate.opsForValue()
+                            .get(String.format(MDE_AUTH_USER_SMS_CODE_LOCK, reqVO.getPhone()));
+            Integer numInt = Integer.parseInt(null == num ? "0" : num) + 1;
+
+            redisTemplate.opsForValue()
+                    .setIfAbsent(MDE_AUTH_USER_SMS_CODE_LOCK, String.valueOf(numInt), 8, TimeUnit.HOURS);
+
+            if (numInt >= 5) {
+                // 8个小时内输错5次 锁定
+                userService.disable(Collections.singletonList(user.getId()));
+                createLoginLog(user.getId(), user.getPhone(), LoginLogTypeEnum.LOGIN_SMS, LoginResultEnum.USER_DISABLED);
+                throw exception(MDE_AUTH_LOGIN_USER_DISABLED);
+            } else {
+                // 提示验证码错误
+                result.getCheckedData();
+            }
+        }
+
+        // 是否禁用
+        if (ObjectUtil.notEqual(user.getStatus(), MendunerStatusEnum.ENABLE.getStatus())) {
+            createLoginLog(user.getId(), user.getPhone(), LoginLogTypeEnum.LOGIN_SMS, LoginResultEnum.USER_DISABLED);
+            throw exception(MDE_AUTH_LOGIN_USER_DISABLED);
+        }
+
         // 如果 socialType 非空,说明需要绑定社交用户
         String openid = null;
         if (reqVO.getSocialType() != null) {
             openid = socialUserApi.bindSocialUser(new SocialUserBindReqDTO(user.getId(), getUserType().getValue(),
                     reqVO.getSocialType(), reqVO.getSocialCode(), reqVO.getSocialState())).getCheckedData();
         }
-
+        // 登录成功就清理
+        redisTemplate.delete(MDE_AUTH_USER_SMS_CODE_LOCK);
         // 创建 Token 令牌,记录登录日志
         return createTokenAfterLoginSuccess(user, reqVO.getPhone(), LoginLogTypeEnum.LOGIN_SMS, openid);
     }

+ 11 - 0
menduner/menduner-system-biz/src/main/java/com/citu/module/menduner/system/service/user/MdeUserService.java

@@ -11,6 +11,7 @@ import com.citu.module.menduner.system.dal.dataobject.user.MdeUserDO;
 
 import javax.validation.Valid;
 import java.time.LocalDateTime;
+import java.util.List;
 
 /**
  * 用户登录 Service 接口
@@ -178,4 +179,14 @@ public interface MdeUserService {
      * 修改vip
      **/
     void updateVip(Long id, String vipFlag, LocalDateTime vipExpireDate);
+
+    /**
+     * 启用用户
+     **/
+    void enable(List<Long> ids);
+
+    /**
+     * 禁用用户
+     **/
+    void disable(List<Long> ids);
 }

+ 17 - 0
menduner/menduner-system-biz/src/main/java/com/citu/module/menduner/system/service/user/MdeUserServiceImpl.java

@@ -34,6 +34,7 @@ import org.springframework.validation.annotation.Validated;
 
 import javax.annotation.Resource;
 import java.time.LocalDateTime;
+import java.util.List;
 
 import static com.citu.framework.common.exception.util.ServiceExceptionUtil.exception;
 import static com.citu.framework.common.util.servlet.ServletUtils.getClientIP;
@@ -404,4 +405,20 @@ public class MdeUserServiceImpl implements MdeUserService {
         LogRecordContext.putVariable("mde-newPassword", updateObj.getPassword());
     }
 
+
+    @Override
+    public void enable(List<Long> ids) {
+        for (Long id : ids) {
+            mdeUserMapper
+                    .updateById(MdeUserDO.builder().id(id).status(MendunerStatusEnum.ENABLE.getStatus()).build());
+        }
+    }
+
+    @Override
+    public void disable(List<Long> ids) {
+        for (Long id : ids) {
+            mdeUserMapper
+                    .updateById(MdeUserDO.builder().id(id).status(MendunerStatusEnum.DISABLE.getStatus()).build());
+        }
+    }
 }