根据登陆次数,提供验证码服务。

文章介绍

验证码,现已成为每个系统必备的一部分。验证码的出现,主要就是为了防止某些恶意用户频繁注册系统账户,或者使用其他手段登陆系统,非法获取信息或篡改信息。因此,验证码的出现就好比拦路虎,能起到一定的防护作用。当然,不是有了验证码,你的系统就高枕无忧了,现在识别验证码的程序也非常多,想绕过验证码也是非常简单的。

我们知道,虽然验证码在保护系统安全方面发挥了那么一点作用,但是每次用户登陆时,都需要进行输入验证码。为了减少用户负担,我们设置了一个账号密码的校验次数,当用户输入密码超过三次时,之后再进行登陆时,需要提供验证码才能进行登陆。

设计目的

  1. 方便普通用户操作,提高登陆效率
  2. 防止恶意用户使用脚本,恶意测试账户

流程图

在这里插入图片描述

设计弊端

程序初步设计时,后端是以用户名的主键id来记录登陆失败的次数,前端不做失败次数的记录。这样设计的弊端在于并不能保证是同一操作人员发送的请求。也就是说,用户登陆时,即使账户一错误了三次,出现了验证码,但是此时切换到账户二时,此时便不需要验证码。但是可以保证的是,恶意用户进行测试登陆时,同一账户只能使用三次,三次之后就出现了验证码。

理想状态:
只要用户失败了三次之后,不管用哪个账户,之后登录都得使用验证码。

实现步骤
方法一:
前端通过计算登录次数,当登录次数大于3时,之后登录都请求获取验证码。
方法二:
当请求含有验证码时,只要登录失败,就都返回验证码。

  1. 判断是否需要进行验证码校验
    /**
     * @param userId
     * @param authUser
     * @return java.util.Map<java.lang.String, java.lang.Object>
     * @description: 验证码校验
     * @date: 2021/8/29 19:03
     * @author: zhq*
     */
    public Map<String, Object> judgeIsVerify(long userId, AuthUserDto authUser) {
    
    
        String failUserKey = CacheKey.AfterUSER_FAIL_LOG + userId;
        int failNum = Integer.valueOf(redisUtils.get(failUserKey).toString());
        if (redisUtils.hasKey(failUserKey)) {
    
    
            if (failNum >= 3) {
    
    
                //进行验证码校验
                // 查询验证码
                String code = (String) redisUtils.get(authUser.getUuid());
                // 清除验证码
                redisUtils.del(authUser.getUuid());
                if (StrUtil.isBlank(code)) {
    
    
                    return getFailLoginResAndVerifyCode(true, "验证码不存在或已过期");
                }
                if (StrUtil.isBlank(authUser.getCode()) || !authUser.getCode().equalsIgnoreCase(code)) {
    
    
                    return getFailLoginResAndVerifyCode(true, "验证码错误");
                }
            }
        }
        return null;
    }
  1. 用户名或账号错误,记录登陆失败次数,并返回失败结果
    /**
     * @return java.util.Map<java.lang.String, java.lang.Object>
     * @description: 校验当前账号的登陆次数  大于3  返回验证码
     * @date: 2021/8/29 18:22
     * @author: zhq
     */
    public Map<String, Object> failLoginRes(long userId) {
    
    
        String failUserKey = CacheKey.AfterUSER_FAIL_LOG + userId;
        long time = 1000 * 60 * 5;
        int failNum = Integer.valueOf(redisUtils.get(failUserKey).toString());
        if (redisUtils.hasKey(failUserKey)) {
    
    
            if (failNum >= 3) {
    
    
                return getFailLoginResAndVerifyCode(true, "账号或密码错误");
            } else {
    
    
                redisUtils.set(failUserKey, ++failNum, time);
                return getFailLoginResAndVerifyCode(false, "账号或密码错误");
            }
        } else {
    
    
            redisUtils.set(failUserKey, 0, time);
            return getFailLoginResAndVerifyCode(false, "账号或密码错误");
        }
    }

  1. 登陆失败结果返回,返回时,根据isHasVerifyCode来判断是否返回前端验证码
    /**
     * @param
     * @return java.util.Map<java.lang.String, java.lang.String>
     * @description: 登陆失败  获取登陆验证码  需要验证
     * @date: 2021/8/29 12:25
     * @author: zhq
     */
    public Map<String, Object> getFailLoginResAndVerifyCode(boolean isHasVerifyCode, String failReason) {
    
    
        String captchaValue = "";
        String uuid = "";
        if (isHasVerifyCode) {
    
    
            Captcha captcha = loginProperties.getCaptcha();
            while (Double.parseDouble(captcha.text()) < 0) {
    
    
                captcha = loginProperties.getCaptcha();
            }
            captchaValue = captcha.text();
            uuid = properties.getCodeKey() + IdUtil.simpleUUID();
            //当验证码类型为 arithmetic时且长度 >= 2 时,captcha.text()的结果有几率为浮点型
            if (captcha.getCharType() - 1 == LoginCodeEnum.arithmetic.ordinal() & captchaValue.contains(".")) {
    
    
                captchaValue = captchaValue.split("\\.")[0];
            }
            // 保存
            redisUtils.set(uuid, captchaValue, loginProperties.getLoginCode().getExpiration(), TimeUnit.MINUTES);
        }
        Map<String, Object> loginRes = new HashMap<>();
        loginRes.put("token", "");
        loginRes.put("user", "");
        loginRes.put("loginStatus", false);
        loginRes.put("isVerify", isHasVerifyCode);
        loginRes.put("img", captchaValue);
        loginRes.put("uuid", uuid);
        loginRes.put("failReason", failReason);
        return loginRes;
    }
  1. 成功返回。成功后,删除缓存,并将用户的信息返回给前端。
    /**
     * @param
     * @return java.util.Map<java.lang.String, java.lang.String>
     * @description: 登陆成功  返回token和用户信息
     * @date: 2021/8/29 12:25
     * @author: zhq
     */
    public Map<String, Object> getFailLoginResAndVerifyCode(String token, JwtUserDto jwtUserDto, long userId) {
    
    
        String failUserKey = CacheKey.AfterUSER_FAIL_LOG + userId;
        redisUtils.del(failUserKey);
        Map<String, Object> loginRes = new HashMap<>();
        loginRes.put("token", token);
        loginRes.put("user", jwtUserDto);
        loginRes.put("loginStatus", true);
        loginRes.put("isVerify", false);
        loginRes.put("img", "");
        loginRes.put("uuid", "");
        loginRes.put("failReason", "");
        return loginRes;
    }

Guess you like

Origin blog.csdn.net/zhang19903848257/article/details/119984734