上次添加密文传输后 这次添加了登陆次数限制(后台均可使用此方法和思路)

简单来讲就是 同一用户输入密码错误某次以上 限制登陆一段时间


实现这个功能分为两点

  1. 记录密码错误次数和错误时间
  2. 密码正确后清除错误次数

数据库添加字段

在记录用户信息的表中添加两个字段

错误次数 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

        
<resultMap type="SysUser" id="SysUserResult">
        ...
        ...
        <result property="errorNums" column="error_nums"/>
        <result property="errorTimes" column="error_times"/>
        ...
</resultMap>


    <!--is null or LENGTH( trim( 字段 ) 判断是否为空 或无值 -->
    <update id="updateErrorNums" parameterType="SysUser">
 		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>

    <update id="cleanErrorNums" parameterType="String">
 		update sys_user set error_nums = 0 where user_name = #{userName}
	</update>

查询用户角色的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)格式

注意下系统服务器的时间是否为北京时间 不是 可以用下面设置更改

--mysql
select 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 + "后重试");
            }
        }
            /**如果想添加某时间段内错误某次后限制
              在第一次密码错误时添加错误时间,再次密码错误时,对比和第一次密码错误的时间
              如在限制时间内 错误次数+1(第一次的错误时间不要覆盖)
              如超过自定义的时间则清除错误时间和错误次数
            */
        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

 /**
  *登录验证
  */
public String login(String username, String password, String code, String uuid) {
              .......
              .......

              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());
            }
        }
        //登陆成功后记得清除错误次数
        userService.cleanErrorNums(username);
        ......
        ......
}

验证错误次数的方法 要放在catch中 因为只是在密码误时引用 如果放在try中 会影响UserDetailsServiceImpl中的账号判断

登陆成功后记得清除错误次数 方法要放在try catch后面

 最后大家需要跟具自己代码做调整 有什么问题欢迎留言讨论😋

Logo

快速构建 Web 应用程序

更多推荐