前后端分离,SpringBoot如何实现验证码操作

验证码的功能是防止非法用户恶意去访问登录接口而设置的一个功能,今天我们就来看看在前后端分离的项目中,SpringBoot是如何提供服务的。

SpringBoot版本

本文基于的Spring Boot的版本是2.6.7 。

引入依赖

captcha一款超简单的验证码生成,还挺好玩的.还有中文验证码,动态验证码. 。在项目中pom.xml配置文件中添加依赖,如下:


    com.github.whvcse
    easy-captcha
    1.6.2

实现思路

  • 把生成的验证码结果保存到redis缓存中,并设置过期时间。
  • 前端通过提交验证码和key,其中key就是保存到redis中的键,通过这个键获取到对应的值,再与前端提交的值对比,相同就通过验证。

实现过程

新建验证码枚举类

由于captcha这款验证码提供了好几种验证码方法,有中文验证码,动态验证码,算术验证码等等,新建一个验证码每周类存放这几种验证码类型。代码如下:

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

定义验证码配置信息

该类是定义验证码的基本信息,例如高度、宽度、字体类型、验证码类型等等、并且我们把它转成通过SpringBoot配置文件类型来定义更加方便。

@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;
    }
}

把配置文件转换Pojo类的统一配置类

@Configuration
public class ConfigBeanConfiguration {

    @Bean
    @ConfigurationProperties(prefix = "login")
    public LoginProperties loginProperties() {
        return new LoginProperties();
    }
}

定义验证逻辑生成类

@Data
public class LoginProperties {

    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();
        }
    }
}

在控制层上定义验证码生成接口

   @ApiOperation(value = "获取验证码", notes = "获取验证码")
    @GetMapping("/code")
    public Object getCode(){

        Captcha captcha = loginProperties.getCaptcha();
        String uuid = "code-key-"+IdUtil.simpleUUID();
        //当验证码类型为 arithmetic时且长度 >= 2 时,captcha.text()的结果有几率为浮点型
        String captchaValue = 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 imgResult = new HashMap(2){{
            put("img",captcha.toBase64());
            put("uuid",uuid);
        }};
        return imgResult;

    }

效果体验

前后端分离,SpringBoot如何实现验证码操作

在前端调用接口


    methods: {
    getCode() {
      getCodeImg().then(res => {
        this.codeUrl = res.data.img
        this.loginForm.uuid = res.data.uuid
      })
    },
    }
    created() {
    // 获取验证码
    this.getCode()
  },

前后端分离,SpringBoot如何实现验证码操作

Original: https://www.cnblogs.com/alanlin/p/16276914.html
Author: 北根娃
Title: 前后端分离,SpringBoot如何实现验证码操作

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/586832/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

  • java技术整理1

    一、基本 1.hashmap: 1.1 转红黑树条件:a.数组长度大于等于64(默认16,要经过2次扩容–当达到16*默认扩容因子0.75=12就扩容)b.链表长度大于…

    Java 2023年6月13日
    0124
  • 分布式系统中如何实现临界资源的互斥访问?

    摘要:一次仅允许一个进程使用的资源称为临界资源。 网络时代,购物、社交等之前只能在线下进行的活动,如今都可以在网络上完成。为了促进消费,电商网、网络店铺经常推出商品限定数量内的&#…

    Java 2023年6月15日
    089
  • Spring Boot 整合 MyBatis

    MyBatis 简介 MyBatis 是一款优秀的持久层框架,它支持自定义 SQL、存储过程以及高级映射。MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的…

    Java 2023年6月9日
    069
  • HTTP标头信息

    HTTP头信息头信息由”键:值”组成。它们描述客户端或者服务器的属性、被传输的资源以及应该实现连接。 四种不同类型的头标 通用头标:即可用于请求,也可用于响…

    Java 2023年6月5日
    092
  • jsonFormater之应用

    html代码: html;collapse:true;;gutter:true; .jf-PropertyName{ color: #CC0000; } .jf-String{ c…

    Java 2023年6月6日
    096
  • 【spring源码学习】@Aspect 实现切面代码的原理

    一、spring-boot框架中的应用 1、服务启动时,会加载spring-boot-autoconfigure的jar中spring.factories文件,加载服务启动自动装配…

    Java 2023年5月29日
    080
  • JAVA实现AES加密、解密

    一、什么是AES? 高级加密标准(英语:Advanced Encryption Standard,缩写:AES),是一种区块加密标准。这个标准用来替代原先的DES,已经被多方分析且…

    Java 2023年6月5日
    075
  • Java如何实现定时任务?

    我是3y,一年 CRUD经验用十年的 markdown程序员👨🏻‍💻常年被誉为优质八股文选手 挺早就规划了要引入分布式定时任务框架了,在年前austin就已经接入了,但代码过年一直…

    Java 2023年6月9日
    089
  • 三分钟入门

    网络编程(通俗易懂) tcp通信:打电话,对方接,接了,通话,互通性 udp通信:发短信,发完就完事 通信作用:无非就是传播交流信息,进行数据交换 实现网络通信: IP 端口 规则…

    Java 2023年6月6日
    073
  • leetcode 513. Find Bottom Left Tree Value 找树左下角的值 (简单)

    给定一个二叉树的 根节点 root,请找出该二叉树的 最底层 最左边 节点的值。 假设二叉树中至少有一个节点。 示例 1: 输入: root = [2,1,3]输出: 1 示例 2…

    Java 2023年6月14日
    085
  • Spring Boot 如何干掉 if else?

    需求 这里虚拟一个业务需求,让大家容易理解。假设有一个订单系统,里面的一个功能是根据订单的不同类型作出不同的处理。 订单实体: service接口: 传统实现 根据订单类型写一堆的…

    Java 2023年5月30日
    086
  • 由于 ASP.NET 进程标识对全局程序集缓存没有读权限,因此未能执行请求。错误: 0x80131902

    分类: c#2013-06-17 10:22 89人阅读 评论(0) 收藏 举报 ASP.NET 由于 ASP.NET 进程标识对全局程序集缓存没有读权限,因此未能执行请求。错误:…

    Java 2023年6月14日
    080
  • 0.前言 在学习Java高级之前的一些想说的话

    在学习Java高级之前的一些想说的话 1.将会学到什么? IO流 线程 网络编程 XML解析 设计模式 当然,真正的JAVA高级对于每个人或者每个组织的定义可能都不太一样,这里所讲…

    Java 2023年6月13日
    086
  • 190_RabbitMQ高级-死信队列

    概述 在rabbitMQ管理界面中结果 死信队列测试 概述 :::infoDLX,全称为Dead-Letter-Exchange , 可以称之为死信交换机,也有人称之为死信邮箱。当…

    Java 2023年6月7日
    0119
  • 使用JDK的同步容器时,应该避免那些坑?

    摘要:在使用JDK中的同步容器时,应该尽量避免哪些坑 同步容器与并发容器 在JDK中,总体上可以将容器分为同步容器和并发容器。 同步容器一般指的是JDK1.5版本之前的线程安全的容…

    Java 2023年5月30日
    072
  • 子程序(一个方法)中出现多个返回语句

    子程序中不建议出现过多的返回语句,尽量减少子程序返回语句的数量。 Original: https://www.cnblogs.com/txzn/p/16600520.htmlAut…

    Java 2023年6月5日
    080
亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球