学习总结录 学习总结录
首页
归档
分类
标签
  • Java基础
  • Java集合
  • MySQL
  • Redis
  • JVM
  • 多线程
  • 计算机网络
  • 操作系统
  • Spring
  • Kafka
  • Elasticsearch
  • Python
  • 面试专题
  • 案例实践
  • 工具使用
  • 项目搭建
  • 服务治理
  • ORM框架
  • 分布式组件
  • MiniSpring
  • 设计模式
  • 算法思想
  • 编码规范
友链
关于
GitHub (opens new window)
首页
归档
分类
标签
  • Java基础
  • Java集合
  • MySQL
  • Redis
  • JVM
  • 多线程
  • 计算机网络
  • 操作系统
  • Spring
  • Kafka
  • Elasticsearch
  • Python
  • 面试专题
  • 案例实践
  • 工具使用
  • 项目搭建
  • 服务治理
  • ORM框架
  • 分布式组件
  • MiniSpring
  • 设计模式
  • 算法思想
  • 编码规范
友链
关于
GitHub (opens new window)
  • 案例实践

    • 定时任务
    • 邮件发送
    • 日志管理
    • Word生成
    • 数据导入
    • 验证码
      • 验证码概述
      • 普通验证码实现
        • 添加依赖
        • 配置验证码
        • 生成验证码
        • 获取验证码
        • 校验验证码
      • 滑块验证码实现
        • 添加依赖
        • 配置文件
        • 相关接口
        • 前端实现
      • 参考
  • 工具使用

  • 项目搭建

  • 服务治理

  • ORM框架

  • MiniSpring

  • 案例归档
  • 案例实践
旭日
2023-03-27
目录

验证码

# 验证码概述

验证码通常用于登录、注册的表单中,防止恶意的重复请求。目前验证码大致的分类有两种:常规验证码和滑块验证码。

  • 常规验证码

    常规验证码一般就是以前那种需要输入对应验证码的数字、英文等。

    动画

  • 滑块验证码

    滑块验证码则极大的提高了用户的体验,用户只需要拖动鼠标就能够完成校验,目前很多校验都已经采用了滑块校验。

    动画2

# 普通验证码实现

# 添加依赖

普通验证码这里采用的是easy-captcha,相关依赖如下:

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-configuration-processor</artifactId>
            <optional>true</optional>
        </dependency>

# 配置验证码

由于验证码可能我们有时候需要关闭,或者需要去切换验证码的类型,所以验证码的相关配置我们采用配置文件的方式来实现。

@Component
@ConfigurationProperties(prefix = "login.captcha")
@Data
public class CaptchaProperties {
    /**
     * 是否启用验证码
     */
    private Boolean enabled = true;
    /**
     * 验证码 key
     */
    private String codeKey;
    /**
     * 验证码配置
     */
    private LoginCodeEnum codeType;
    /**
     * 验证码有效期 分钟
     */
    private Long expiration;
    /**
     * 验证码内容长度
     */
    private int length;
    /**
     * 验证码宽度
     */
    private int width;
    /**
     * 验证码高度
     */
    private int height;
    /**
     * 验证码字体
     */
    private String fontName;
    /**
     * 字体大小
     */
    private int fontSize;
}

配置文件如下:

# 普通验证码
login:
  captcha:
      # 是否启用验证码
      enabled: true
      # 验证码 key
      code-key: code-key-
      #  验证码类型配置 查看 LoginCodeEnum 类
      code-type: arithmetic
      #  登录图形验证码有效时间/分钟
      expiration: 2
      #  验证码高度
      width: 111
      #  验证码宽度
      height: 36
      # 内容长度
      length: 4
      # 字体名称,为空则使用默认字体
      font-name:
      # 字体大小
      font-size: 25

验证码枚举类:

public enum LoginCodeEnum {
    /**
     * 算数
     */
    arithmetic,
    /**
     * 中文
     */
    chinese,
    /**
     * 中文闪图
     */
    chinese_gif,
    /**
     * 闪图
     */
    gif,
    /**
     * 英文
     */
    spec
}

到此验证码的前提工作我们就完成了,现在我们需要根据所配置的验证码类型,去生成对应类型的验证码,通过上面的配置文件,现在我们生成的验证码类型为计算型。

# 生成验证码

创建一个CaptchaUtil工具类来实现生成验证码相关操作。

@Component
public class CaptchaUtil {

    @Autowired
    private CaptchaProperties captchaProperties;

    /**
     * 获取验证码生产类
     *
     * @return /
     */
    public Captcha getCaptcha() {
        return switchCaptcha(captchaProperties);
    }

    /**
     * 依据配置信息生产验证码
     *
     * @param captchaProperties 验证码配置信息
     * @return /
     */
    private Captcha switchCaptcha(CaptchaProperties captchaProperties) {
        Captcha captcha;
        synchronized (this) {
            switch (captchaProperties.getCodeType()) {
                case arithmetic:
                    // 算术类型 https://gitee.com/whvse/EasyCaptcha
                    captcha = new FixedArithmeticCaptcha(captchaProperties.getWidth(), captchaProperties.getHeight());
                    // 几位数运算,默认是两位
                    captcha.setLen(captchaProperties.getLength());
                    break;
                case chinese:
                    captcha = new ChineseCaptcha(captchaProperties.getWidth(), captchaProperties.getHeight());
                    captcha.setLen(captchaProperties.getLength());
                    break;
                case chinese_gif:
                    captcha = new ChineseGifCaptcha(captchaProperties.getWidth(), captchaProperties.getHeight());
                    captcha.setLen(captchaProperties.getLength());
                    break;
                case gif:
                    captcha = new GifCaptcha(captchaProperties.getWidth(), captchaProperties.getHeight());
                    captcha.setLen(captchaProperties.getLength());
                    break;
                case spec:
                    captcha = new SpecCaptcha(captchaProperties.getWidth(), captchaProperties.getHeight());
                    captcha.setLen(captchaProperties.getLength());
                    break;
                default:
                    throw new BadConfigurationException("验证码配置信息错误!");
            }
        }
        if(StrUtil.isNotBlank(captchaProperties.getFontName())){
            captcha.setFont(new Font(captchaProperties.getFontName(), Font.PLAIN, captchaProperties.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();
        }
    }
}

这里最关键的就是switchCaptcha方法,这个方法通过拿到CaptchaProperties的验证码类型codeType,来生成对应类型的验证码。

BadConfigurationException参考如下:

public class BadConfigurationException extends RuntimeException {
    public BadConfigurationException() {
        super();
    }

    public BadConfigurationException(String message) {
        super(message);
    }

    public BadConfigurationException(String message, Throwable cause) {
        super(message, cause);
    }

    public BadConfigurationException(Throwable cause) {
        super(cause);
    }

    protected BadConfigurationException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
        super(message, cause, enableSuppression, writableStackTrace);
    }
}

# 获取验证码

现在前端就需要通过请求,来获取验证码,这里为用户的体验,我们需要把验证码的value存放到Redis中,校验的时候再从Redis中获取即可。

    @ApiOperation("获取验证码")
    @RequestMapping(value = "/code", method = RequestMethod.GET)
    public Result getCode() {
        if (!captchaProperties.getEnabled()) {
            // 验证码信息
            Map<String, Object> imgResult = new HashMap<String, Object>(2) {{
                put("enabled", 0);
            }};
            return Result.success(imgResult);
        }
        // 获取运算的结果
        Captcha captcha = captchaUtil.getCaptcha();
        String uuid = captchaProperties.getCodeKey() + IdUtil.simpleUUID();
        // 当验证码类型为 arithmetic时且长度 >= 2 时,captcha.text()的结果有几率为浮点型
        String captchaValue = captcha.text();
        if (captcha.getCharType() - 1 == LoginCodeEnum.arithmetic.ordinal() && captchaValue.contains(".")) {
            captchaValue = captchaValue.split("\\.")[0];
        }
        // 保存
        redisUtil.set(uuid, captchaValue, captchaProperties.getExpiration(), TimeUnit.MINUTES);
        // 验证码信息
        Map<String, Object> imgResult = new HashMap<String, Object>(2) {{
            put("enabled", 1);
            put("img", captcha.toBase64());
            put("uuid", uuid);
        }};
        return Result.success(imgResult);
    }

# 校验验证码

代码参考如下:

        // 开启验证码
        if(captchaProperties.getEnabled()) {
            // 查询验证码
            String code = (String) redisUtil.get(userDto.getUuid());
            // 清除验证码
            redisUtil.del(userDto.getUuid());
            // 拿到是空,证明redis缓存已经过期
            if (StrUtil.isBlank(code)) {
                throw new BadRequestException("验证码不存在或已过期");
            }
            if (StrUtil.isBlank(userDto.getCode()) || !userDto.getCode().equalsIgnoreCase(code)) {
                throw new BadRequestException("验证码错误");
            }
        }

BadRequestException参考如下:

public class BadRequestException extends RuntimeException{

    public BadRequestException(String msg){
        super(msg);
    }

}

UserDto参考如下:

@Data
public class UserDto {
    @NotBlank
    private String username;

    @NotBlank
    private String password;

    private String code;

    private String uuid = "";
}

# 滑块验证码实现

滑块验证码采用AJ-Captcha (opens new window)

# 添加依赖

<dependency>
   <groupId>com.anji-plus</groupId>
   <artifactId>spring-boot-starter-captcha</artifactId>
   <version>1.3.0</version>
</dependency>

# 配置文件

# 行为验证码
aj:
  captcha:
    aes-status: true
    cache-type: local
    font-size: 25
    font-style: 1
    history-data-clear-enable: false
    interference-options: 1
    req-check-minute-limit: 60
    req-frequency-limit-enable: false
    req-get-lock-limit: 5
    req-get-lock-seconds: 360
    req-get-minute-limit: 30
    req-verify-minute-limit: 60
    slip-offset: 5
    type: default
    water-mark: 旭日
    jigsaw: classpath:images/jigsaw
    pic-click: classpath:images/pic-click

# 相关接口

image-20211223183205516

具体参数去参考官网文档。

# 前端实现

具体实现请参考官网文档。

# 参考

https://captcha.anji-plus.com/#/doc

https://el-admin.vip/

上次更新: 2024/06/29, 15:13:44
数据导入
MyBatisPlus

← 数据导入 MyBatisPlus→

最近更新
01
基础概念
10-31
02
Pytorch
10-30
03
Numpy
10-30
更多文章>
Theme by Vdoing | Copyright © 2021-2024 旭日 | 蜀ICP备2021000788号-1
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式