验证码实现
在 Java 开发中,最常用的验证码类型有图片验证码、短信 / 邮箱验证码、行为验证码,在这里我们主要介绍一下图片验证码的相关内容。
easy-captcha
核心定位:
easy-captcha 是一款专为 Java Web 场景设计的轻量级开源验证码库,核心目标是简化各类验证码的开发与集成,替代手写验证码的复杂底层实现。
核心价值:
该库封装了画布创建、随机字符生成、干扰线 / 噪点绘制、IO 流输出等所有手写验证码的底层细节,大幅降低开发成本,开发者仅需几行代码即可快速实现验证码功能,无需关注图形绘制的底层逻辑。
核心特性
零依赖轻量化:仅依赖 JDK 内置的 AWT 绘图类库,无需引入任何第三方依赖,打包体积小,部署无额外成本;
多类型全覆盖:支持静态图片、动态 GIF、算术运算、中文汉字等主流验证码类型,满足绝大多数业务场景需求;
高度可定制化:支持自定义验证码长度、图片尺寸、字体样式、颜色方案、干扰元素等,适配个性化视觉需求;
输出方式灵活:支持三种输出形式 ------ 本地文件、HTTP 响应流(适配传统 Web 项目)、Base64 编码字符串(适配前后端分离项目);
Web 生态友好:原生适配 Servlet/JSP、Spring MVC、Spring Boot 等 Java Web 技术栈,提供开箱即用的工具类,无需额外适配。
引入依赖坐标
xml
<dependency>
<groupId>com.github.whvcse</groupId>
<artifactId>easy-captcha</artifactId>
<version>1.6.2</version>
</dependency>
1.6.2 是 easy-captcha 的稳定版本,适配 JDK 8+,如果项目使用的是 JDK 11+,建议使用 1.7.0 及以上版本,修复了高版本 JDK 的兼容问题。
支持的验证码类型
静态验证码:静态 PNG 格式,性能最高
| 子类型 | 实现类 | 核心特点 | 适用场景 |
|---|---|---|---|
| 轻量字符验证码 | CharCaptcha | 专注数字 / 字母 / 混合字符 | 对性能要求高的简单场景 |
| 纯数字验证码 | NumericCaptcha | 仅含 0-9 数字 | 普通登录、简单验证 |
| 纯字母验证码 | LetterCaptcha | 含大小写英文字母 | 中等安全需求的验证 |
| 数字 + 大写字母验证码 | SpecCaptcha | 数字 + 大写字母,且剔除易混淆字符 | 绝大多数通用登录 / 注册场景 |
| 算术运算验证码 | ArithmeticCaptcha | 生成 0-99 加减乘算术题 | 登录 / 注册等核心业务场景 |
| 中文验证码 | ChineseCaptcha | 随机中文汉字 | 防破解能力远高于字符类,高安全需求 |
动态验证码:GIF 格式,防识别最强
| 子类型 | 实现类 | 核心特点 | 适用场景 |
|---|---|---|---|
| 字符 GIF 验证码 | GifCaptcha | 字符随机位移 / 闪烁 | 高安全防爬虫场景 |
| 中文 GIF 验证码 | ChineseGifCaptcha | 汉字随机位移 / 闪烁 | 极高安全需求 |
| 算术 GIF 验证码 | ArithmeticGifCaptcha | 算术题字符动态位移 | 核心业务和高防爬需求 |
下面具体介绍 SpecCaptcha 类
SpecCaptcha
SpecCaptcha 是开源验证码库 easy-captcha 面向通用 Web 验证场景设计的静态图片验证码实现类。
该类的核心字符集限定为数字(0-9)与大写英文字母(A-Z),并通过剔除 0/O、1/I、8/B 等视觉易混淆字符,降低用户识别误差。输出形态为 PNG 格式静态图片,具备生成效率高、加载速度快的特性,在易用性、安全防护能力与用户体验间实现平衡,是 Java Web 项目中登录、注册等常规验证场景的主流选型。
下面介绍该库的部分使用方法,主要介绍构造方法、核心获取 / 输出方法 以及 样式配置方法。
构造方法
用于创建 SpecCaptcha 实例,支持自定义图片尺寸、验证码长度
| 方法签名 | 说明 |
|---|---|
| SpecCaptcha() | 无参构造:默认宽度 150、高度 50、验证码长度 4 |
| SpecCaptcha(int width, int height) | 指定宽高,验证码长度默认 4 |
| SpecCaptcha(int width, int height, int len) | 自定义宽、高、验证码长度(最常用) |
获取 / 输出方法
这类方法是验证码使用的核心,用于获取验证码文本、生成图片、输出到流:
| 方法签名 | 说明 |
|---|---|
| String text() | 获取生成的验证码文本 |
| BufferedImage image() | 生成验证码图片的 BufferedImage 对象 |
| void out(OutputStream os) | 将验证码图片以 PNG 格式写入输出流(Web 场景中写入响应流) |
| void out(ServletOutputStream sos) | 重载方法,适配 Servlet 响应流(简化 Web 输出) |
| String toBase64() | 将验证码图片转换为 Base64 编码字符串(适用于前后端分离场景) |
| String toBase64(boolean deletePrefix) | 转换为 Base64,deletePrefix=true 则移除 data:image/png;base64, 前缀 |
样式配置方法
自定义验证码外观,用于调整验证码的字体、尺寸、干扰元素、背景等样式,均为 set/get 成对出现:
void setWidth(int width) / int getWidth() 设置 / 获取验证码图片宽度
void setHeight(int height) / int getHeight() 设置 / 获取验证码图片高度
void setLen(int len) / int getLen() 设置 / 获取验证码字符长度
void setCharType(int charType) / int getCharType() 设置 / 获取字符类型(核心),可选值:
Captcha.TYPE_DEFAULT:中文
Captcha.TYPE_ONLY_NUMBER:纯数字
Captcha.TYPE_ONLY_CHAR:纯字母
Captcha.TYPE_MIX:数字 + 字母
void setFont(Font font) / Font getFont() 设置 / 获取字符字体(支持自定义字体、字号、加粗)
void setInterfereLineCount(int count) / int getInterfereLineCount() 设置 / 获取干扰线数量
void setNoiseCount(int count) / int getNoiseCount() 设置 / 获取干扰像素点
void setBackground(Color color) / Color getBackground() 设置 / 获取图片背景色
void setCharColor(Color color) / Color getCharColor() 设置 / 获取字符颜色(默认随机颜色)
验证码实现的代码示例
前端 vue 代码示例:
html
<template>
<div class="captcha-container">
<el-form ref="captchaForm" :model="loginForm" :rules="loginRules">
<el-form-item prop="captcha">
<el-input
ref="captcha"
v-model="loginForm.captcha"
placeholder="请输入验证码"
prefix-icon="el-icon-circle-check"
name="captcha"
style="width: 65%"
size="default"
/>
<!-- 验证码图片:支持点击刷新与展示图片 -->
<el-image
class="captcha-img"
:src="captchaPath"
@click="getCaptcha()"
/>
</el-form-item>
</el-form>
</div>
</template>
<script>
// 验证码API导入
import { getCaptchaImg } from '@/api/sys/login'
export default {
name: 'CaptchaDemo',
data() {
return {
captchaPath: '',
loginForm: {
captcha: '', // 用户输入的验证码
uuid: '' // 验证码唯一标识(传给后端验证用)
},
// 验证码输入校验规则
loginRules: {
captcha: [{ required: true, message: '请输入验证码', trigger: 'blur' }]
}
}
},
// 页面创建时自动加载验证码
created() {
this.getCaptcha()
},
methods: {
// 核心:获取/刷新验证码
getCaptcha() {
getCaptchaImg().then(res => {
this.captchaPath = res.data.captcha // 赋值验证码图片地址
this.loginForm.uuid = res.data.uuid // 保存验证码唯一标识
// 开发环境自动填充验证码
if (process.env.NODE_ENV === 'development') {
this.loginForm.captcha = res.data.code
}
})
}
}
}
</script>
<style lang="scss" scoped>
.captcha-container {
padding: 30px;
background: #ffffff;
width: 390px;
border-radius: 5px;
.captcha-img {
width: 100px;
height: 40px;
margin-left: 8px;
position: absolute;
cursor: pointer;
}
}
</style>
其中导入的验证码 API 的逻辑内容为:
javascript
export function getCaptchaImg() {
return request({
url: '/captcha',
method: 'get'
})
}
后端逻辑代码示例:
java
@RestController
public class LoginController {
@Autowired
private RedisTemplate redisTemplate;
/**
* 获取验证码
*
* @return
*/
@GetMapping("/captcha")
public Result getCaptcha() {
// 借助工具包生成符合要求的验证码
SpecCaptcha specCaptcha = new SpecCaptcha(130, 48, 4);
// 获取到验证码图片的内容值并转大写
String code = specCaptcha.text().toUpperCase();
// 用uuid生成唯一的key,用于存储验证码
String uuid = IdUtil.simpleUUID();
// 往redis里存数据
redisTemplate.opsForValue().set(uuid, code, 120, TimeUnit.SECONDS);
// 往回组装数据
HashMap<String, String> res = new HashMap<String, String>();
res.put("uuid", uuid);
// 获取Base64编码
res.put("captcha", specCaptcha.toBase64());
res.put("code", code);
return Result.ok().put("data", res);
}
}
Base64 编码的核心作用:
后端通过 easy-captcha 生成验证码图片后,会得到图片的二进制数据(字节流),Base64 编码能将这种二进制数据转换成可打印的文本字符串(避免二进制传输的乱码 / 解析问题),这段代码中 specCaptcha.toBase64() 会直接生成包含 Data URI 前缀(如data:image/png;base64,)的 Base64 字符串,前端拿到后无需手动拼接前缀,直接将这个字符串赋值给 <img> 标签的 src 属性,浏览器就能自动解码 Base64 字符串并渲染出验证码图片。