Commit 39e7f7f735418a554b797020d96b01a04c6e4c42
Merge remote-tracking branch 'upstream/master'
Showing
15 changed files
with
222 additions
and
12 deletions
ruoyi-admin/src/main/java/com/ruoyi/web/controller/monitor/CacheController.java
... | ... | @@ -41,6 +41,7 @@ public class CacheController |
41 | 41 | caches.add(new SysCache(CacheConstants.CAPTCHA_CODE_KEY, "验证码")); |
42 | 42 | caches.add(new SysCache(CacheConstants.REPEAT_SUBMIT_KEY, "防重提交")); |
43 | 43 | caches.add(new SysCache(CacheConstants.RATE_LIMIT_KEY, "限流处理")); |
44 | + caches.add(new SysCache(CacheConstants.PWD_ERR_CNT_KEY, "密码错误次数")); | |
44 | 45 | } |
45 | 46 | |
46 | 47 | @PreAuthorize("@ss.hasPermi('monitor:cache:list')") |
... | ... |
ruoyi-admin/src/main/resources/application.yml
ruoyi-admin/src/main/resources/i18n/messages.properties
... | ... | @@ -5,7 +5,7 @@ user.jcaptcha.expire=验证码已失效 |
5 | 5 | user.not.exists=用户不存在/密码错误 |
6 | 6 | user.password.not.match=用户不存在/密码错误 |
7 | 7 | user.password.retry.limit.count=密码输入错误{0}次 |
8 | -user.password.retry.limit.exceed=密码输入错误{0}次,帐户锁定10分钟 | |
8 | +user.password.retry.limit.exceed=密码输入错误{0}次,帐户锁定{1}分钟 | |
9 | 9 | user.password.delete=对不起,您的账号已被删除 |
10 | 10 | user.blocked=用户已封禁,请联系管理员 |
11 | 11 | role.blocked=角色已封禁,请联系管理员 |
... | ... |
ruoyi-common/src/main/java/com/ruoyi/common/constant/CacheConstants.java
ruoyi-common/src/main/java/com/ruoyi/common/core/redis/RedisCache.java
... | ... | @@ -75,6 +75,28 @@ public class RedisCache |
75 | 75 | } |
76 | 76 | |
77 | 77 | /** |
78 | + * 获取有效时间 | |
79 | + * | |
80 | + * @param key Redis键 | |
81 | + * @return 有效时间 | |
82 | + */ | |
83 | + public long getExpire(final String key) | |
84 | + { | |
85 | + return redisTemplate.getExpire(key); | |
86 | + } | |
87 | + | |
88 | + /** | |
89 | + * 判断 key是否存在 | |
90 | + * | |
91 | + * @param key 键 | |
92 | + * @return true 存在 false不存在 | |
93 | + */ | |
94 | + public Boolean hasKey(String key) | |
95 | + { | |
96 | + return redisTemplate.hasKey(key); | |
97 | + } | |
98 | + | |
99 | + /** | |
78 | 100 | * 获得缓存的基本对象。 |
79 | 101 | * |
80 | 102 | * @param key 缓存键值 |
... | ... |
ruoyi-common/src/main/java/com/ruoyi/common/exception/user/UserPasswordRetryLimitExceedException.java
0 → 100644
1 | +package com.ruoyi.common.exception.user; | |
2 | + | |
3 | +/** | |
4 | + * 用户错误最大次数异常类 | |
5 | + * | |
6 | + * @author ruoyi | |
7 | + */ | |
8 | +public class UserPasswordRetryLimitExceedException extends UserException | |
9 | +{ | |
10 | + private static final long serialVersionUID = 1L; | |
11 | + | |
12 | + public UserPasswordRetryLimitExceedException(int retryLimitCount, int lockTime) | |
13 | + { | |
14 | + super("user.password.retry.limit.exceed", new Object[] { retryLimitCount, lockTime }); | |
15 | + } | |
16 | +} | |
... | ... |
ruoyi-framework/src/main/java/com/ruoyi/framework/security/context/AuthenticationContextHolder.java
0 → 100644
1 | +package com.ruoyi.framework.security.context; | |
2 | + | |
3 | +import org.springframework.security.core.Authentication; | |
4 | + | |
5 | +/** | |
6 | + * 身份验证信息 | |
7 | + * | |
8 | + * @author ruoyi | |
9 | + */ | |
10 | +public class AuthenticationContextHolder | |
11 | +{ | |
12 | + private static final ThreadLocal<Authentication> contextHolder = new ThreadLocal<>(); | |
13 | + | |
14 | + public static Authentication getContext() | |
15 | + { | |
16 | + return contextHolder.get(); | |
17 | + } | |
18 | + | |
19 | + public static void setContext(Authentication context) | |
20 | + { | |
21 | + contextHolder.set(context); | |
22 | + } | |
23 | + | |
24 | + public static void clearContext() | |
25 | + { | |
26 | + contextHolder.remove(); | |
27 | + } | |
28 | +} | |
... | ... |
ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/SysLoginService.java
... | ... | @@ -23,6 +23,7 @@ import com.ruoyi.common.utils.StringUtils; |
23 | 23 | import com.ruoyi.common.utils.ip.IpUtils; |
24 | 24 | import com.ruoyi.framework.manager.AsyncManager; |
25 | 25 | import com.ruoyi.framework.manager.factory.AsyncFactory; |
26 | +import com.ruoyi.framework.security.context.AuthenticationContextHolder; | |
26 | 27 | import com.ruoyi.system.service.ISysConfigService; |
27 | 28 | import com.ruoyi.system.service.ISysUserService; |
28 | 29 | |
... | ... | @@ -70,9 +71,10 @@ public class SysLoginService |
70 | 71 | Authentication authentication = null; |
71 | 72 | try |
72 | 73 | { |
74 | + UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(username, password); | |
75 | + AuthenticationContextHolder.setContext(authenticationToken); | |
73 | 76 | // 该方法会去调用UserDetailsServiceImpl.loadUserByUsername |
74 | - authentication = authenticationManager | |
75 | - .authenticate(new UsernamePasswordAuthenticationToken(username, password)); | |
77 | + authentication = authenticationManager.authenticate(authenticationToken); | |
76 | 78 | } |
77 | 79 | catch (Exception e) |
78 | 80 | { |
... | ... |
ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/SysPasswordService.java
0 → 100644
1 | +package com.ruoyi.framework.web.service; | |
2 | + | |
3 | +import java.util.concurrent.TimeUnit; | |
4 | +import org.springframework.beans.factory.annotation.Autowired; | |
5 | +import org.springframework.beans.factory.annotation.Value; | |
6 | +import org.springframework.security.core.Authentication; | |
7 | +import org.springframework.stereotype.Component; | |
8 | +import com.ruoyi.common.constant.CacheConstants; | |
9 | +import com.ruoyi.common.constant.Constants; | |
10 | +import com.ruoyi.common.core.domain.entity.SysUser; | |
11 | +import com.ruoyi.common.core.redis.RedisCache; | |
12 | +import com.ruoyi.common.exception.user.UserPasswordNotMatchException; | |
13 | +import com.ruoyi.common.exception.user.UserPasswordRetryLimitExceedException; | |
14 | +import com.ruoyi.common.utils.MessageUtils; | |
15 | +import com.ruoyi.common.utils.SecurityUtils; | |
16 | +import com.ruoyi.framework.manager.AsyncManager; | |
17 | +import com.ruoyi.framework.manager.factory.AsyncFactory; | |
18 | +import com.ruoyi.framework.security.context.AuthenticationContextHolder; | |
19 | + | |
20 | +/** | |
21 | + * 登录密码方法 | |
22 | + * | |
23 | + * @author ruoyi | |
24 | + */ | |
25 | +@Component | |
26 | +public class SysPasswordService | |
27 | +{ | |
28 | + @Autowired | |
29 | + private RedisCache redisCache; | |
30 | + | |
31 | + @Value(value = "${user.password.maxRetryCount}") | |
32 | + private int maxRetryCount; | |
33 | + | |
34 | + @Value(value = "${user.password.lockTime}") | |
35 | + private int lockTime; | |
36 | + | |
37 | + /** | |
38 | + * 登录账户密码错误次数缓存键名 | |
39 | + * | |
40 | + * @param username 用户名 | |
41 | + * @return 缓存键key | |
42 | + */ | |
43 | + private String getCacheKey(String username) | |
44 | + { | |
45 | + return CacheConstants.PWD_ERR_CNT_KEY + username; | |
46 | + } | |
47 | + | |
48 | + public void validate(SysUser user) | |
49 | + { | |
50 | + Authentication usernamePasswordAuthenticationToken = AuthenticationContextHolder.getContext(); | |
51 | + String username = usernamePasswordAuthenticationToken.getName(); | |
52 | + String password = usernamePasswordAuthenticationToken.getCredentials().toString(); | |
53 | + | |
54 | + Integer retryCount = redisCache.getCacheObject(getCacheKey(username)); | |
55 | + | |
56 | + if (retryCount == null) | |
57 | + { | |
58 | + retryCount = 0; | |
59 | + } | |
60 | + | |
61 | + if (retryCount >= Integer.valueOf(maxRetryCount).intValue()) | |
62 | + { | |
63 | + AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, | |
64 | + MessageUtils.message("user.password.retry.limit.exceed", maxRetryCount, lockTime))); | |
65 | + throw new UserPasswordRetryLimitExceedException(maxRetryCount, lockTime); | |
66 | + } | |
67 | + | |
68 | + if (!matches(user, password)) | |
69 | + { | |
70 | + retryCount = retryCount + 1; | |
71 | + AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, | |
72 | + MessageUtils.message("user.password.retry.limit.count", retryCount))); | |
73 | + redisCache.setCacheObject(getCacheKey(username), retryCount, lockTime, TimeUnit.MINUTES); | |
74 | + throw new UserPasswordNotMatchException(); | |
75 | + } | |
76 | + else | |
77 | + { | |
78 | + clearLoginRecordCache(username); | |
79 | + } | |
80 | + } | |
81 | + | |
82 | + public boolean matches(SysUser user, String rawPassword) | |
83 | + { | |
84 | + return SecurityUtils.matchesPassword(rawPassword, user.getPassword()); | |
85 | + } | |
86 | + | |
87 | + public void clearLoginRecordCache(String loginName) | |
88 | + { | |
89 | + if (redisCache.hasKey(getCacheKey(loginName))) | |
90 | + { | |
91 | + redisCache.deleteObject(getCacheKey(loginName)); | |
92 | + } | |
93 | + } | |
94 | +} | |
... | ... |
ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/UserDetailsServiceImpl.java
... | ... | @@ -26,6 +26,9 @@ public class UserDetailsServiceImpl implements UserDetailsService |
26 | 26 | |
27 | 27 | @Autowired |
28 | 28 | private ISysUserService userService; |
29 | + | |
30 | + @Autowired | |
31 | + private SysPasswordService passwordService; | |
29 | 32 | |
30 | 33 | @Autowired |
31 | 34 | private SysPermissionService permissionService; |
... | ... | @@ -50,6 +53,8 @@ public class UserDetailsServiceImpl implements UserDetailsService |
50 | 53 | throw new ServiceException("对不起,您的账号:" + username + " 已停用"); |
51 | 54 | } |
52 | 55 | |
56 | + passwordService.validate(user); | |
57 | + | |
53 | 58 | return createLoginUser(user); |
54 | 59 | } |
55 | 60 | |
... | ... |
ruoyi-quartz/src/main/java/com/ruoyi/quartz/controller/SysJobController.java
... | ... | @@ -167,8 +167,8 @@ public class SysJobController extends BaseController |
167 | 167 | @PutMapping("/run") |
168 | 168 | public AjaxResult run(@RequestBody SysJob job) throws SchedulerException |
169 | 169 | { |
170 | - jobService.run(job); | |
171 | - return AjaxResult.success(); | |
170 | + boolean result = jobService.run(job); | |
171 | + return result ? success() : error("任务不存在或已过期!"); | |
172 | 172 | } |
173 | 173 | |
174 | 174 | /** |
... | ... | @@ -180,6 +180,6 @@ public class SysJobController extends BaseController |
180 | 180 | public AjaxResult remove(@PathVariable Long[] jobIds) throws SchedulerException, TaskException |
181 | 181 | { |
182 | 182 | jobService.deleteJobByIds(jobIds); |
183 | - return AjaxResult.success(); | |
183 | + return success(); | |
184 | 184 | } |
185 | 185 | } |
... | ... |
ruoyi-quartz/src/main/java/com/ruoyi/quartz/service/ISysJobService.java
ruoyi-quartz/src/main/java/com/ruoyi/quartz/service/impl/SysJobServiceImpl.java
... | ... | @@ -174,15 +174,22 @@ public class SysJobServiceImpl implements ISysJobService |
174 | 174 | */ |
175 | 175 | @Override |
176 | 176 | @Transactional(rollbackFor = Exception.class) |
177 | - public void run(SysJob job) throws SchedulerException | |
177 | + public boolean run(SysJob job) throws SchedulerException | |
178 | 178 | { |
179 | + boolean result = false; | |
179 | 180 | Long jobId = job.getJobId(); |
180 | 181 | String jobGroup = job.getJobGroup(); |
181 | 182 | SysJob properties = selectJobById(job.getJobId()); |
182 | 183 | // 参数 |
183 | 184 | JobDataMap dataMap = new JobDataMap(); |
184 | 185 | dataMap.put(ScheduleConstants.TASK_PROPERTIES, properties); |
185 | - scheduler.triggerJob(ScheduleUtils.getJobKey(jobId, jobGroup), dataMap); | |
186 | + JobKey jobKey = ScheduleUtils.getJobKey(jobId, jobGroup); | |
187 | + if (scheduler.checkExists(jobKey)) | |
188 | + { | |
189 | + result = true; | |
190 | + scheduler.triggerJob(jobKey, dataMap); | |
191 | + } | |
192 | + return result; | |
186 | 193 | } |
187 | 194 | |
188 | 195 | /** |
... | ... |
ruoyi-quartz/src/main/java/com/ruoyi/quartz/util/ScheduleUtils.java
... | ... | @@ -83,7 +83,12 @@ public class ScheduleUtils |
83 | 83 | scheduler.deleteJob(getJobKey(jobId, jobGroup)); |
84 | 84 | } |
85 | 85 | |
86 | - scheduler.scheduleJob(jobDetail, trigger); | |
86 | + // 判断任务是否过期 | |
87 | + if (StringUtils.isNotNull(CronUtils.getNextExecution(job.getCronExpression()))) | |
88 | + { | |
89 | + // 执行调度任务 | |
90 | + scheduler.scheduleJob(jobDetail, trigger); | |
91 | + } | |
87 | 92 | |
88 | 93 | // 暂停任务 |
89 | 94 | if (job.getStatus().equals(ScheduleConstants.Status.PAUSE.getValue())) |
... | ... |
ruoyi-ui/src/components/RightToolbar/index.vue
1 | 1 | <template> |
2 | - <div class="top-right-btn"> | |
2 | + <div class="top-right-btn" :style="style"> | |
3 | 3 | <el-row> |
4 | - <el-tooltip class="item" effect="dark" :content="showSearch ? '隐藏搜索' : '显示搜索'" placement="top"> | |
4 | + <el-tooltip class="item" effect="dark" :content="showSearch ? '隐藏搜索' : '显示搜索'" placement="top" v-if="search"> | |
5 | 5 | <el-button size="mini" circle icon="el-icon-search" @click="toggleSearch()" /> |
6 | 6 | </el-tooltip> |
7 | 7 | <el-tooltip class="item" effect="dark" content="刷新" placement="top"> |
... | ... | @@ -42,6 +42,23 @@ export default { |
42 | 42 | columns: { |
43 | 43 | type: Array, |
44 | 44 | }, |
45 | + search: { | |
46 | + type: Boolean, | |
47 | + default: true, | |
48 | + }, | |
49 | + gutter: { | |
50 | + type: Number, | |
51 | + default: 10, | |
52 | + }, | |
53 | + }, | |
54 | + computed: { | |
55 | + style() { | |
56 | + const ret = {}; | |
57 | + if (this.gutter) { | |
58 | + ret.marginRight = `${this.gutter / 2}px`; | |
59 | + } | |
60 | + return ret; | |
61 | + } | |
45 | 62 | }, |
46 | 63 | created() { |
47 | 64 | // 显隐列初始默认隐藏列 |
... | ... |