后台添加登陆次数限制(Ruoyi若依前后分离版)
上次添加密文传输后 这次添加了登陆次数限制(后台均可使用此方法和思路)
简单来讲就是 同一用户输入密码错误某次以上 限制登陆一段时间
实现这个功能分为两点
- 记录密码错误次数和错误时间
- 密码正确后清除错误次数
数据库添加字段
在记录用户信息的表中添加两个字段
错误次数 error_nums int类型 (设置默认为0)错误时间 error_times varchar类型或datetime类型
添加接口和字段
在你的用户信息(SysUser.java)实体类里添加字段 并生成getset方法
/** * 错误次数 */private int errorNums;/** * 错误时间 */private String errorTimes;
新增两个接口
/** * 增加密码错误次数 * * @param sysUser 用户信息 * @return 结果 */ public int updateErrorNums(SysUser sysUser); /** * 清空密码错误次数 * * @param username 用户名 * @return 结果 */ public int cleanErrorNums(String username);
我的系统使用的myBatis 所以在xml文件要加上字段 和sql
... ... ... update sys_user set error_nums = if(error_nums is null or LENGTH( trim( error_nums )) = 0, 0, error_nums) + 1, error_times = if(#{errorTimes} is null,now(),#{errorTimes}) where user_name = #{userName} update sys_user set error_nums = 0 where user_name = #{userName}
查询用户角色的sql也需要加上字段
if(error_nums is null or LENGTH( trim( error_nums )) = 0, 0, error_nums) + 1
当error_nums为空 或无值时 赋值为0 加1 如有值直接+1
if(#{errorTimes} is null,now(),#{errorTimes})
当参数errorTimes为null 后无值 传当前时间(yyyy-MM-dd HH:mm:ss)格式
注意下系统服务器的时间是否为北京时间 不是 可以用下面设置更改
--mysqlselect NOW();select sysdate();select localtime();show variables like '%time_zone%';set global time_zone = '+8:00';或set global time_zone='Asia/Shanghai'--linux设置时间date -s "20220527 18:30:50"
添加登陆限制接口
/** * 登陆错误限制 * 更新时sys_user加两字段 * error_nums int类型(默认0!!) * error_times varchar类型 * * @param sysUser */ public void loginError(SysUser sysUser) { //能进来代表用户正常 单纯密码错误 if (sysUser.getErrorNums() > UserStatus.WRONG_TIMES) { long timeNow = DateUtils.timeToStampSecond(DateUtils.getTime()); long errorTimes = DateUtils.timeToStampSecond(sysUser.getErrorTimes()) + UserStatus.WRONG_DURATION * 60; if (errorTimes - timeNow > 0) { String s = DateUtils.formatHMS(errorTimes - timeNow); throw new CustomException("对不起,您的账号:" + sysUser.getUserName() + " 错误次数过多,请" + s + "后重试"); } } if (sysUser.getErrorNums() < UserStatus.WRONG_TIMES) { userService.updateErrorNums(sysUser); throw new CustomException("还有" + (UserStatus.WRONG_TIMES - sysUser.getErrorNums()) + "次输入机会"); } else { sysUser.setErrorTimes(DateUtils.getTime()); userService.updateErrorNums(sysUser); throw new CustomException("对不起,您的账号:" + sysUser.getUserName() + " 错误次数过多,请" + UserStatus.WRONG_DURATION + "分钟后重试"); } }
timeToStampSecond()是string类型日期转时间戳(秒级)方法
formatHMS() 是long类型时间(秒级)转 天/小时/分钟/秒 大家可以添加下方法
/** * String类型日期(2021-12-07 10:00:00)转时间戳(秒级) * * @param time * @return */ public static long timeToStampSecond(String time) { Date d = new Date(); long timeStamp = 0; try { SimpleDateFormat sf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); d = sf.parse(time);// 日期转换为时间戳 } catch (ParseException e) { // TODO Auto-generated catch block e.printStackTrace(); } timeStamp = d.getTime() / 1000; return timeStamp; }/** * 秒转天/小时/分钟/秒 * * @param value * @return */ public static String formatHMS(long value) { String str = ""; long i = value / (3600 * 24); if (i > 0) { str = i + "天"; } value %= 3600 * 24; i = value / 3600; if (i > 0) { str += i + "时"; } value %= 3600; i = value / 60; if (i > 0) { str += i + "分"; } value %= 60; str += value + "秒"; return str; }
WRONG_TIMES和WRONG_DURATION是我添加的枚举类字段 方便之后更改限制次数和时间
//密码错误次数 public static int WRONG_TIMES = 3; //密码解锁时长(分钟) public static int WRONG_DURATION = 5;
现在接口都已写好 就看在哪里使用了
打开SysLoginService.java
Authentication authentication = null; try { // 该方法会去调用UserDetailsServiceImpl.loadUserByUsername/decryptByPrivateKey()私钥解密 authentication = authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(username, RsaUtils.decryptByPrivateKey(password))); } catch (Exception e) { //进入这里代表密码错误 if (e instanceof BadCredentialsException) { //登陆限制 错误三次限制五分钟 loginError(userService.selectUserByUserName(username)); AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.password.not.match"))); throw new UserPasswordNotMatchException(); } else { AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, e.getMessage())); throw new CustomException(e.getMessage()); } }
loginError(userService.selectUserByUserName(username));
要放在catch后 在密码误时引用 如果放在try中 会影响UserDetailsServiceImpl中的账号判断
最后大家需要跟具自己代码做调整 有什么问题欢迎留言讨论😋