需求:为了防止枚举攻击,完成安全性测试扫描。先是保证账号和密码其一错误但返回错误一致,添加Referer拦截器,现在需要限制登录失败次数限制,本文做的是累计登录失败五次账号锁定3小时。(如果有一次登录成功则会重新计数)

登录Controller添加如下代码

    @Autowired
    private RedisTemplate<String, String> redisTemplate;
    //用户登录是否被锁定    三小时 redisKey 前缀
    private String SHIRO_IS_LOCK = "shiro-is-lock:";

    // 以下放在方法内
    ValueOperations<String, String> opsForValue = redisTemplate.opsForValue();
        // 判断该用户是否被锁
        if ("LOCK".equals(opsForValue.get(SHIRO_IS_LOCK + person.getUsername()))) {
            return ResponseVo.fail("登录失败次数过多已锁定,请稍后再试");
        }

 在service实现类里登录失败后按业务增加如下代码


    @Autowired
    private RedisTemplate<String, String> redisTemplate;
    //用户登录次数计数  redisKey 前缀
    private String SHIRO_LOGIN_COUNT = "shiro-login-count:";
    //用户登录是否被锁定    一小时 redisKey 前缀
    private String SHIRO_IS_LOCK = "shiro-is-lock:";


    ValueOperations<String, String> opsForValue = redisTemplate.opsForValue();
    //条件根据实际
    if (result.containsKey("errcode") || result.containsKey("errmsg")) {
            opsForValue.increment(SHIRO_LOGIN_COUNT + username, 1);
            //计数大于5时,设置用户被锁定三小时
            if (Integer.parseInt(opsForValue.get(SHIRO_LOGIN_COUNT + username)) >= 5) {
                opsForValue.set(SHIRO_IS_LOCK + username, "LOCK", 3, TimeUnit.HOURS);
                redisTemplate.expire(SHIRO_LOGIN_COUNT + username,3,TimeUnit.HOURS);
                return ResponseVo.fail("登录失败次数过多已锁定,请稍后再试");
            }

            return ResponseVo.fail("登录失败" + opsForValue.get(SHIRO_LOGIN_COUNT + username) + "次,请检查用户名或密码");
        }
    opsForValue.set(SHIRO_LOGIN_COUNT + username, "0");
    opsForValue.set(SHIRO_IS_LOCK + username, "UNLOCK");

仅本文仅记录个大概实现 具体锁定多久 返回错误码描述 是否需要提示锁定剩余多长时间  根据业务需求而定。

其实也有了解 可以在数据库做几个字段 账号状态 上次登录失败时间等 来做这个需求 但是个人觉得使用redis更加简洁~~

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐