登录时动态验证码的使用

文章介绍了如何在Java项目中创建一个登录接口,并通过动态验证码增强安全性。首先定义了枚举类型以选择不同的验证码类型,接着创建了验证码实体类来存储配置信息,然后设置了配置类用于根据这些信息生成不同类型的验证码。最后,提供了一个获取验证码的接口和登录时的验证码验证逻辑。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

    当我们写一个登录接口时,都会想到使用一个图片生成一个动态验证码去增加我们功能的严谨性和复杂性,下面就开始讲述我们如何在项目中去使用它。

一、创建枚举

public enum  LoginCodeEnum {
    /**
     * 算数
     */
    ARITHMETIC,
    /**
     * 中文
     */
    CHINESE,
    /**
     * 中文闪图
     */
    CHINESE_GIF,
    /**
     * 闪图
     */
    GIF,
    SPEC
}

二、创建一个实体类

@Data
public class LoginCode {
    /**
     * 验证码配置
     */
    private LoginCodeEnum codeType;
    /**
     * 验证码有效期 分钟
     */
    private Long expiration = 2L;
    /**
     * 验证码内容长度
     */
    private int length = 2;
    /**
     * 验证码宽度
     */
    private int width = 111;
    /**
     * 验证码高度
     */
    private int height = 36;
    /**
     * 验证码字体
     */
    private String fontName;
    /**
     * 字体大小
     */
    private int fontSize = 25;

    /**
     * 验证码前缀
     * @return
     */
    private  String   codeKey;


    public LoginCodeEnum getCodeType() {
        return codeType;
    }
}

三、创建配置类

@Configuration
public class LoginCodeConfig {

    private LoginCode loginCode;


    /**
     * 获取验证码生产类
     * @return
     */
    public Captcha getCaptcha(){
        if(Objects.isNull(loginCode)){
            loginCode = new LoginCode();
            if(Objects.isNull(loginCode.getCodeType())){
                loginCode.setCodeType(LoginCodeEnum.ARITHMETIC);
            }

        }
        return switchCaptcha(loginCode);
    }

    /**
     * 依据配置信息生产验证码
     * @param loginCode
     * @return
     */
    private Captcha switchCaptcha(LoginCode loginCode){
        Captcha captcha = null;
        synchronized (this){
            switch (loginCode.getCodeType()){
                case ARITHMETIC:
                    captcha = new FixedArithmeticCaptcha(loginCode.getWidth(),loginCode.getHeight());
                    captcha.setLen(loginCode.getLength());
                    break;
                case CHINESE:
                    captcha = new ChineseCaptcha(loginCode.getWidth(),loginCode.getHeight());
                    captcha.setLen(loginCode.getLength());
                    break;
                case CHINESE_GIF:
                    captcha = new ChineseGifCaptcha(loginCode.getWidth(),loginCode.getHeight());
                    captcha.setLen(loginCode.getLength());
                    break;
                case GIF:
                    captcha = new GifCaptcha(loginCode.getWidth(),loginCode.getHeight());
                    captcha.setLen(loginCode.getLength());
                    break;
                case SPEC:
                    captcha = new SpecCaptcha(loginCode.getWidth(),loginCode.getHeight());
                    captcha.setLen(loginCode.getLength());
                default:
                    System.out.println("验证码配置信息错误!正确配置查看 LoginCodeEnum ");

            }
        }
        if(StringUtils.isNotBlank(loginCode.getFontName())){
            captcha.setFont(new Font(loginCode.getFontName(),Font.PLAIN,loginCode.getFontSize()));
        }
        return captcha;
    }

    static  class FixedArithmeticCaptcha extends ArithmeticCaptcha {
        public FixedArithmeticCaptcha(int width,int height){
            super(width,height);
        }

        @Override
        protected char[] alphas() {
            // 生成随机数字和运算符
            int n1 = num(1, 10), n2 = num(1, 10);
            int opt = num(3);

            // 计算结果
            int res = new int[]{n1 + n2, n1 - n2, n1 * n2}[opt];
            // 转换为字符运算符
            char optChar = "+-x".charAt(opt);

            this.setArithmeticString(String.format("%s%c%s=?", n1, optChar, n2));
            this.chars = String.valueOf(res);

            return chars.toCharArray();
        }
    }

四、编写获取验证码的接口

 @GetMapping("/getLoginCode")
    public ResponseBean getLoginCode(){
        Captcha captcha = loginCodeConfig.getCaptcha();
        String uuid = "code-key-"+ UUID.randomUUID().toString().replaceAll("-","");
        //当验证码类型为 arithmetic时且长度 >= 2 时,captcha.text()的结果有几率为浮点型
        String captchaValue = captcha.text();
        if(captcha.getCharType()-1 == LoginCodeEnum.ARITHMETIC.ordinal() && captchaValue.contains(".")){
            captchaValue = captchaValue.split("\\.")[0];
        }
        // 保存
        redisTemplate.opsForHash().put(uuid,"loginCode",captchaValue);
        // 验证码信息
        log.info("loginCode.uuid:{},captchaValue:{},loginProperties.getLoginCode().getExpiration():{}, TimeUnit.MINUTES:{}",uuid,captchaValue,captcha.text(), TimeUnit.MINUTES);
        Map<String,Object> imgResult = new HashMap<String,Object>(2){{
            put("img",captcha.toBase64());
            put("loginCodeId",uuid);
        }};
        return new ResponseBean(1,"获取成功",imgResult);
    }

五、登录时,相关验证码逻辑

 //从redis获取存储的验证码
        Object loginCodePwd=redisTemplate.opsForHash().get(bdOrganizeEmployeeDTO.getLoginCodeId(),"loginCode");
        //验证码未获取到
        if(loginCodePwd==null) return new ResponseBean(CodeEnum.LOGIN_CODE_TIMEOUT.getCode(), CodeEnum.LOGIN_CODE_TIMEOUT.getMsg(), null);
        //从redis中删除,验证失败,让前端重新获取验证码,验证成功也不再需要
        redisTemplate.opsForHash().delete(bdOrganizeEmployeeDTO.getLoginCodeId(),"loginCode");
        //验证码填写不正确
        if(!loginCodePwd.toString().equals(bdOrganizeEmployeeDTO.getLoginCode())) return new ResponseBean(CodeEnum.LOGIN_CODE_ERROR.getCode(), CodeEnum.LOGIN_CODE_ERROR.getMsg(), null);
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

木木的成长之路

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值