【Spring Boot组件集成实战】集成Kaptcha谷歌验证码

2023-05-16

更多精彩内容,请访问 Spring Boot组件集成实战专栏 !

推荐项目:一套基于Spring Boot+Layui的内容管理系统/快速开发脚手架(含完整的开发文档、演示网址等)

文章目录

    • 1. 验证码的作用
    • 2. Spring Boot集成Kaptcha
      • 2.1 引入依赖
      • 2.2 配置Kaptcha配置类
      • 2.3 实现验证码服务层接口
    • 3. 验证码的生成与使用
    • 4. 本文源码下载

1. 验证码的作用

一个技术的出现,必然有它的道理。

关于验证码的定义,维基百科是这样解释的:

全自动区分计算机和人类的公开(英语:Completely Automated Public Turing test to tell Computers and Humans Apart,简称CAPTCHA),又称验证码,是一种区分用户是机器人类的公共全自动程序。

所以,通过其定义,即可得出其主要功能是帮助计算机判断当前的用户是机器还是人类,从而防止有人利用计算机程序对网站进行一些破坏性操作,比如留言板大量张贴广告等。

目前常用的验证码主要有图片验证码声音验证码滑块验证码等。

本文主要基于Kaptcha组件,实现两种常用的图片验证码:文本验证码算式验证码

captcha02

captcha01

2. Spring Boot集成Kaptcha

注意:请详细阅读代码的注释!建议结合文末的本文源码阅读~

2.1 引入依赖

1、新建一个Spring Boot项目,结构如下图所示。

image-20211022202542811

2、在pom.xml中,引入kaptchahutool依赖,如下。

<!-- hutool工具类 -->
<dependency>
    <groupId>cn.hutool</groupId>
    <artifactId>hutool-all</artifactId>
    <version>5.7.12</version>
</dependency>
<!-- 图片验证码 -->
<dependency>
    <groupId>com.github.penggle</groupId>
    <artifactId>kaptcha</artifactId>
    <version>2.3.2</version>
</dependency>

这里为什么也引入hutool组件?

因为hutool组件也集成了验证码功能,所以这个项目中一并进行演示。

2.2 配置Kaptcha配置类

上图中我们已经给出了项目的架构,com.cxhit.captcha 包下,包含configcontrollerentityserviceutils 5个包。

3、在utils包下,新建一个名为MyCaptchaUtiljava class ,是一个工具类。

该类中的方法主要是生成数学类型的文本算式验证码,如1+1=?类型的验证码。

该工具类的完整源码如下。

package com.cxhit.captcha.utils;

import java.security.SecureRandom;
import java.util.HashMap;
import java.util.Map;
import java.util.Random;

/**
 * 自定义的验证码工具类
 *
 * @author 拾年之璐
 * @since 2021-10-04 0004 20:54
 */
public class MyCaptchaUtil {

    /**
     * 生成数学文本算式
     *
     * @param a 数字
     * @param b 数字
     * @return 字符
     */
    public static Map<String, String> mathTextCreator(int a, int b) {
        Random random = new SecureRandom();
        // 生成随机操作,操作范围:[0,4),分别表示: +  -  *  /
        int op = random.nextInt(4);
        // 定义计算的结果
        Integer result = 0;
        // 定义构建的算式字符串
        StringBuilder resultString = new StringBuilder();
        // 运算符:-
        if (1 == op) {
            if (a >= b) {
                result = a - b;
                resultString.append(a).append("-").append(b).append("=?@").append(result);
            } else {
                result = b - a;
                resultString.append(b).append("-").append(a).append("=?@").append(result);
            }
        }
        // 运算符:*
        else if (2 == op) {
            result = a * b;
            resultString.append(a).append("*").append(b).append("=?@").append(result);
        }
        // 运算符:/
        else if (3 == op) {
            if (a != 0 && b % a == 0) {
                result = b / a;
                resultString.append(b).append("/").append(a).append("=?@").append(result);
            } else if (b != 0 && a % b == 0) {
                result = a / b;
                resultString.append(a).append("/").append(b).append("=?@").append(result);
            } else {
                return mathTextCreator(a, b);
            }
        }
        // 运算符:+
        else {
            result = b + a;
            resultString.append(a).append("+").append(b).append("=?@").append(result);
        }
        Map<String, String> ret = new HashMap<String, String>();
        ret.put("resultCode", result.toString());
        ret.put("resultString", resultString.toString());
        return ret;
    }
}

4、在config包下,新建两个类:KaptchaMathOneTextCreatorKaptchaMathTwoTextCreator,并均继承自DefaultTextCreator

这两个方法的主要作用是生成一位数两位数加减乘除算式验证码。

KaptchaMathOneTextCreator.java 源码如下。

package com.cxhit.captcha.config;

import com.cxhit.captcha.utils.MyCaptchaUtil;
import com.google.code.kaptcha.text.impl.DefaultTextCreator;

import java.security.SecureRandom;
import java.util.Map;
import java.util.Random;

/**
 * 验证码随机文本生成器之:一位数的加减乘除
 *
 * @author 拾年之璐
 * @since 2021-10-04 0004 20:24
 */
public class KaptchaMathOneTextCreator extends DefaultTextCreator {

    @Override
    public String getText() {
        Random random = new SecureRandom();
        // 生成两个随机数,随机数范围:[0,10),并返回结果
        Map<String, String> result = MyCaptchaUtil.mathTextCreator(random.nextInt(10), random.nextInt(10));
        return result.get("resultString");
    }
}

KaptchaMathTwoTextCreator.java 源码如下。

package com.cxhit.captcha.config;

import com.cxhit.captcha.utils.MyCaptchaUtil;
import com.google.code.kaptcha.text.impl.DefaultTextCreator;

import java.security.SecureRandom;
import java.util.Map;
import java.util.Random;

/**
 * 验证码随机文本生成器之:两位数的加减乘除
 *
 * @author 拾年之璐
 * @since 2021-10-04 0004 20:26
 */
public class KaptchaMathTwoTextCreator extends DefaultTextCreator {

    @Override
    public String getText() {
        Random random = new SecureRandom();
        // 保存计算结果
        Map<String, String> result = MyCaptchaUtil.mathTextCreator(random.nextInt(100), random.nextInt(100));
        // 生成两个随机数,随机数范围:[0,100),并返回结果
        return result.get("resultString");
    }
}

5、在config包下,新建名为KaptchaConfigjava class,写入如下配置信息,配置的详情见代码注释。

package com.cxhit.captcha.config;

import com.google.code.kaptcha.impl.DefaultKaptcha;
import com.google.code.kaptcha.util.Config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.Properties;

import static com.google.code.kaptcha.Constants.*;

/**
 * Google验证码配置类
 *
 * @author 拾年之璐
 * @since 2021-10-04 0004 20:12
 */
@Configuration
public class KaptchaConfig {

    /**
     * 验证码配置默认配置
     *
     * @return 配置信息
     */
    @Bean(name = "captchaProducer")
    public DefaultKaptcha getKaptchaBean() {
        DefaultKaptcha defaultKaptcha = new DefaultKaptcha();
        Properties properties = new Properties();
        // 是否有边框。默认true,可选:yes,no
        properties.setProperty(KAPTCHA_BORDER, "yes");
        // 验证码文本字符颜色。默认Color.BLACK
        properties.setProperty(KAPTCHA_TEXTPRODUCER_FONT_COLOR, "black");
        // 验证码图片宽度。默认200
        properties.setProperty(KAPTCHA_IMAGE_WIDTH, "160");
        // 验证码图片高度。默认50
        properties.setProperty(KAPTCHA_IMAGE_HEIGHT, "60");
        // 验证码文本字符大小。默认40
        properties.setProperty(KAPTCHA_TEXTPRODUCER_FONT_SIZE, "38");
        // KAPTCHA_SESSION_KEY
        properties.setProperty(KAPTCHA_SESSION_CONFIG_KEY, "kaptchaCode");
        // 验证码文本字符长度。默认5
        properties.setProperty(KAPTCHA_TEXTPRODUCER_CHAR_LENGTH, "4");
        // 验证码文本字体样式。默认:new Font("Arial", 1, fontSize), new Font("Courier", 1, fontSize)
        properties.setProperty(KAPTCHA_TEXTPRODUCER_FONT_NAMES, "Arial,Courier");
        // 图片样式。
        // 水纹:com.google.code.kaptcha.impl.WaterRipple
        // 鱼眼:com.google.code.kaptcha.impl.FishEyeGimpy
        // 阴影:com.google.code.kaptcha.impl.ShadowGimpy
        properties.setProperty(KAPTCHA_OBSCURIFICATOR_IMPL, "com.google.code.kaptcha.impl.ShadowGimpy");
        Config config = new Config(properties);
        defaultKaptcha.setConfig(config);
        return defaultKaptcha;
    }

    /**
     * 验证码数学题类配置(一位数加减乘除)
     *
     * @return 配置信息
     */
    @Bean(name = "captchaProducerMathOne")
    public DefaultKaptcha getKaptchaBeanMathOne() {
        DefaultKaptcha defaultKaptcha = new DefaultKaptcha();
        Properties properties = commonConfig("com.cxhit.captcha.config.KaptchaMathOneTextCreator");
        Config config = new Config(properties);
        defaultKaptcha.setConfig(config);
        return defaultKaptcha;
    }

    /**
     * 验证码数学题类配置(两位数的加减乘除)
     *
     * @return 配置信息
     */
    @Bean(name = "captchaProducerMathTwo")
    public DefaultKaptcha getKaptchaBeanMathTwo() {
        DefaultKaptcha defaultKaptcha = new DefaultKaptcha();
        Properties properties = commonConfig("com.cxhit.captcha.config.KaptchaMathTwoTextCreator");
        Config config = new Config(properties);
        defaultKaptcha.setConfig(config);
        return defaultKaptcha;
    }

    /**
     * 算式运算配置的公共配置类
     *
     * @param textImpl 验证码文本生成器
     * @return 配置类
     */
    protected static Properties commonConfig(String textImpl) {
        Properties properties = new Properties();
        // 是否有边框。默认为true,可设置:yes,no
        properties.setProperty(KAPTCHA_BORDER, "yes");
        // 边框颜色。默认:Color.BLACK
        properties.setProperty(KAPTCHA_BORDER_COLOR, "105,179,90");
        // 验证码文本字符颜色。默认:Color.BLACK
        properties.setProperty(KAPTCHA_TEXTPRODUCER_FONT_COLOR, "blue");
        // 验证码图片宽度。默认:200
        properties.setProperty(KAPTCHA_IMAGE_WIDTH, "160");
        // 验证码图片高度。默认:50
        properties.setProperty(KAPTCHA_IMAGE_HEIGHT, "60");
        // 验证码文本字符大小。默认:40
        properties.setProperty(KAPTCHA_TEXTPRODUCER_FONT_SIZE, "35");
        // KAPTCHA_SESSION_KEY
        properties.setProperty(KAPTCHA_SESSION_CONFIG_KEY, "kaptchaCodeMath");
        // 验证码文本生成器
        properties.setProperty(KAPTCHA_TEXTPRODUCER_IMPL, textImpl);
        // 验证码文本字符间距。默认:2
        properties.setProperty(KAPTCHA_TEXTPRODUCER_CHAR_SPACE, "3");
        // 验证码文本字符长度。默认:5
        properties.setProperty(KAPTCHA_TEXTPRODUCER_CHAR_LENGTH, "6");
        // 验证码文本字体样式。默认:new Font("Arial", 1, fontSize), new Font("Courier", 1, fontSize)
        properties.setProperty(KAPTCHA_TEXTPRODUCER_FONT_NAMES, "Arial,Courier");
        // 验证码噪点颜色。默认:Color.BLACK
        properties.setProperty(KAPTCHA_NOISE_COLOR, "white");
        // 干扰实现类
        properties.setProperty(KAPTCHA_NOISE_IMPL, "com.google.code.kaptcha.impl.NoNoise");
        // 图片样式。
        // 水纹:com.google.code.kaptcha.impl.WaterRipple
        // 鱼眼:com.google.code.kaptcha.impl.FishEyeGimpy
        // 阴影:com.google.code.kaptcha.impl.ShadowGimpy
        properties.setProperty(KAPTCHA_OBSCURIFICATOR_IMPL, "com.google.code.kaptcha.impl.ShadowGimpy");
        // 返回生成的配置类
        return properties;
    }
}

该配置文件主要是配置了三种类型的验证码生成器:

  • 简单文本验证码
  • 一位数加减乘除算式验证码
  • 两位数加减乘除算式验证码

至此,完成的配置文件如下图所示。

image-20211022203604802

2.3 实现验证码服务层接口

6、在entity包下,新建名为CaptchaDomain实体,用来进行验证码的数据传输。

其详细源码如下所示。

package com.cxhit.captcha.entity;

import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonInclude;

import java.awt.image.BufferedImage;
import java.io.Serializable;

/**
 * 第三方验证码实体类
 *
 * @author 拾年之璐
 * @since 2021-10-04 0004 20:02
 */
@JsonInclude(JsonInclude.Include.NON_NULL)
public class CaptchaDomain implements Serializable {

    private static final long serialVersionUID = 1L;

    /**
     * 验证码的Token
     */
    private String token;

    /**
     * 验证码的字符。返回的JSON,禁止返回给前端。
     */
    @JsonIgnore
    private String text;

    /**
     * 验证码的验证字符。比如算式的结果等。
     */
    @JsonIgnore
    private String code;

    /**
     * 验证码缓冲图像
     */
    @JsonIgnore
    private BufferedImage image;

    /**
     * 验证码图片的Base64字符串
     */
    private String base64;

    public String getToken() {
        return token;
    }

    public void setToken(String token) {
        this.token = token;
    }

    public String getText() {
        return text;
    }

    public void setText(String text) {
        this.text = text;
    }

    public String getCode() {
        return code;
    }

    public void setCode(String code) {
        this.code = code;
    }

    public BufferedImage getImage() {
        return image;
    }

    public void setImage(BufferedImage image) {
        this.image = image;
    }

    public String getBase64() {
        return base64;
    }

    public void setBase64(String base64) {
        this.base64 = base64;
    }

    @Override
    public String toString() {
        return "CaptchaDomain{" +
                "token='" + token + '\'' +
                ", text='" + text + '\'' +
                ", code='" + code + '\'' +
                ", image=" + image +
                ", base64='" + base64 + '\'' +
                '}';
    }
}

7、在service包下,新建名为ICaptchaService的服务层接口。详细源码如下。

package com.cxhit.captcha.service;

import com.cxhit.captcha.entity.CaptchaDomain;

/**
 * <p>
 * 验证码服务接口
 * </p>
 *
 * @author 拾年之璐
 * @since 2021-10-06 0006 19:47
 */
public interface ICaptchaService {

    /**
     * Kaptcha生成验证码实体的服务层接口
     *
     * @param type 类型,char - 字符(缺省) | math - 一位数算式 | math2 - 两位数算式
     * @return 验证码实体
     */
    public CaptchaDomain createGoogleCaptcha(String type);


    /**
     * Hutool生成验证码实体的服务层接口
     *
     * @param width  验证码图片的宽度
     * @param height 验证码图片的高度
     * @return 验证码实体
     */
    public CaptchaDomain createHutoolCaptcha(Integer width, Integer height);
}

只有在服务层接口中,我们才集成了hutool的验证码功能。所以说,hutool的验证码生成似乎更简单。

8、在service.impl包下,新建名为ICaptchaServiceImpl的类,并实现ICaptchaService的接口。详细源码如下。

package com.cxhit.captcha.service.impl;

import cn.hutool.captcha.AbstractCaptcha;
import cn.hutool.captcha.CaptchaUtil;
import com.cxhit.captcha.entity.CaptchaDomain;
import com.cxhit.captcha.service.ICaptchaService;
import com.google.code.kaptcha.Producer;
import org.springframework.stereotype.Service;
import sun.misc.BASE64Encoder;

import javax.annotation.Resource;
import javax.imageio.ImageIO;
import java.io.ByteArrayOutputStream;
import java.security.SecureRandom;
import java.util.Random;
import java.util.UUID;

/**
 * 验证码服务接口的实现
 *
 * @author 拾年之璐
 * @since 2021-10-06 0006 19:47
 */
@Service
public class ICaptchaServiceImpl implements ICaptchaService {

    @Resource(name = "captchaProducer")
    private Producer captchaProducer;

    @Resource(name = "captchaProducerMathOne")
    private Producer captchaProducerMathOne;

    @Resource(name = "captchaProducerMathTwo")
    private Producer captchaProducerMathTwo;

    private static final String TYPE_CHAR = "char";
    private static final String TYPE_MATH_ONE = "math";
    private static final String TYPE_MATH_TWO = "math2";

    /**
     * Kaptcha生成验证码实体
     *
     * @param type 类型,char - 字符(缺省) | math - 一位数算式 | math2 - 两位数算式
     * @return 验证码实体
     */
    @Override
    public CaptchaDomain createGoogleCaptcha(String type) {
        // 定义验证码实体
        CaptchaDomain captchaDomain = new CaptchaDomain();
        // 一位数加减乘除
        if (TYPE_MATH_ONE.equals(type)) {
            // 生成文本
            String producerText = captchaProducerMathOne.createText();
            // 设置验证码字符
            captchaDomain.setText(producerText.substring(0, producerText.indexOf("@")));
            // 设置验证码答案码
            captchaDomain.setCode(producerText.substring(producerText.indexOf("@") + 1));
            // 设置验证码图片
            captchaDomain.setImage(captchaProducerMathOne.createImage(captchaDomain.getText()));
        }
        // 两位数加减乘除
        else if (TYPE_MATH_TWO.equals(type)) {
            String producerText = captchaProducerMathTwo.createText();
            captchaDomain.setText(producerText.substring(0, producerText.indexOf("@")));
            captchaDomain.setCode(producerText.substring(producerText.indexOf("@") + 1));
            captchaDomain.setImage(captchaProducerMathTwo.createImage(captchaDomain.getText()));
        }
        // 缺省情况:字符
        else {
            captchaDomain.setText(captchaProducer.createText());
            captchaDomain.setCode(captchaDomain.getText());
            captchaDomain.setImage(captchaProducer.createImage(captchaDomain.getText()));
        }
        // 生成base64
        try {
            // 定义字节数组输出流
            ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
            // 将图像以 jpg 的形式,写到字节数组输出流中
            ImageIO.write(captchaDomain.getImage(), "jpg", outputStream);
            // 对字节数组Base64编码
            BASE64Encoder encoder = new BASE64Encoder();
            // 写入base64格式
            captchaDomain.setBase64("data:image/jpg;base64," + encoder.encode(outputStream.toByteArray()));
            // 写入唯一Token
            captchaDomain.setToken(UUID.randomUUID().toString());
            // 返回结果
            return captchaDomain;
        } catch (Exception e) {
            System.out.println(e.getMessage());
            return null;
        }
    }

    @Override
    public CaptchaDomain createHutoolCaptcha(Integer width, Integer height) {
        CaptchaDomain captchaDomain = new CaptchaDomain();
        // 生成 0、1、2 三个整数其一,随机对应下面的三种验证码类型
        // 注意:这里只是尽可能多的展示每种验证码的使用方法,实际项目中选择一种即可。
        Random random = new SecureRandom();
        int type = random.nextInt(3);
        AbstractCaptcha captcha = null;
        // 【0】生成 线段干扰验证码
        if (type == 0) {
            //定义图形验证码的长和宽
            captcha = CaptchaUtil.createLineCaptcha(width, height);
        }
        // 【1】生成 圆圈干扰验证码
        else if (type == 1) {
            //定义图形验证码的长、宽。还可以设置两个参数:验证码字符数、干扰元素个数
            captcha = CaptchaUtil.createCircleCaptcha(width, height);
        }
        // 【2】生成 扭曲干扰验证码
        else {
            captcha = CaptchaUtil.createShearCaptcha(width, height);
        }
        // 信息配置
        captchaDomain.setText(captcha.getCode());
        captchaDomain.setCode(captcha.getCode());
        captchaDomain.setBase64(captcha.getImageBase64Data());
        captchaDomain.setImage(captcha.getImage());
        captchaDomain.setToken(UUID.randomUUID().toString());

        return captchaDomain;
    }
}

至此,我们已经在项目中完成了验证码的集成工作。

截止当前,完成的文件如下图所示。

image-20211022204002543

3. 验证码的生成与使用

我们先分析一下验证码在不同框架中的使用流程。

  • 前后端不分离:

这种框架下,可以使用sessionRedisMySQL等存储验证码。

每个会话的属性等配置信息,均以Session存储在服务端内存中,可以使用Session ID(会话的Key)对访问用户的身份进行判断和区分。

  • 前后端分离:

这种框架下,一般使用RedisMySQL等存储验证码,使用Token+验证码的形式,来对访问用户身份进行判断和区分。

其实现方案就是后端生成验证码的同时,为该验证码生成一个唯一的Token。将验证码图片和Token返回给前端,验证码答案和Token存储在数据库中,并设置过期时间。前端用户提交验证码的同时,也将Token一起提交给后端,Token负责架起用户输入的验证码和正确验证码之间的桥梁。

所以,在接下来的控制类接口中,我们将模拟这两种场景。

代码的逻辑,请见代码注释!

CaptchaController的完整源码如下。

package com.cxhit.captcha.controller;


import com.cxhit.captcha.entity.CaptchaDomain;
import com.cxhit.captcha.service.ICaptchaService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;

import javax.annotation.Resource;
import javax.imageio.ImageIO;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
import java.security.SecureRandom;
import java.util.Random;

/**
 * 生成验证码接口
 *
 * @author 拾年之璐
 * @since 2021-10-04 0004 21:16
 */
@Controller
@RequestMapping("/")
public class CaptchaController {

    @Autowired
    private ICaptchaService captchaService;

    @Resource
    protected HttpServletRequest request;
    @Resource
    protected HttpServletResponse response;

    /**
     * 测试的主页
     *
     * @return 访问 localhost:9003 ,即可访问主页
     */
    @GetMapping("")
    @ResponseBody
    public String index() {
        return "<html>\n" +
                "\n" +
                "<head>\n" +
                "<title>验证码生成</title>\n" +
                "</head>\n" +
                "\n" +
                "<body>\n" +
                "<p>获取验证码实体:<a href=\"/captcha/get\" target=_blank>localhost:9003/captcha/get</a> (链接缺省type参数,默认生成文本验证码)<br></p>\n" +
                "<p>获取验证码实体:<a href=\"/captcha/get?type=math\" target=_blank>localhost:9003/captcha/get?type=math</a><br></p>\n" +
                "<p>获取验证码实体:<a href=\"/captcha/get?type=math2\" target=_blank>localhost:9003/captcha/get?type=math2</a><br></p>\n" +
                "<p>获取验证码图片:<a href=\"/captcha/get/image?type=math\" target=_blank>localhost:9003/captcha/get/image?type=math</a> (此接口包含hutool验证码的测试)<br></p>\n" +
                "<p>链接末尾可加的type参数有:char、math、math2" +
                "</body>\n" +
                "\n" +
                "</html>\n";
    }

    /**
     * 获取验证码接口
     *
     * @param type 类型,char - 字符(默认) | math - 一位数算式 | math2 - 两位数算式
     * @return 返回验证码Token+验证码图片的Base64值
     */
    @GetMapping("/captcha/get")
    @ResponseBody
    public CaptchaDomain getCaptcha(@RequestParam(value = "type", required = false, defaultValue = "char") String type) {
        // 生成验证码实体
        CaptchaDomain captchaDomain = captchaService.createGoogleCaptcha(type);
        if (null != captchaDomain) {
            // 将验证码保存至redis
            // redisUtils.set(captchaDomain.getToken(), captchaDomain.getCode(), 300L);
            // 判断验证码正确
            // if (null != redisUtils.get(captchaToken) && redisUtils.get(captchaToken).toString().equals(captcha)) {
            //     System.out.println("验证码正确!继续执行验证码正确后的操作");
            // }
            // 打印测试
            System.out.println("Token:" + captchaDomain.getToken() + "\t验证码:" + captchaDomain.getCode());
            // 无用信息设空
            captchaDomain.setText(null);
            captchaDomain.setCode(null);
            // 返回前端信息
            return captchaDomain;
        } else {
            return null;
        }

    }

    /**
     * 直接输出图片
     *
     * @param type 类型,char - 字符(默认) | math - 一位数算式 | math2 - 两位数算式
     */
    @GetMapping("/captcha/get/image")
    public void getCaptchaImage(@RequestParam(value = "type", required = false, defaultValue = "char") String type) {
        CaptchaDomain captchaDomain = null;
        // 此处生成0或1的随机数,以随机测试谷歌验证码和hutool验证码
        Random random = new SecureRandom();
        int rand = random.nextInt(2);
        if (rand == 0) {
            // 生成谷歌验证码实体
            captchaDomain = captchaService.createGoogleCaptcha(type);
        } else {
            // 生成hutool验证码实体
            captchaDomain = captchaService.createHutoolCaptcha(160, 60);
        }
        // 打印验证码
        System.out.println("Token:" + captchaDomain.getToken() + "\t验证码:" + captchaDomain.getCode());
        // 保存至session
        HttpSession session = request.getSession();
        session.setAttribute(captchaDomain.getToken(), captchaDomain.getCode());
        // 从session中读取测试测试
        System.out.println("根据Token:" + captchaDomain.getToken() + ",从session中读取验证码:" + session.getAttribute(captchaDomain.getToken()));
        // 以文件流的形式,输出验证码图片
        ServletOutputStream out = null;
        try {
            response.setContentType("image/jpeg");
            out = response.getOutputStream();
            ImageIO.write(captchaDomain.getImage(), "jpg", out);
            out.flush();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                if (out != null) {
                    out.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }

        }
    }

}

启动项目,访问 localhost:9003 (这里的端口在application.yml里配置),如下图所示。

image-20211022210819894

访问前三个链接,返回的数据格式如下图所示。

image-20211022211015324

base64的值,复制到浏览器新建标签页中,即可查看生成的验证码图片。如下图所示。

image-20211022211128654

主要前后不要加引号。

后台可以看到打印的测试内容。如下图所示。

image-20211022211255055

直接访问第四个链接,可以直接生成图片验证码,如下图所示。

image-20211022211531427

后台同样可以看到打印的测试内容,如下图所示。

至此,关于验证码的全部功能,测试完毕。

4. 本文源码下载

在这里插入图片描述

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

【Spring Boot组件集成实战】集成Kaptcha谷歌验证码 的相关文章

  • debian9的dns文件默认为resolv.conf

    debian9的dns文件默认为resolv conf sudo vim etc resolv conf nameserver 114 114 114 114 nameserver 8 8 8 8 这只能暂时性的修改DNS 下次系统重启后
  • 驅動Intel無線網卡(IPW2100/IPW2200)

    分类 xff1a LINUX 2007 09 05 12 38 21 驅動 Intel 無線網卡 IPW2100 IPW2200 目前 DFB 預設並沒有安裝 wireless tools xff0c 所以請手動安裝 apt get ins
  • 解决 debian TAB 键不能自动补全命令的原因

    weixin 33928137 2015 04 23 10 46 00 512 收藏 文章标签 xff1a 操作系统 版权 预约直播 xff1a 9月9日 12日 xff0c 携手众开源社区 xff0c 开发者们的年度盛会 开源大咖与开发者
  • 为debian8.2更换官方源

    最近 xff0c 配置一个韩国vps xff0c 里面用的是163的源 xff0c 感觉不如官方的好用 xff0c 就改为官方源 地址为 xff1a ftp cn debian org 输入命令 xff1a vi etc apt sourc
  • debian装好了。之后开始js的旅程了。~

    xff5e
  • 2021-08-28

    卸载无用依赖 Ubuntu卸载软件的几种方法 xff0c 你会用哪种 xff1f 九乡河龙牙 2021 01 12 07 48 13 306 收藏 1 文章标签 xff1a 卸载无用依赖 版权 9月11日 xff0c 腾讯Techo Hub
  • Debian中apache服务,htts,认证网站

    网络技能大赛A模块第一套 xff0c 涉及到apache的配置 xff0c 认证网站 加密https网站 debian中apache配置和Centos有点不太一样 xff0c 各类配置放在子配置文件中 5 Webserver apache
  • 使用Apache转发,解决jQuery的跨域问题!

    一 下载Apache 登录Apache官网 http httpd apache org 点击 Download xff08 我下载的是最新的版本 xff09 下载Windows版本 选择下载平台 ApacheHaus 选择下载相应的32或者
  • 我在这里面写学习程序的博客了

    我在这里面写学习程序的博客了
  • 第一次参加技术类的活动应该还是在十年前

    第一次参加技术类的活动应该还是在十年前 xff0c 当时应该是参加LINUX的一个技术类的活动 具体情况想不起来了 xff0c 地点应该是在中关村上地那个地方的一个什么楼里面 xff0c 当时记的好荒凉的地方 xff0c 没有什么树木 xf
  • 提问

    程序员日记吗 xff1f 我去写日记 xff0c 说着说着 xff0c 晚上吃了个火锅 然后正事没办 就算是什么也不学 xff0c 也要写日记啊 先去提问 什么是程序 xff1f 什么是语言 xff1f 程序是怎么运行的 xff1f 程序和
  • 我遭报应了?游戏过度之后的反弹反应 其实呢?

    我遭报应了 xff1f 过度游戏的之后反应反弹 其实呢 xff1f 我队最近只要沾电子产品就会起不舒服的反应 手机放在裤兜里面 xff0c 皮肤就会疼 之前在香山住的时候 xff0c 旁边有人用电脑 xff0c 之后睡醒死就像一样了 只有在
  • php是啥

    php是啥 有没有技术树 它们的因果关系是什么 xff1f 尝试着写一下 xff1f php的基本格式是什么 PHP的环境怎么装 第一个PHP的程序怎么写 PHP的组成部分有什么 差不多就是这样的问题了吧
  • ubuntu php 乱码解决,为何访问ubuntu的apache服务器下的php文件出现乱码

    这不是 apache 的问题 是 php 本身编码 xff0c 或者 数据库编码问题 给你看一篇别人的问题 让人烦恼的 PHP 43 UTF8 乱码解决方案 088月2009 一般来说 xff0c 如果将 各个文件类型 xff0c HTML
  • easyexcel读取合并单元格

    easyexcel读取合并单元格 文章目录 easyexcel读取合并单元格一 设置读取额外信息二 重写Listener中的extra 方法 xff0c 获取合并单元格的信息三 遍历合并单元格的信息四 代码清单1 UploadDataLis
  • 【Debian 10】win10 远程连接 Debian 10

    1 查询虚拟机的IP地址 使用ifconfig 查询虚拟机的IP地址 xff1a 2 出错问题 直接连接会报错 xff1a 首先需要排除一下网络原因 xff1a Debian需要安装对应的软件才能远程连接 xff1a 3 成功连接上 安装完
  • C/C++ 中typedef关键字

    文章目录 C C 43 43 中typedef关键字1 简介2 1 常规变量类型定义2 2 指针类型定义2 3 结构体定义2 4 数组类型定义2 5 函数定义2 5 1 函数声明2 5 2 函数指针 C C 43 43 中typedef关键
  • 解决“Failed to start bean ‘documentationPluginsBootstrapper‘; nested exception is java.lang.NullPoint”

    当你的spring boot版本是2 6 x并且你的swagger版本是3 0 0以上的时候 xff0c 项目启动会报错 org springframework context ApplicationContextException Fai
  • 3.汇编指令:【寻址方式】立即数寻址、寄存器寻址、存储器寻址

    文章目录 指令格式指令中的 xff08 目标 源 xff09 操作数来源一 立即数寻址二 寄存器寻址三 存储器寻址3 1 直接寻址3 2 寄存器间接寻址3 3 基址寻址 xff08 寄存器相对寻址 xff1f xff09 3 4 变址寻址
  • 51单片机定时器/计数器

    一 课前须知 xff1a 1 51单片机有两组定时器 计数器 xff0c 因为既可以定时 xff0c 也可以计数 xff0c 所以称之为定时器 计数器 2 定时器 计数器和单片机CPU是相互独立的 定时器 计数器的工作过程是自动完成的 xf

随机推荐

  • matlab中矩阵某列最大值,MATLAB怎么取出矩阵每列中最大的数

    你说的列到底是指什么 xff1f a 61 2 3 3 6 4 9 是三行两列 xff0c a 61 2 3 3 6 4 9 如果你要得到b 61 4 9 则程序为 a 61 2 3 3 6 4 9 或者 a 61 2 3 3 6 4 9
  • debian10 安装jdk8

    下载Oracle JDK 8 在 Debian 上安装 Oracle JDK 需要从官网上下载可供安装的软件包 这里我们使用curl命令来从 Oracle 网站下载 Oracle Java 8 默认情况下curl命令工具并未在系统中安装可以
  • debian10 安装nodejs

    从Debian存储库安装Node js和npm Node js和npm可以从标准的Debian存储库安装 xff0c 在选写本文时 xff0c 存储库中的版本是v10 x xff0c 这是最新的LTS版本 要在Debian上安装Node j
  • Grafana+MySQL(3)grafana展示mysql源数据:表格展示

    背景 grafana展示mysql源数据 xff0c 且以table形式 MySQL表内数据格式如下 xff1a 表格展示 Dashboard 添加panel xff0c 右侧菜单选择 Table xff0c 添加Query xff0c 选
  • debian使用php+mysql+nginx快速搭建网站

    1 apt get update 更新插件库 2 apt get install nginx 安装nginx 3 apt get install php5 fpm php5 curl 安装php一系列拓展 xff0c 可以使用tab查看ph
  • ECS简介

    Amazon Elastic Container Service ECS 是一个有高度扩展性的容器管理服务 它可以轻松运行 停止和管理集群上的Docker容器 xff0c 你可以将容器安装在EC2 实例上 xff0c 或者使用Fargate
  • 刷爆朋友圈!程序员怒斥:凉透吧!月薪40k的Python人!

    作为一名老码农 xff0c 我的心这次凉透了 xff01 事情起因是这样的 xff1a 前天我晚上正在全国最大的同性组织某Hub上浏览时候 xff0c 发现这样的一条信息 xff1a Python 116K 超过 C 43 43 JS 薪酬
  • ABAQUS设置云图的显示

    1 单击云图选项 2 边界 指定 xff08 设定指定值 xff09 值设置的越大 xff0c 变形效果越小
  • 【Spring Boot组件集成实战】集成Druid数据库连接池和MyBatis-Plus(含代码生成器)

    更多精彩内容 xff0c 请访问 Spring Boot组件集成实战专栏 xff01 文章目录 1 MyBatis Plus产生的原因2 MyBatis Plus解决的问题3 Spring Boot集成Druid2 1 引入依赖2 2 配置
  • Nextcloud 登录后提示”服务器内部错误”

    修复日记 xff1a 公司的nextcloud重启后又崩了 202104061730修好 1 拿一个正常的nextcloud的config目录来替换成当前的config目录 2 编辑好config php文件的内容 有几个要注意的点 xff
  • Nextcloud23 内部服务器错误 4047 InnoDB refuses to write tables with ROW_FORMAT=COMPRESSED or KEY_BLOCK_SIZE

    本故障同适用 xff08 当更换容器端口重启后产生的 xff09 内部服务器错误 都是进数据库执行这段代码 xff1a SET GLOBAL innodb read only compressed 61 OFF 随后nextcloud恢复正
  • 软件工程中五种常用的软件开发模型整理

    软件工程期末考试复习资料整理 xff0c 顺便码了个博客 xff0c emmm 下面都是我对各位博主文章种我认为写的比较好的内容的截取 引言 软件将要经历一个定义 开发 运行维护 xff0c 直至被淘汰这样的生命周期 为了使软件生命周期中的
  • 默认的microsoft edge浏览器内如何打开IE浏览器(各大银行网银登陆时需要)

    大多数银行的企业网银都只支持IE浏览器登陆 xff0c 作为电脑小白 43 新电脑 xff0c 今天一直找不到IE浏览器的接入口 xff0c 好心累 写一个这个给和我类似的朋友们用 1 打开Microsoft edge 右上角 设置 xff
  • 各种常用的默认端口号 总结

    各种常用的默认端口号 总结 端口号的范围是从1 xff5e 65535 其中1 xff5e 1024是被RFC 3232规定好了的 xff0c 被称作 众所周知的端口 Well Known Ports xff1b 从1025 xff5e 6
  • 利用hdparm工具配合crontab使硬盘不用时休眠

    背景 xff1a 上次搞定了硬盘的自动挂载问题 xff0c 回头购入了个功率测试仪 xff0c 发现树莓派取消挂载移动硬盘后 xff0c 硬盘依然不能自动休眠 我用的是一个两盘位硬盘盒做RAID1 xff0c 运行两个3 5的2T硬盘功耗大
  • C++学习笔记

    一 基于过程的程序设计 1 1 概念及基础 pragma once 防止头文件重复包含 自定义的头文件用 34 34 xff0c 系统的用 lt gt 在标准输入流与输出流中使用控制符需要添加 include iomanip头文件 C 43
  • JAVA学习笔记

    第一章 IDEA基本配置和快捷键 IDEA快捷键 快捷键功能shift 43 F6选中目标内容后 xff0c 更改所有用到它的内容ctrl 43 Y删除当前行ctrl 43 D复制当前行Alt 43 Enter导入包自动修正代码Ctrl 4
  • 动态规划——装箱问题

    使用动态规划 xff0c dp i 记录当容积为i时的最大填充体积 span class token keyword import span java span class token punctuation span util span
  • 两种经典最短路径算法

    dijkstral算法 xff1a 计算单源最短路径 xff08 固定起点 xff0c 计算出起点到其他所有顶点的最短路径 xff09 用贪心思想 xff0c 每次找出距离起点最近的节点 xff0c 直到找出所有节点动态规划 xff1a 每
  • 【Spring Boot组件集成实战】集成Kaptcha谷歌验证码

    更多精彩内容 xff0c 请访问 Spring Boot组件集成实战专栏 xff01 推荐项目 xff1a 一套基于Spring Boot 43 Layui的内容管理系统 快速开发脚手架 xff08 含完整的开发文档 演示网址等 xff09