ソースを参照

新增密码错误次数限制

RuoYi 3 年 前
コミット
0ef63207f4

+ 16 - 47
ruoyi-auth/src/main/java/com/ruoyi/auth/service/SysLoginService.java

@@ -8,13 +8,9 @@ import com.ruoyi.common.core.constant.UserConstants;
8 8
 import com.ruoyi.common.core.domain.R;
9 9
 import com.ruoyi.common.core.enums.UserStatus;
10 10
 import com.ruoyi.common.core.exception.ServiceException;
11
-import com.ruoyi.common.core.utils.ServletUtils;
12 11
 import com.ruoyi.common.core.utils.StringUtils;
13
-import com.ruoyi.common.core.utils.ip.IpUtils;
14 12
 import com.ruoyi.common.security.utils.SecurityUtils;
15
-import com.ruoyi.system.api.RemoteLogService;
16 13
 import com.ruoyi.system.api.RemoteUserService;
17
-import com.ruoyi.system.api.domain.SysLogininfor;
18 14
 import com.ruoyi.system.api.domain.SysUser;
19 15
 import com.ruoyi.system.api.model.LoginUser;
20 16
 
@@ -27,10 +23,13 @@ import com.ruoyi.system.api.model.LoginUser;
27 23
 public class SysLoginService
28 24
 {
29 25
     @Autowired
30
-    private RemoteLogService remoteLogService;
26
+    private RemoteUserService remoteUserService;
31 27
 
32 28
     @Autowired
33
-    private RemoteUserService remoteUserService;
29
+    private SysPasswordService passwordService;
30
+
31
+    @Autowired
32
+    private SysRecordLogService recordLogService;
34 33
 
35 34
     /**
36 35
      * 登录
@@ -40,21 +39,21 @@ public class SysLoginService
40 39
         // 用户名或密码为空 错误
41 40
         if (StringUtils.isAnyBlank(username, password))
42 41
         {
43
-            recordLogininfor(username, Constants.LOGIN_FAIL, "用户/密码必须填写");
42
+            recordLogService.recordLogininfor(username, Constants.LOGIN_FAIL, "用户/密码必须填写");
44 43
             throw new ServiceException("用户/密码必须填写");
45 44
         }
46 45
         // 密码如果不在指定范围内 错误
47 46
         if (password.length() < UserConstants.PASSWORD_MIN_LENGTH
48 47
                 || password.length() > UserConstants.PASSWORD_MAX_LENGTH)
49 48
         {
50
-            recordLogininfor(username, Constants.LOGIN_FAIL, "用户密码不在指定范围");
49
+            recordLogService.recordLogininfor(username, Constants.LOGIN_FAIL, "用户密码不在指定范围");
51 50
             throw new ServiceException("用户密码不在指定范围");
52 51
         }
53 52
         // 用户名不在指定范围内 错误
54 53
         if (username.length() < UserConstants.USERNAME_MIN_LENGTH
55 54
                 || username.length() > UserConstants.USERNAME_MAX_LENGTH)
56 55
         {
57
-            recordLogininfor(username, Constants.LOGIN_FAIL, "用户名不在指定范围");
56
+            recordLogService.recordLogininfor(username, Constants.LOGIN_FAIL, "用户名不在指定范围");
58 57
             throw new ServiceException("用户名不在指定范围");
59 58
         }
60 59
         // 查询用户信息
@@ -67,33 +66,29 @@ public class SysLoginService
67 66
 
68 67
         if (StringUtils.isNull(userResult) || StringUtils.isNull(userResult.getData()))
69 68
         {
70
-            recordLogininfor(username, Constants.LOGIN_FAIL, "登录用户不存在");
69
+            recordLogService.recordLogininfor(username, Constants.LOGIN_FAIL, "登录用户不存在");
71 70
             throw new ServiceException("登录用户:" + username + " 不存在");
72 71
         }
73 72
         LoginUser userInfo = userResult.getData();
74 73
         SysUser user = userResult.getData().getSysUser();
75 74
         if (UserStatus.DELETED.getCode().equals(user.getDelFlag()))
76 75
         {
77
-            recordLogininfor(username, Constants.LOGIN_FAIL, "对不起,您的账号已被删除");
76
+            recordLogService.recordLogininfor(username, Constants.LOGIN_FAIL, "对不起,您的账号已被删除");
78 77
             throw new ServiceException("对不起,您的账号:" + username + " 已被删除");
79 78
         }
80 79
         if (UserStatus.DISABLE.getCode().equals(user.getStatus()))
81 80
         {
82
-            recordLogininfor(username, Constants.LOGIN_FAIL, "用户已停用,请联系管理员");
81
+            recordLogService.recordLogininfor(username, Constants.LOGIN_FAIL, "用户已停用,请联系管理员");
83 82
             throw new ServiceException("对不起,您的账号:" + username + " 已停用");
84 83
         }
85
-        if (!SecurityUtils.matchesPassword(password, user.getPassword()))
86
-        {
87
-            recordLogininfor(username, Constants.LOGIN_FAIL, "用户密码错误");
88
-            throw new ServiceException("用户不存在/密码错误");
89
-        }
90
-        recordLogininfor(username, Constants.LOGIN_SUCCESS, "登录成功");
84
+        passwordService.validate(user, password);
85
+        recordLogService.recordLogininfor(username, Constants.LOGIN_SUCCESS, "登录成功");
91 86
         return userInfo;
92 87
     }
93 88
 
94 89
     public void logout(String loginName)
95 90
     {
96
-        recordLogininfor(loginName, Constants.LOGOUT, "退出成功");
91
+        recordLogService.recordLogininfor(loginName, Constants.LOGOUT, "退出成功");
97 92
     }
98 93
 
99 94
     /**
@@ -128,32 +123,6 @@ public class SysLoginService
128 123
         {
129 124
             throw new ServiceException(registerResult.getMsg());
130 125
         }
131
-        recordLogininfor(username, Constants.REGISTER, "注册成功");
132
-    }
133
-
134
-    /**
135
-     * 记录登录信息
136
-     * 
137
-     * @param username 用户名
138
-     * @param status 状态
139
-     * @param message 消息内容
140
-     * @return
141
-     */
142
-    public void recordLogininfor(String username, String status, String message)
143
-    {
144
-        SysLogininfor logininfor = new SysLogininfor();
145
-        logininfor.setUserName(username);
146
-        logininfor.setIpaddr(IpUtils.getIpAddr(ServletUtils.getRequest()));
147
-        logininfor.setMsg(message);
148
-        // 日志状态
149
-        if (StringUtils.equalsAny(status, Constants.LOGIN_SUCCESS, Constants.LOGOUT, Constants.REGISTER))
150
-        {
151
-            logininfor.setStatus(Constants.LOGIN_SUCCESS_STATUS);
152
-        }
153
-        else if (Constants.LOGIN_FAIL.equals(status))
154
-        {
155
-            logininfor.setStatus(Constants.LOGIN_FAIL_STATUS);
156
-        }
157
-        remoteLogService.saveLogininfor(logininfor, SecurityConstants.INNER);
126
+        recordLogService.recordLogininfor(username, Constants.REGISTER, "注册成功");
158 127
     }
159
-}
128
+}

+ 85 - 0
ruoyi-auth/src/main/java/com/ruoyi/auth/service/SysPasswordService.java

@@ -0,0 +1,85 @@
1
+package com.ruoyi.auth.service;
2
+
3
+import java.util.concurrent.TimeUnit;
4
+import org.springframework.beans.factory.annotation.Autowired;
5
+import org.springframework.stereotype.Component;
6
+import com.ruoyi.common.core.constant.CacheConstants;
7
+import com.ruoyi.common.core.constant.Constants;
8
+import com.ruoyi.common.core.exception.ServiceException;
9
+import com.ruoyi.common.redis.service.RedisService;
10
+import com.ruoyi.common.security.utils.SecurityUtils;
11
+import com.ruoyi.system.api.domain.SysUser;
12
+
13
+/**
14
+ * 登录密码方法
15
+ * 
16
+ * @author ruoyi
17
+ */
18
+@Component
19
+public class SysPasswordService
20
+{
21
+    @Autowired
22
+    private RedisService redisService;
23
+
24
+    private int maxRetryCount = CacheConstants.passwordMaxRetryCount;
25
+
26
+    private Long lockTime = CacheConstants.passwordLockTime;
27
+
28
+    @Autowired
29
+    private SysRecordLogService recordLogService;
30
+
31
+    /**
32
+     * 登录账户密码错误次数缓存键名
33
+     * 
34
+     * @param username 用户名
35
+     * @return 缓存键key
36
+     */
37
+    private String getCacheKey(String username)
38
+    {
39
+        return CacheConstants.PWD_ERR_CNT_KEY + username;
40
+    }
41
+
42
+    public void validate(SysUser user, String password)
43
+    {
44
+        String username = user.getUserName();
45
+
46
+        Integer retryCount = redisService.getCacheObject(getCacheKey(username));
47
+
48
+        if (retryCount == null)
49
+        {
50
+            retryCount = 0;
51
+        }
52
+
53
+        if (retryCount >= Integer.valueOf(maxRetryCount).intValue())
54
+        {
55
+            String errMsg = String.format("密码输入错误%s次,帐户锁定%s分钟", maxRetryCount, lockTime);
56
+            recordLogService.recordLogininfor(username, Constants.LOGIN_FAIL,errMsg);
57
+            throw new ServiceException(errMsg);
58
+        }
59
+
60
+        if (!matches(user, password))
61
+        {
62
+            retryCount = retryCount + 1;
63
+            recordLogService.recordLogininfor(username, Constants.LOGIN_FAIL, String.format("密码输入错误%s次", maxRetryCount));
64
+            redisService.setCacheObject(getCacheKey(username), retryCount, lockTime, TimeUnit.MINUTES);
65
+            throw new ServiceException("用户不存在/密码错误");
66
+        }
67
+        else
68
+        {
69
+            clearLoginRecordCache(username);
70
+        }
71
+    }
72
+
73
+    public boolean matches(SysUser user, String rawPassword)
74
+    {
75
+        return SecurityUtils.matchesPassword(rawPassword, user.getPassword());
76
+    }
77
+
78
+    public void clearLoginRecordCache(String loginName)
79
+    {
80
+        if (redisService.hasKey(getCacheKey(loginName)))
81
+        {
82
+            redisService.deleteObject(getCacheKey(loginName));
83
+        }
84
+    }
85
+}

+ 49 - 0
ruoyi-auth/src/main/java/com/ruoyi/auth/service/SysRecordLogService.java

@@ -0,0 +1,49 @@
1
+package com.ruoyi.auth.service;
2
+
3
+import org.springframework.beans.factory.annotation.Autowired;
4
+import org.springframework.stereotype.Component;
5
+import com.ruoyi.common.core.constant.Constants;
6
+import com.ruoyi.common.core.constant.SecurityConstants;
7
+import com.ruoyi.common.core.utils.ServletUtils;
8
+import com.ruoyi.common.core.utils.StringUtils;
9
+import com.ruoyi.common.core.utils.ip.IpUtils;
10
+import com.ruoyi.system.api.RemoteLogService;
11
+import com.ruoyi.system.api.domain.SysLogininfor;
12
+
13
+/**
14
+ * 记录日志方法
15
+ * 
16
+ * @author ruoyi
17
+ */
18
+@Component
19
+public class SysRecordLogService
20
+{
21
+    @Autowired
22
+    private RemoteLogService remoteLogService;
23
+
24
+    /**
25
+     * 记录登录信息
26
+     * 
27
+     * @param username 用户名
28
+     * @param status 状态
29
+     * @param message 消息内容
30
+     * @return
31
+     */
32
+    public void recordLogininfor(String username, String status, String message)
33
+    {
34
+        SysLogininfor logininfor = new SysLogininfor();
35
+        logininfor.setUserName(username);
36
+        logininfor.setIpaddr(IpUtils.getIpAddr(ServletUtils.getRequest()));
37
+        logininfor.setMsg(message);
38
+        // 日志状态
39
+        if (StringUtils.equalsAny(status, Constants.LOGIN_SUCCESS, Constants.LOGOUT, Constants.REGISTER))
40
+        {
41
+            logininfor.setStatus(Constants.LOGIN_SUCCESS_STATUS);
42
+        }
43
+        else if (Constants.LOGIN_FAIL.equals(status))
44
+        {
45
+            logininfor.setStatus(Constants.LOGIN_FAIL_STATUS);
46
+        }
47
+        remoteLogService.saveLogininfor(logininfor, SecurityConstants.INNER);
48
+    }
49
+}

+ 31 - 1
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/constant/CacheConstants.java

@@ -1,7 +1,7 @@
1 1
 package com.ruoyi.common.core.constant;
2 2
 
3 3
 /**
4
- * 缓存的key 常量
4
+ * 缓存常量信息
5 5
  * 
6 6
  * @author ruoyi
7 7
  */
@@ -18,7 +18,37 @@ public class CacheConstants
18 18
     public final static long REFRESH_TIME = 120;
19 19
 
20 20
     /**
21
+     * 密码最大错误次数
22
+     */
23
+    public final static int passwordMaxRetryCount = 5;
24
+
25
+    /**
26
+     * 密码锁定时间,默认10(分钟)
27
+     */
28
+    public final static long passwordLockTime = 10;
29
+
30
+    /**
21 31
      * 权限缓存前缀
22 32
      */
23 33
     public final static String LOGIN_TOKEN_KEY = "login_tokens:";
34
+
35
+    /**
36
+     * 验证码 redis key
37
+     */
38
+    public static final String CAPTCHA_CODE_KEY = "captcha_codes:";
39
+
40
+    /**
41
+     * 参数管理 cache key
42
+     */
43
+    public static final String SYS_CONFIG_KEY = "sys_config:";
44
+
45
+    /**
46
+     * 字典管理 cache key
47
+     */
48
+    public static final String SYS_DICT_KEY = "sys_dict:";
49
+
50
+    /**
51
+     * 登录账户密码错误次数 redis key
52
+     */
53
+    public static final String PWD_ERR_CNT_KEY = "pwd_err_cnt:";
24 54
 }

+ 0 - 16
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/constant/Constants.java

@@ -103,26 +103,10 @@ public class Constants
103 103
     public static final String IS_ASC = "isAsc";
104 104
 
105 105
     /**
106
-     * 验证码 redis key
107
-     */
108
-    public static final String CAPTCHA_CODE_KEY = "captcha_codes:";
109
-
110
-    /**
111 106
      * 验证码有效期(分钟)
112 107
      */
113 108
     public static final long CAPTCHA_EXPIRATION = 2;
114 109
 
115
-
116
-    /**
117
-     * 参数管理 cache key
118
-     */
119
-    public static final String SYS_CONFIG_KEY = "sys_config:";
120
-
121
-    /**
122
-     * 字典管理 cache key
123
-     */
124
-    public static final String SYS_DICT_KEY = "sys_dict:";
125
-
126 110
     /**
127 111
      * 资源映射路径 前缀
128 112
      */

+ 35 - 14
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/web/domain/AjaxResult.java

@@ -1,6 +1,7 @@
1 1
 package com.ruoyi.common.core.web.domain;
2 2
 
3 3
 import java.util.HashMap;
4
+import java.util.Objects;
4 5
 import com.ruoyi.common.core.constant.HttpStatus;
5 6
 import com.ruoyi.common.core.utils.StringUtils;
6 7
 
@@ -57,20 +58,6 @@ public class AjaxResult extends HashMap<String, Object>
57 58
             super.put(DATA_TAG, data);
58 59
         }
59 60
     }
60
-    
61
-    /**
62
-     * 方便链式调用
63
-     *
64
-     * @param key
65
-     * @param value
66
-     * @return
67
-     */
68
-    @Override
69
-    public AjaxResult put(String key, Object value)
70
-    {
71
-        super.put(key, value);
72
-        return this;
73
-    }
74 61
 
75 62
     /**
76 63
      * 返回成功消息
@@ -159,4 +146,38 @@ public class AjaxResult extends HashMap<String, Object>
159 146
     {
160 147
         return new AjaxResult(code, msg, null);
161 148
     }
149
+
150
+    /**
151
+     * 是否为成功消息
152
+     *
153
+     * @return 结果
154
+     */
155
+    public boolean isSuccess()
156
+    {
157
+        return !isError();
158
+    }
159
+
160
+    /**
161
+     * 是否为错误消息
162
+     *
163
+     * @return 结果
164
+     */
165
+    public boolean isError()
166
+    {
167
+        return Objects.equals(HttpStatus.SUCCESS, this.get(CODE_TAG));
168
+    }
169
+
170
+    /**
171
+     * 方便链式调用
172
+     *
173
+     * @param key
174
+     * @param value
175
+     * @return
176
+     */
177
+    @Override
178
+    public AjaxResult put(String key, Object value)
179
+    {
180
+        super.put(key, value);
181
+        return this;
182
+    }
162 183
 }

+ 3 - 3
ruoyi-common/ruoyi-common-security/src/main/java/com/ruoyi/common/security/utils/DictUtils.java

@@ -3,7 +3,7 @@ package com.ruoyi.common.security.utils;
3 3
 import java.util.Collection;
4 4
 import java.util.List;
5 5
 import com.alibaba.fastjson2.JSONArray;
6
-import com.ruoyi.common.core.constant.Constants;
6
+import com.ruoyi.common.core.constant.CacheConstants;
7 7
 import com.ruoyi.common.core.utils.SpringUtils;
8 8
 import com.ruoyi.common.core.utils.StringUtils;
9 9
 import com.ruoyi.common.redis.service.RedisService;
@@ -58,7 +58,7 @@ public class DictUtils
58 58
      */
59 59
     public static void clearDictCache()
60 60
     {
61
-        Collection<String> keys = SpringUtils.getBean(RedisService.class).keys(Constants.SYS_DICT_KEY + "*");
61
+        Collection<String> keys = SpringUtils.getBean(RedisService.class).keys(CacheConstants.SYS_DICT_KEY + "*");
62 62
         SpringUtils.getBean(RedisService.class).deleteObject(keys);
63 63
     }
64 64
 
@@ -70,6 +70,6 @@ public class DictUtils
70 70
      */
71 71
     public static String getCacheKey(String configKey)
72 72
     {
73
-        return Constants.SYS_DICT_KEY + configKey;
73
+        return CacheConstants.SYS_DICT_KEY + configKey;
74 74
     }
75 75
 }

+ 3 - 2
ruoyi-gateway/src/main/java/com/ruoyi/gateway/service/impl/ValidateCodeServiceImpl.java

@@ -9,6 +9,7 @@ import org.springframework.beans.factory.annotation.Autowired;
9 9
 import org.springframework.stereotype.Service;
10 10
 import org.springframework.util.FastByteArrayOutputStream;
11 11
 import com.google.code.kaptcha.Producer;
12
+import com.ruoyi.common.core.constant.CacheConstants;
12 13
 import com.ruoyi.common.core.constant.Constants;
13 14
 import com.ruoyi.common.core.exception.CaptchaException;
14 15
 import com.ruoyi.common.core.utils.StringUtils;
@@ -55,7 +56,7 @@ public class ValidateCodeServiceImpl implements ValidateCodeService
55 56
 
56 57
         // 保存验证码信息
57 58
         String uuid = IdUtils.simpleUUID();
58
-        String verifyKey = Constants.CAPTCHA_CODE_KEY + uuid;
59
+        String verifyKey = CacheConstants.CAPTCHA_CODE_KEY + uuid;
59 60
 
60 61
         String capStr = null, code = null;
61 62
         BufferedImage image = null;
@@ -106,7 +107,7 @@ public class ValidateCodeServiceImpl implements ValidateCodeService
106 107
         {
107 108
             throw new CaptchaException("验证码已失效");
108 109
         }
109
-        String verifyKey = Constants.CAPTCHA_CODE_KEY + uuid;
110
+        String verifyKey = CacheConstants.CAPTCHA_CODE_KEY + uuid;
110 111
         String captcha = redisService.getCacheObject(verifyKey);
111 112
         redisService.deleteObject(verifyKey);
112 113
 

+ 3 - 3
ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysConfigServiceImpl.java

@@ -5,7 +5,7 @@ import java.util.List;
5 5
 import javax.annotation.PostConstruct;
6 6
 import org.springframework.beans.factory.annotation.Autowired;
7 7
 import org.springframework.stereotype.Service;
8
-import com.ruoyi.common.core.constant.Constants;
8
+import com.ruoyi.common.core.constant.CacheConstants;
9 9
 import com.ruoyi.common.core.constant.UserConstants;
10 10
 import com.ruoyi.common.core.exception.ServiceException;
11 11
 import com.ruoyi.common.core.text.Convert;
@@ -162,7 +162,7 @@ public class SysConfigServiceImpl implements ISysConfigService
162 162
     @Override
163 163
     public void clearConfigCache()
164 164
     {
165
-        Collection<String> keys = redisService.keys(Constants.SYS_CONFIG_KEY + "*");
165
+        Collection<String> keys = redisService.keys(CacheConstants.SYS_CONFIG_KEY + "*");
166 166
         redisService.deleteObject(keys);
167 167
     }
168 168
 
@@ -202,6 +202,6 @@ public class SysConfigServiceImpl implements ISysConfigService
202 202
      */
203 203
     private String getCacheKey(String configKey)
204 204
     {
205
-        return Constants.SYS_CONFIG_KEY + configKey;
205
+        return CacheConstants.SYS_CONFIG_KEY + configKey;
206 206
     }
207 207
 }