SpringBoot3整合Hutool-captcha实现图形验证码

 更新时间:2024年11月28日 11:58:12   作者:Harry技术  
在整合技术框架的时候,想找一个图形验证码相关的框架,看到很多验证码的maven库不再更新了或中央仓库下载不下来,还需要多引入依赖,后面看到了Hutool图形验证码(Hutool-captcha)中对验证码的实现,所以本文介绍了SpringBoot3整合Hutool-captcha实现图形验证码

验证码需求分析:

1. 生成验证码,点击图片可进行刷新
2. 输入验证码,点击提交,验证用户输入验证码是否正确

项目创建

首先创建项目这里使用的Spring boot 3 + JDK17,并引入相关依赖

pom.xml

<properties>
        <java.version>17</java.version>

        <hutool.version>5.8.26</hutool.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-captcha</artifactId>
        </dependency>
    </dependencies>
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>cn.hutool</groupId>
                <artifactId>hutool-bom</artifactId>
                <version>${hutool.version}</version>
                <type>pom</type>
                <!-- 注意这里是import -->
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

import方式的使用说明

如果你想像Spring-Boot一样引入Hutool,再由子模块决定用到哪些模块,你可以在父模块中加入:

<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-bom</artifactId>
            <version>${hutool.version}</version>
            <type>pom</type>
            <!-- 注意这里是import -->
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>

在子模块中就可以引入自己需要的模块了:

<dependency>
    <groupId>cn.hutool</groupId>
    <artifactId>hutool-captcha</artifactId>
</dependency>

使用import的方式,只会引入hutool-bom内的dependencyManagement的配置,其它配置在这个引用方式下完全不起作用。

exclude方式

如果你引入的模块比较多,但是某几个模块没用,你可以:

<dependencies>
    <dependency>
        <groupId>cn.hutool</groupId>
        <artifactId>hutool-bom</artifactId>
        <version>${hutool.version}</version>
        <!-- 加不加这句都能跑,区别只有是否告警  -->
        <type>pom</type>
        <exclusions>
            <exclusion>
                    <groupId>cn.hutool</groupId>
                    <artifactId>hutool-system</artifactId>
            </exclusion>
        </exclusions>
    </dependency>
</dependencies>

这个配置会传递依赖hutool-bom内所有dependencies的内容,当前hutool-bom内的dependencies全部设置了version,就意味着在maven resolve的时候hutool-bom内就算存在dependencyManagement也不会产生任何作用。

定义接口:

1. 生成验证码,并返回
2. 校验验证码是否正确

接口定义

1.  生成验证码
[URL]
GET /captcha/getCaptcha
[请求参数]
[响应]
{
    "uuid": "fc2b40825f20470bb4f5d9868b33856f",
    "img": "data:image/png;base64,issaxxxxxxxxgg=="
}

uuid 是为了方便在真实项目使用中做区分
2. 校验验证码是否正确
[URL]
POST /captcha/check
[请求参数]
{"uuid":"43b4b30bf1134e9f8430507b7babd620","code":""}
[响应]
{
   true
}

根据用户输入的验证码,校验验证码是否正确,校验成功,返回true;校验失败,返回false

定义 CaptchaController

@Slf4j
@RestController
@RequestMapping("/captcha")
@AllArgsConstructor
public class CaptchaController {

    private final SysCaptchaService sysCaptchaService;

    /**
     * 生成验证码
     */
    @GetMapping("/getCaptcha")
    public CaptchaResult getCaptcha(HttpSession session) {
        return sysCaptchaService.getCaptcha(session);
    }

    /**
     * 验证码校验
     *
     * @param param 验证码参数
     */
    @PostMapping("/check")
    public boolean checkCaptcha(@RequestBody CaptchaValidateParam param, HttpSession session) {
        String uuid = param.getUuid();
        String captcha = param.getCode();
        if (StrUtil.isEmpty(uuid) || StrUtil.isEmpty(captcha)) {
            log.error("验证码参数不能为空");
            return false;
        }
        log.info("接收到验证码: uuId:{}, 验证码:{}", uuid, captcha);
        // 参数校验
        return sysCaptchaService.validate(uuid, captcha, session);
    }
}

由于当用户输入验证码时,我们需要进行校验,因此,我们需要对生成的验证码进行存储,同时,需要存储验证码的生成时间,以便判断验证码是否超时

public class Constants {
    public static final String CAPTCHA_CODE = "code";
    public static final String CAPTCHA_UUID = "uuid";
}
  • SysCaptchaService
public interface SysCaptchaService {

    /**
     * 获取验证码
     *
     * @param session
     * @return
     */
    CaptchaResult getCaptcha(HttpSession session);

    /**
     * 验证码效验
     *
     * @param uuid
     * @param code
     * @param session
     * @return
     */
    boolean validate(String uuid, String code, HttpSession session);
}

  • SysCaptchaServiceImpl
@Service
@RequiredArgsConstructor
@Slf4j
public class SysCaptchaServiceImpl implements SysCaptchaService {

    private final CaptchaProperties captchaProperties;
    private final CodeGenerator codeGenerator;
    private final Font captchaFont;


    @Override
    public CaptchaResult getCaptcha(HttpSession session) {


        String captchaType = captchaProperties.getType();
        int width = captchaProperties.getWidth();
        int height = captchaProperties.getHeight();
        int interfereCount = captchaProperties.getInterfereCount();
        int codeLength = captchaProperties.getCode().getLength();

        AbstractCaptcha captcha;
        if (CaptchaTypeEnums.CIRCLE.name().equalsIgnoreCase(captchaType)) {
            captcha = CaptchaUtil.createCircleCaptcha(width, height, codeLength, interfereCount);
        } else if (CaptchaTypeEnums.GIF.name().equalsIgnoreCase(captchaType)) {
            captcha = CaptchaUtil.createGifCaptcha(width, height, codeLength);
        } else if (CaptchaTypeEnums.LINE.name().equalsIgnoreCase(captchaType)) {
            captcha = CaptchaUtil.createLineCaptcha(width, height, codeLength, interfereCount);
        } else if (CaptchaTypeEnums.SHEAR.name().equalsIgnoreCase(captchaType)) {
            captcha = CaptchaUtil.createShearCaptcha(width, height, codeLength, interfereCount);
        } else {
            throw new IllegalArgumentException("Invalid captcha type: " + captchaType);
        }
        captcha.setGenerator(codeGenerator);
        captcha.setTextAlpha(captchaProperties.getTextAlpha());
        captcha.setFont(captchaFont);

        String code = captcha.getCode();
        String imageBase64Data = captcha.getImageBase64Data();

        // 验证码文本缓存至Redis,用于登录校验
        String uuid = IdUtil.fastSimpleUUID();

        session.setAttribute(Constants.CAPTCHA_CODE, code);
        session.setAttribute(Constants.CAPTCHA_UUID, uuid);

        return CaptchaResult.builder().img(imageBase64Data).uuid(uuid).build();
    }

    @Override
    public boolean validate(String uuid, String code, HttpSession session) {
        // session中获取验证码
        String captchaCode = (String) session.getAttribute(Constants.CAPTCHA_CODE);
        return codeGenerator.verify(captchaCode, code);
    }

将用户输入的验证码与存储在 session 中的验证码进行对比,判断其是否相同,若相同且在1min内,则验证成功

前端代码

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <style>
        #app {
            font-family: Avenir, Helvetica, Arial, sans-serif;
            -webkit-font-smoothing: antialiased;
            -moz-osx-font-smoothing: grayscale;
            text-align: center;
            color: #2c3e50;
            margin-top: 60px;
        }

        #inputCaptcha {
            height: 30px;
            vertical-align: middle;
        }

        #verificationCodeImg {
            vertical-align: middle;
        }

        #checkCaptcha {
            height: 40px;
            width: 100px;
            border: 1px solid #000;
            border-radius: 5px;
            margin-left: 10px;
            vertical-align: middle;
        }
    </style>
</head>
<script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.4/jquery.min.js"></script>

<body>
<div id="app">
    <h1>{{ message }}</h1>
    <input type="text" v-model="inputCaptcha" id="inputCaptcha">
    <img id="verificationCodeImg" :src="captchaData.img" alt="验证码" @click="refreshCaptcha" title="看不清?换一张"/>
    <input type="button" value="提交" id="checkCaptcha" @click="checkCaptcha">
</div>

<script type="module">
    import {createApp, ref, onMounted} from 'https://unpkg.com/vue@3/dist/vue.esm-browser.js'

    createApp({
        setup() {
            const message = ref('验证码校验')
            const inputCaptcha = ref('')
            const captchaData = ref({
                img: '',
                uuid: ''
            })

            const refreshCaptcha = () => {
                $.ajax({
                    url: '/captcha/getCaptcha',
                    type: 'GET',
                    success: function (data) {
                        captchaData.value = data
                    }
                })
            }

            const checkCaptcha = () => {
                const  dataFrom = {
                    uuid: captchaData.value.uuid,
                    code: inputCaptcha.value
                }
                $.ajax({
                    url: '/captcha/check',
                    type: 'POST',
                    headers: {
                        'Content-Type': 'application/json'
                    },
                    data: JSON.stringify(dataFrom),
                    success: function (data) {
                        if (data) {
                            alert('验证码正确')
                            refreshCaptcha()
                        } else {
                            alert('验证码错误')
                            refreshCaptcha()
                        }
                    }
                })
            }

            // 页面加载完成后,刷新验证码
            onMounted(() => {
                refreshCaptcha()
            })

            return {
                message,
                captchaData,
                inputCaptcha,
                refreshCaptcha,
                checkCaptcha
            }
        }
    }).mount('#app')
</script>
</body>
</html>

验证成功:

验证失败:

为让验证码有多样性,这里将验证的生成放入了application.yml配置文件中,内容如下:

# 验证码配置
captcha:
  # 验证码类型 circle-圆圈干扰验证码|gif-Gif验证码|line-干扰线验证码|shear-扭曲干扰验证码
  type: circle
  # 验证码宽度
  width: 130
  # 验证码高度
  height: 48
  # 验证码干扰元素个数
  interfere-count: 2
  # 文本透明度(0.0-1.0)
  text-alpha: 0.8
  # 验证码字符配置
  code:
    # 验证码字符类型 math-算术|random-随机字符
    type: math
    # 验证码字符长度,type=算术时,表示运算位数(1:个位数运算 2:十位数运算);type=随机字符时,表示字符个数
    length: 1
  # 验证码字体
  font:
    # 字体名称 Dialog|DialogInput|Monospaced|Serif|SansSerif
    name: SansSerif
    # 字体样式 0-普通|1-粗体|2-斜体
    weight: 1
    # 字体大小
    size: 20
  # 验证码有效期(秒)
  expire-seconds: 120

以上就是SpringBoot3整合Hutool-captcha实现图形验证码的详细内容,更多关于SpringBoot3 Hutool-captcha验证码的资料请关注脚本之家其它相关文章!

相关文章

  • 如何劫持Java应用的HTTP请求

    如何劫持Java应用的HTTP请求

    这篇文章主要介绍了如何劫持Java应用的HTTP请求,帮助大家针对部分特殊的流量,希望将它引导到特定服务上,感兴趣的朋友可以了解下
    2020-10-10
  • java基础知识I/O流使用详解

    java基础知识I/O流使用详解

    编程语言的I/O类库中常常使用流这个抽象的概念,它代表任何有能力产生数据的数据源对象或时有能力接收数据的接收端对象,本文为大家介绍Java中I/O系统基础知识
    2014-01-01
  • SpringBoot多环境开发该如何配置

    SpringBoot多环境开发该如何配置

    这篇文章主要介绍了 SpringBoot多环境的开发配置详情,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2022-09-09
  • 使用spring-data-redis中的Redis事务

    使用spring-data-redis中的Redis事务

    这篇文章主要介绍了使用spring-data-redis中的Redis事务,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2024-07-07
  • SpringBoot Session接口验证实现流程详解

    SpringBoot Session接口验证实现流程详解

    这篇文章主要介绍了SpringBoot+Session实现接口验证(过滤器+拦截器)文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2022-09-09
  • 阿里巴巴 Sentinel + InfluxDB + Chronograf 实现监控大屏

    阿里巴巴 Sentinel + InfluxDB + Chronograf 实现监控大屏

    这篇文章主要介绍了阿里巴巴 Sentinel + InfluxDB + Chronograf 实现监控大屏,本文通过实例代码给大家介绍的非常详细,具有一定的参考借鉴价值,需要的朋友可以参考下
    2019-09-09
  • Java Cmd运行Jar出现乱码的解决方案

    Java Cmd运行Jar出现乱码的解决方案

    这篇文章主要介绍了Java Cmd运行Jar出现乱码的解决方案,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-09-09
  • Java基础之反射技术相关知识总结

    Java基础之反射技术相关知识总结

    今天带大家复习Java基础知识,文中对Java反射技术介绍的非常详细,对正在学习Java的小伙伴们很有帮助,,需要的朋友可以参考下
    2021-05-05
  • Java 二维码,QR码,J4L-QRCode 的资料整理

    Java 二维码,QR码,J4L-QRCode 的资料整理

    本文主要介绍Java 中二维码,QR码,J4L-QRCode,这里整理了详细的资料供大家学习参考关于二维码的知识,有需要的小伙伴可以参考下
    2016-08-08
  • springboot2.5.2与 flowable6.6.0整合流程引擎应用分析

    springboot2.5.2与 flowable6.6.0整合流程引擎应用分析

    这篇文章主要介绍了springboot2.5.2与 flowable6.6.0整合流程引擎应用分析,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2021-07-07

最新评论