目录
前言
验证码实现方式很多,可以前端实现,也可以后端实现,网上也有比较多的插件或者工具包可以使用,咱们选择使用Hutool提供的小工具来实现。
一、Hutool工具介绍
Hutool是一个Java工具包类库,对文件、流、加密解密、转码、正则、线程、XML等JDK方法进行封装,组成各种Util工具类。Hutool官网:https://hutool.cn/
1.1 Maven
如果你想在项目中使用Hutool中的某个模块,在项目的pom.xml的dependencies中加入以下内容:
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-captcha</artifactId>
<version>5.8.22</version>
</dependency>
1.2 介绍
验证码功能位于cn.hutool.captcha包中,核心接口为ICaptcha
,此接口定义了以下方法:
createCode
创建验证码,实现类需同时生成随机验证码字符串和验证码图片getCode
获取验证码的文字内容verify
验证验证码是否正确,建议忽略大小写write
将验证码图片写出到目标流中
其中write方法只有一个OutputStream
,ICaptcha
实现类可以根据这个方法封装写出到文件等方法。
AbstractCaptcha
为一个ICaptcha
抽象实现类,此类实现了验证码文本生成、非大小写敏感的验证、写出到流和文件等方法,通过继承此抽象类只需实现createImage
方法定义图形生成规则即可。
1.3 实现类
LineCaptcha线段干扰的验证码
贴栗子:
import cn.hutool.captcha.CaptchaUtil;
import cn.hutool.captcha.LineCaptcha;
import cn.hutool.core.lang.Console;
public class LineCaptchaTest {
public static void main(String[] args) {
//定义图形验证码的长和宽
LineCaptcha lineCaptcha = CaptchaUtil.createLineCaptcha(200, 100);
//图形验证码写出,可以写出到文件,也可以写出到流
lineCaptcha.write("d:/line.png");
//输出code
Console.log(lineCaptcha.getCode());
//验证图形验证码的有效性,返回boolean值
lineCaptcha.verify("1234");
//重新生成验证码
lineCaptcha.createCode();
lineCaptcha.write("d:/line.png");
//新的验证码
Console.log(lineCaptcha.getCode());
//验证图形验证码的有效性,返回boolean值
lineCaptcha.verify("1234");
}
}
控制台截图生成的验证码:
在写入的路径中查看代码生成的验证码截图:
CircleCaptcha
圆圈干扰验证码
import cn.hutool.captcha.CaptchaUtil;
import cn.hutool.captcha.CircleCaptcha;
import cn.hutool.core.lang.Console;
public class CircleCaptchaTest {
public static void main(String[] args) {
//定义图形验证码的长、宽、验证码字符数、干扰元素个数
CircleCaptcha captcha = CaptchaUtil.createCircleCaptcha(200, 100, 4, 20);
//CircleCaptcha captcha = new CircleCaptcha(200, 100, 4, 20);
//图形验证码写出,可以写出到文件,也可以写出到流
captcha.write("d:/circle.png");
//输出code
Console.log(captcha.getCode());
//验证图形验证码的有效性,返回boolean值
captcha.verify("1234");
}
}
控制台截图生成的验证码:
在写入的路径中查看代码生成的验证码截图:
ShearCaptcha
扭曲干扰验证码
import cn.hutool.captcha.CaptchaUtil;
import cn.hutool.captcha.ShearCaptcha;
import cn.hutool.core.lang.Console;
public class ShearCaptchaTest {
public static void main(String[] args) {
//定义图形验证码的长、宽、验证码字符数、干扰线宽度
ShearCaptcha captcha = CaptchaUtil.createShearCaptcha(200, 100, 4, 4);
//ShearCaptcha captcha = new ShearCaptcha(200, 100, 4, 4);
//图形验证码写出,可以写出到文件,也可以写出到流
captcha.write("d:/shear.png");
//输出code
Console.log(captcha.getCode());
//验证图形验证码的有效性,返回boolean值
captcha.verify("1234");
}
}
控制台截图生成的验证码:
在写入的路径中查看代码生成的验证码截图:
二、验证码案例
2.1 需求
需求如下:
- 页面生成验证码
- 输入验证码,点击提交,验证用户输入验证码是否正确,正确则进行页面跳转
2.2 约定前后端交互接口
2.2.1 需求分析
后端需要提供两个服务:
- 生成验证码,并返回验证码
- 校验验证码是否正确
2.2.2 接口定义
1、生成验证码:
url:/captcha/get
param:无
return:图片的内容
2、校验验证码
url:/captcha/check
param:inputCode
return:true/false
2.3 后端生成验证码
import cn.hutool.captcha.CaptchaUtil;
import cn.hutool.captcha.LineCaptcha;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.io.IOException;
@RestController
@RequestMapping("/captcha")
public class CaptchaController {
@RequestMapping("/get")
public void getCaptcha(HttpServletResponse response) {
//定义图形验证码的长和宽
LineCaptcha lineCaptcha = CaptchaUtil.createLineCaptcha(200, 100);
//图像验证码写出,可以写出到文件,也可以写出到流,此处写出到流
try {
lineCaptcha.write(response.getOutputStream());
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
小技巧:在我们每写完一个后端代码模块时,我们可以进行测试,看是否有错误,避免后续代码量过多发生错误时不知道哪块出错。
根据后端定义的url进行测试截图:
2.4 前端接收验证码图片
<!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();
});
$("#checkCaptcha").click(function () {
alert("验证码校验");
});
</script>
</body>
</html>
验证前端是否有问题:
2.5 后端校验验证码
import cn.hutool.captcha.CaptchaUtil;
import cn.hutool.captcha.LineCaptcha;
import com.example.demo.model.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 {
private final static long session_valid_timeout = 60 * 1000;
@Autowired
private CaptchaProperties captchaProperties;
@RequestMapping("/check")
public Boolean check(HttpSession session, String inputCode) {
//验证码输入的内容和用户输入的进行比较
//从session获取信息
if (!StringUtils.hasLength(inputCode)) {
return false;
}
String savedCode = (String) session.getAttribute(captchaProperties.getSession().getKey());
Date saveDate = (Date) session.getAttribute(captchaProperties.getSession().getDate());
if (inputCode.equalsIgnoreCase(savedCode)) {
//判断验证码是否过期
if (saveDate != null && System.currentTimeMillis() - saveDate.getTime() < session_valid_timeout) {
return true;
}
return true;
}
return false;
}
}
检查校验验证码是否存在问题:
1、先查看后端生成的验证码
2、根据校验验证的url进行验证
首先先输入错误的验证码观察返回ture或者false,如果返回false证明验证码输入错误或者过期,否则反正true。
先输入错误的验证码1234截图:
再输入正确的验证码截图:
2.6 前端校验验证码
<!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();
});
$("#checkCaptcha").click(function () {
$.ajax({
url: "/captcha/check",
type: "post",
data: {
inputCode: $("#inputCaptcha").val()
},
success: function(result) {
if (result) {
location.href = "success.html";
} else {
alert("验证码错误或者过期");
}
}
})
});
</script>
</body>
</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>
运行截图:
2.7 后端完整代码
package com.example.demo.controller;
import cn.hutool.captcha.CaptchaUtil;
import cn.hutool.captcha.LineCaptcha;
import com.example.demo.model.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 {
private final static long session_valid_timeout = 60 * 1000;
@Autowired
private CaptchaProperties captchaProperties;
@RequestMapping("/get")
public void getCaptcha(HttpSession session, HttpServletResponse response) {
//定义图形验证码的长和宽
LineCaptcha lineCaptcha = CaptchaUtil.createLineCaptcha(captchaProperties.getWidth(), captchaProperties.getHeight());
//设置缓存类型
response.setContentType("image/jpeg");
//禁止缓存
response.setHeader("Progma", "No-cache");
//图像验证码写出,可以写出到文件,也可以写出到流,此处写出到流
try {
lineCaptcha.write(response.getOutputStream());
//存储session
session.setAttribute(captchaProperties.getSession().getKey(), lineCaptcha.getCode());
session.setAttribute(captchaProperties.getSession().getDate(), new Date());
} catch (IOException e) {
throw new RuntimeException(e);
}
}
@RequestMapping("/check")
public Boolean check(HttpSession session, String inputCode) {
//验证码输入的内容和用户输入的进行比较
//从session获取信息
if (!StringUtils.hasLength(inputCode)) {
return false;
}
String savedCode = (String) session.getAttribute(captchaProperties.getSession().getKey());
Date saveDate = (Date) session.getAttribute(captchaProperties.getSession().getDate());
if (inputCode.equalsIgnoreCase(savedCode)) {
//判断验证码是否过期
if (saveDate != null && System.currentTimeMillis() - saveDate.getTime() < session_valid_timeout) {
return true;
}
}
return false;
}
}
配置文件:
captcha:
width: 200
height: 100
session:
key: captcha_session_key
date: captcha_session_date
captcha配置:
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
@Component
@ConfigurationProperties(prefix = "captcha")
@Data
public class CaptchaProperties {
private Integer width;
private Integer height;
private Session session;
@Data
public static class Session {
private String key;
private String date;
}
}