Commit 39e7f7f735418a554b797020d96b01a04c6e4c42

Authored by “zyj”
2 parents 2ff42a1d aee5d417

Merge remote-tracking branch 'upstream/master'

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
... ... @@ -39,6 +39,14 @@ logging:
39 39 com.ruoyi: debug
40 40 org.springframework: warn
41 41  
  42 +# 用户配置
  43 +user:
  44 + password:
  45 + # 密码最大错误次数
  46 + maxRetryCount: 5
  47 + # 密码锁定时间(默认10分钟)
  48 + lockTime: 10
  49 +
42 50 # Spring配置
43 51 spring:
44 52 # 资源信息
... ...
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
... ... @@ -36,4 +36,9 @@ public class CacheConstants
36 36 * 限流 redis key
37 37 */
38 38 public static final String RATE_LIMIT_KEY = "rate_limit:";
  39 +
  40 + /**
  41 + * 登录账户密码错误次数 redis key
  42 + */
  43 + public static final String PWD_ERR_CNT_KEY = "pwd_err_cnt:";
39 44 }
... ...
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
... ... @@ -74,7 +74,7 @@ public interface ISysJobService
74 74 * @param job 调度信息
75 75 * @return 结果
76 76 */
77   - public void run(SysJob job) throws SchedulerException;
  77 + public boolean run(SysJob job) throws SchedulerException;
78 78  
79 79 /**
80 80 * 新增任务
... ...
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 // 显隐列初始默认隐藏列
... ...