基于SpringBoot和Hutool工具包实现的验证码案例

news2024/11/16 10:25:28

目录

验证码案例

1. 需求

2. 准备工作

3. 约定前后端交互接口 

需求分析

接口定义

4. Hutool 工具介绍

5. 实现验证码

后端代码 

前端代码

6. 运行测试 


验证码案例

随着安全性的要求越来越高,目前项目中很多都会使用验证码,只要涉及到登录,绝大多数都会有验证的要求,验证码的形式也是多种多样,更复杂的图形验证码和行为验证码已经成为了更流行的趋势。

验证码的实现方式很多,可以前端实现,也可以后端实现。网上也有比较多的插件或者工具包可以使用,这里选择使用 Hutool工具包 来实现一个简单的验证码案例。完整代码见文章末尾。

1. 需求

最终实现的验证码界面如下图所示:

1. 页面生成验证码

2. 输入验证码,点击提交,验证用户输入验证码是否正确且是否超时,正确且未超时则进行页面跳转,错误或已超时则进行提示,并更换验证码

2. 准备工作

创建SpringBoot项目,引入Spring-Web-MVC和Lombok的依赖包,前端界面会在后面提供.

3. 约定前后端交互接口 

需求分析

后端需要提供两个服务:

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

接口定义

1)生成验证码

请求: 

请求URL:/captcha/get

 响应:浏览器显示图片内容

浏览器给服务器发送一个 /captcha/get 请求,服务器返回一个图片,浏览器显示在页面上

2)校验验证码是否正确

请求:

请求URL:/captcha/check
请求参数:captcha=验证码字符串

响应:

true or false
根据用户输入的验证码,校验验证码是否正确,并判断验证码是否超时

4. Hutool 工具介绍

Hutool官网:https://hutool.cn/

Hutool参考文档:https://hutool.cn/docs/#/

要使用Hutool工具包,需要在pom.xml文件中添加对应的依赖,参考文档中有详细介绍:

<dependency>
    <groupId>cn.hutool</groupId>
    <artifactId>hutool-all</artifactId>
    <version>5.8.16</version>
</dependency>

在Hutolol参考文档中找到图形验证码部分:

接下来可以就可以参考Hutool工具提供的方法,快速生成验证码,里面的介绍非常清晰,很容易看懂。此处使用如下格式验证码:

5. 实现验证码

项目目录结构如下:

后端代码 

实现接口

import cn.hutool.captcha.CaptchaUtil;
import cn.hutool.captcha.LineCaptcha;
import cn.hxxy.captchademo.config.CaptchaProperties;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.servlet.http.HttpSession;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.io.IOException;
import java.util.Date;

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

    @Autowired
    private CaptchaProperties captchaProp;

    @RequestMapping("/get")
    public void getCaptcha(HttpServletResponse response, HttpSession session) {
        //定义图形验证码的长和宽(配置默认值)
        LineCaptcha lineCaptcha = CaptchaUtil.createLineCaptcha(captchaProp.getWidth(), captchaProp.getHeight());
        //细节问题,不影响程序
        //设置返回类型
        response.setContentType("image/jpeg");
        //静止缓存
        response.setHeader("Progma", "No-cache");
        try {
            //图形验证码写出,可以写出到文件,也可以写出到流
            lineCaptcha.write(response.getOutputStream());
            //同时将验证码内容和当前时间戳存储到Session中
            //此处Session的键可以配置成常量
            session.setAttribute(captchaProp.getSession().getKey(), lineCaptcha.getCode());
            session.setAttribute(captchaProp.getSession().getDate(), new Date());
            //关流
            response.getOutputStream().close();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    //验证码生效时间限制
    private static final long VALID_MILLIS_TIME = 60 * 1000;

    @RequestMapping("/check")
    public boolean checkCaptcha(String captcha, HttpSession session) {
        //保证传过来的参数是合法的
        if (StringUtils.hasLength(captcha)) {
            //根据配置的默认session信息获取key和date
            String key = (String) session.getAttribute(captchaProp.getSession().getKey());
            Date date = (Date) session.getAttribute(captchaProp.getSession().getDate());
            //1.验证码正确(不区分大小写)  2.验证码还未失效
            return key.equalsIgnoreCase(captcha)
                    && System.currentTimeMillis() - date.getTime() < VALID_MILLIS_TIME;
        }
        return false;
    }
}

代码解析:

  1. 通过@Autowired注解注入了一个CaptchaProperties对象,这个对象是用来配置图形验证码的属性的。使用这种方式是因为代码中的有些属性可能会在别处使用,且它们都是固定的,例如图形验证码的长和宽,Session的字段名等。将这些属性封装在一个对象中,并通过读取配置文件的方式绑定属性值。

  2. 在@RequestMapping注解的getCaptcha方法中,使用Hutool包的CaptchaUtil.createLineCaptcha方法创建了一个图形验证码对象lineCaptcha,其大小由配置文件中的captchaProp.getWidth()和captchaProp.getHeight()决定。然后,将验证码内容和当前时间戳存储到HttpSession中,以便后续校验验证码的时候使用。最后,将图形验证码写出到HttpServletResponse的输出流中,以返回给前端页面显示。

  3. 在@RequestMapping注解的checkCaptcha方法中,首先判断传入的验证码参数是否合法。如果合法,则从HttpSession中获取存储的验证码内容和时间戳信息,并进行比较。如果验证码正确且未过期,则返回true,否则返回false。

CaptchaProperties类的实现和配置文件(.yml)信息如下:

import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;

@Data
@Configuration
@ConfigurationProperties(prefix = "captcha")
public class CaptchaProperties {
    private Integer width;
    private Integer height;
    private Session session; //使用自定义的Session类

    @Data
    public static class Session {
        private String key;
        private String date;
    }
}
spring:
  application:
    name:
      captcha-demo

captcha:
  width: 200
  height: 100
  session:
    key: CAPTCHA_SESSION_KEY
    date: CAPTCHA_SESSION_DATE

此处还没有前端代码,但是已经可以先测试了,通常写好一个接口就可以测试一下。

启动项目,再访问 http://127.0.0.1:8080/captcha/get  显示验证码图片如下:

通过 Fiddler 进行抓包,HTTP请求和响应数据如下,图片就在响应的body部分:

前端代码

注意前端代码要放在resources的static目录下

index.html(重点关注script标签内的代码):

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="utf-8">

    <title>验证码</title>
    <style>
        #inputCaptcha {
            height: 30px;
            vertical-align: middle;
        }

        #verificationCodeImg {
            vertical-align: middle;
        }

        #checkCaptcha {
            height: 40px;
            width: 100px;
        }
    </style>
</head>

<body>
<h1>输入验证码</h1>
<div id="confirm">
    <input type="text" name="inputCaptcha" id="inputCaptcha">
    <img id="verificationCodeImg" src="/captcha/get" style="cursor: pointer;" title="看不清?换一张"/>
    <input type="button" value="提交" id="checkCaptcha">
</div>
<script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.4/jquery.min.js"></script>
<script>
    //点击图片,切换验证码
    $("#verificationCodeImg").click(function () {
        //$(this).hide().attr('src', '/captcha/get?dt=' + new Date().getTime()).fadeIn();
        //使用attr()方法修改src属性值,获取一个新的验证码图片,后面添加一个时间戳参数来保证每次请求的验证码图片都是不同的
        $(this).attr('src', '/captcha/get?dt=' + new Date().getTime());
    });
    //点击提交按钮,进行验证码校验
    $("#checkCaptcha").click(function () {
        $.ajax({
            url: "/captcha/check",
            type: "post",
            data: {
                captcha: $("#inputCaptcha").val()
            },
            success: function (result) {
                if (result) {
                    location.href = "success.html";
                } else {
                    alert("验证码错误或已超时");
                    //验证码错误,重新生成验证码
                    $("#verificationCodeImg").attr('src', '/captcha/get?dt=' + new Date().getTime());
                    $("#inputCaptcha").val("");
                }
            }
        });
    });

</script>
</body>

</html>

JavaScript代码中主要包含两部分逻辑:

  1. 点击图片,切换验证码: 当id为"verificationCodeImg"的图片被点击时,会触发click事件的回调函数。回调函数通过修改图片的src属性,向后端发送一个请求来获取新的验证码图片,并使用当前时间戳作为参数,以确保每次请求的验证码图片都是不同的。最后,将获取到的新图片显示出来。

  2. 点击提交按钮,进行验证码校验: 当id为"checkCaptcha"的按钮被点击时,会触发click事件的回调函数。回调函数通过使用jQuery的ajax方法发送一个POST请求到"/captcha/check"接口,并传递了一个名为"captcha"的参数,该参数的值为id为"inputCaptcha"的输入框中用户输入的验证码。

    在请求成功后的回调函数中,会根据后端返回的结果(result)进行处理。如果校验成功,则跳转到"success.html"页面;如果校验失败,则弹出一个提示框显示"验证码错误或已超时",并重新生成新的验证码图片并清空输入框中的验证码。

success.html:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>验证成功页</title>
</head>
<body>
    <h1>验证成功</h1>
</body>
</html>

6. 运行测试 

启动项目,浏览器访问 http://127.0.0.1:8080/index.html 或 http://localhost:8080/index.html

输入错误的验证码: 

提示正确,且验证码能正确刷新,超时同样会提示以上错误。且点击验证码图片时,会重新生成验证码。

输入正确的验证码:

验证成功,页面正确跳转,测试完成。

完整代码:https://gitee.com/lv-jiacong/Java-EE/tree/master/captcha-demo/src

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1684630.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【C语言】VS编译器的scanf

我们在写代码的时候通常需要用到输入函数&#xff1a;scanf&#xff0c;但在vs编译环境下却必须写为&#xff1a;scanf_s&#xff0c;这是为什么呢&#xff1f;这里就是vs规定的了&#xff0c;VS认为这样写更安全&#xff0c;但如果我们非要写成scanf形式也是有办法的。 # 看我…

hls.js实现分片播放视频

前言&#xff1a;hls.js官网&#xff1a;hls.js - npm 一、demo——在HTML中使用 <audio id"audio" controls></audio><script src"https://cdn.jsdelivr.net/npm/hls.jslatest"></script> <script>document.addEventList…

OTP8脚-全自动擦鞋机WTN6020-低成本语音方案

一&#xff0c;产品开发背景 首先&#xff0c;随着人们生活质量的提升&#xff0c;对鞋子的保养需求也日益增加。鞋子作为人们日常穿着的重要组成部分&#xff0c;其清洁度和外观状态直接影响到个人形象和舒适度。因此&#xff0c;一种能够自动清洁和擦亮鞋子的设备应运而生&am…

oracle中insert all的用法

1、简述 使用insert into语句进行表数据行的插入&#xff0c;但是oracle中有一个更好的实现方式&#xff1a;使用insert all语句。 insert all语句是oracle中用于批量写数据的 。insert all分又为 无判断条件插入有判断条件插入有判断条件插入分为 Insert all when... 子句 …

VMware 和 VirtualBox开机自启指定虚拟机详细教程

VMware上虚拟机随宿主机开机自启 1. 设置自动启动虚拟机 网上教程旧版的&#xff0c;界面和新版有所差异。17版本设置如下&#xff1a;VMware Workstation工作台 -> 文件 -> 配置自动启动虚拟机 -> 按顺序选择需要启动的虚拟机 VMWare17配置自动启动虚拟机提示&…

使用WindTerm通过公钥让其他同事登录linux服务器

1.其他同事电脑操作cmd输入 ssh-keygen,如果是第一次生成公钥&#xff0c;则直接一路回车确认就行 会生成在C:\Users\nideNAME\.ssh目录 公钥文件名为id_rsa.pub&#xff0c;私钥文件名为id_rsa。 2.你的电脑你已经连接上服务器了 在root下新建目录 root/.ssh/新建文件touck …

数据库小项目——叮叮移动业务大厅(三层架构+MySQL数据库)

源码已上传至资源 该项目主要使用技术为MySQL数据库&#xff0c;其中也包含了一些对于文件的写入和读取操作。项目结构采用三层架构&#xff0c;后端的业务逻辑清晰明了。 1.项目结构 项目采用控制台版&#xff0c;前端业务在java包下&#xff0c;每个业务单独成块。若想要GUI…

GD32F307+lwip+freeRTOS+DP83848 JPerf接收测速

1.原理图 2.代码 https://www.firebbs.cn/forum.php?modviewthread&tid26274&fromuid37393 //22_ENET 1&#xff09;注释掉tcp_client_init(); 2&#xff09;init_task中添加测速线程iperf_server_init() //main.c #include "gd32f30x.h" #include &quo…

自定义RedisTemplate序列化器

大纲 RedisSerializerFastJsonRedisSerializer自定义二进制序列化器总结代码 在《RedisTemplate保存二进制数据的方法》一文中&#xff0c;我们将Java对象通过《使用java.io库序列化Java对象》中介绍的方法转换为二进制数组&#xff0c;然后保存到Redis中。实际可以通过定制Red…

QTextCodec NO such file or directory让qt6兼容qt5

首先在.pro 文件中新加 QT core5compat这时会报错 链接 报错之后修复qt&#xff0c;新加兼容模块&#xff0c;见链接。

C++实现基于http协议的epoll非阻塞模型的web服务器框架(支持访问服务器目录下文件的解析)

使用方法&#xff1a; 编译 例子&#xff1a;./httpserver 9999 ../ htmltest/ 可执行文件 端口 要访问的目录下的 例子&#xff1a;http://192.168.88.130:9999/luffy.html 前提概要 http协议 &#xff1a;应用层协议&#xff0c;用于网络通信&#xff0c;封装要传输的数据&…

LeetCode 79.单词搜索

原题链接&#xff1a;. - 力扣&#xff08;LeetCode&#xff09; 给定一个 m x n 二维字符网格 board 和一个字符串单词 word 。如果 word 存在于网格中&#xff0c;返回 true &#xff1b;否则&#xff0c;返回 false 。 单词必须按照字母顺序&#xff0c;通过相邻的单元格内…

本地centos7+docker+ollama+gpu部署

1、一台有 NVIDIA GPU 驱动的机器 2、Docker CE安装 # 删除旧版本的 Docker&#xff08;如果存在&#xff09; sudo yum remove -y docker docker-common docker-selinux docker-engine # 安装必要的软件包&#xff1a; sudo yum install -y yum-utils device-mapper-persiste…

PyCharm设置——用于调试虚拟环境中的django程序

使用Pycharm新建了一个项目。 项目目录&#xff1a;C:\Users\grace\PycharmProjects\learning_log 在该路径下安装虚拟环境ll_env&#xff0c;并在虚拟环境下安装Django。 为了调试该Django需要对PyCharm进行设置。 1、确保PyCharm使用正确的虚拟环境 打开PyCharm&#xff…

市面上前 11 名的 Android 数据恢复软件

Android数据恢复软件是恢复无意中删除的文件或文件夹的必要工具。该软件还将帮助您恢复丢失或损坏的信息。本文介绍提供数据备份和磁盘克隆选项的程序&#xff0c;这些选项有助于在Android设备上恢复文件的过程。 如果您正在寻找一种有效的方法来恢复图像&#xff0c;文档&…

qmt量化交易策略小白学习笔记第8期【qmt编程之获取股票资金流向数据--内置Python】

qmt编程之获取股票资金流向数据 qmt更加详细的教程方法&#xff0c;会持续慢慢梳理。 也可找寻博主的历史文章&#xff0c;搜索关键词查看解决方案 &#xff01; 感谢关注&#xff0c;需免费开通量化回测与咨询实盘权限&#xff0c;可以和博主联系&#xff01; 获取股票资金…

java学习四

Random 随机数 数组 静态初始化数组 数组在计算机中的基本原理 数组的访问 什么是遍历 数组的动态初始化 动态初始化数组元素默认值规则 Java内存分配介绍 数组在计算机中的执行原理 使用数组时常见的一个问题 案例求数组元素最大值 public class Test1 {public static void ma…

面试八股之MySQL篇5——主从同步原理篇

&#x1f308;hello&#xff0c;你好鸭&#xff0c;我是Ethan&#xff0c;一名不断学习的码农&#xff0c;很高兴你能来阅读。 ✔️目前博客主要更新Java系列、项目案例、计算机必学四件套等。 &#x1f3c3;人生之义&#xff0c;在于追求&#xff0c;不在成败&#xff0c;勤通…

Qt学习记录(14)线程

前言&#xff1a; 我的臀部已经翘到可以顶起一屁股债了 为什么要使用线程 什么时候用线程 复杂的数据处理 头文件.h #ifndef WIDGET_H #define WIDGET_H#include <QWidget> #include <QTimer>//定时器头文件QT_BEGIN_NAMESPACE namespace Ui { class Widget; }…

表现层框架设计之表现层设计模式_3.MVVM模式

1.MVVM模式 MVVM模式正是为解决MVP中UI种类变多&#xff0c;接口也会不断增加的问题而提出的。 MVVM模式全称是模型-视图-视图模型&#xff08;Model-View-ViewModel&#xff09;&#xff0c;它和MVC、MVP类似&#xff0c;主要目的都是为了实现视图和模型的分离&#xff0c;不…