本文目录导读:

搭建图形验证码有很多方式,主要取决于你的技术栈(后端语言、框架)以及是否需要集成到现有业务中。
目前主流的图形验证码方案可以分为三大类:
- 自研方案:自己生成图片或SVG。
- 集成开源库:使用成熟的开源验证码工具包(如
captcha、kaptcha、django-simple-captcha)。 - 第三方服务:极验、腾讯防水墙、Cloudflare Turnstile等(这些更多是滑块、点选或行为验证,安全性更高,但非纯图形验证码)。
下面以最常用的 后端生成图片 + 前端展示 方案为例,从原理到具体实现进行说明。
核心原理(无论用什么语言)
- 后端生成:在服务器生成一张包含随机字符或图形的图片,同时将正确答案(通常是文本)存入Session或缓存(Redis)。
- 存入Session:
session["captcha_code"] = "A1B2" - 存入Redis:
SET 用户唯一标识 "A1B2" EX 300(5分钟过期)
- 存入Session:
- 前端展示:后端通过HTTP响应返回图片的二进制流(
image/png),前端直接使用<img src="/captcha">展示。 - 用户输入:用户在输入框中填写看到的字符。
- 后端校验:用户提交表单时,将输入的字符与 session中/redis 中存储的答案进行比对(通常忽略大小写),比对后立即删除该session/缓存,防止重复使用。
具体实现示例(主流语言)
Python(Flask + Pillow 方案)
这是非常经典且上手快的方式,需要安装 Pillow 库用于图像处理。
from flask import Flask, session, make_response, request
from PIL import Image, ImageDraw, ImageFont
import random
import io
app = Flask(__name__)
app.secret_key = 'your-secret-key-here' # 必须设置,用于签名session
def generate_captcha():
# 1. 生成随机字符 (4-6位)
chars = 'ABCDEFGHJKLMNPQRSTUVWXYZ23456789' # 去掉易混淆的 0, O, 1, I
code = ''.join(random.choices(chars, k=4))
# 2. 创建图片 (宽度120, 高度40)
img = Image.new('RGB', (120, 40), color=(255, 255, 255)) # 白色背景
draw = ImageDraw.Draw(img)
# 3. 添加干扰线 (3条)
for i in range(3):
start = (random.randint(0, 120), random.randint(0, 40))
end = (random.randint(0, 120), random.randint(0, 40))
draw.line([start, end], fill=(random.randint(0, 200), random.randint(0, 200), random.randint(0, 200)), width=2)
# 4. 绘制字符 (注意字体路径,Windows用arial.ttf,Linux需下载)
font = ImageFont.truetype("arial.ttf", size=30)
for i, char in enumerate(code):
x = 10 + i * 25 + random.randint(-3, 3) # 位置随机偏移
y = random.randint(2, 10)
draw.text((x, y), char, fill=(random.randint(0, 100), random.randint(0, 100), random.randint(0, 100)), font=font)
# 5. 添加噪点
for i in range(50):
draw.point((random.randint(0, 120), random.randint(0, 40)), fill=(random.randint(0, 255), random.randint(0, 255), random.randint(0, 255)))
# 6. 保存到内存字节流
buf = io.BytesIO()
img.save(buf, format='PNG')
img_data = buf.getvalue()
return code, img_data
@app.route('/captcha')
def get_captcha():
code, img_data = generate_captcha()
session['captcha_code'] = code # 存入服务器session
response = make_response(img_data)
response.headers['Content-Type'] = 'image/png'
return response
@app.route('/login', methods=['POST'])
def login():
user_input = request.form.get('captcha_input', '').upper()
real_code = session.pop('captcha_code', None) # 取出后立刻删除
if not real_code:
return '验证码已过期', 400
if user_input != real_code:
return '验证码错误', 400
return '验证成功!'
Java(Spring Boot + EasyCaptcha)
比自研更推荐使用 EasyCaptcha 库,效率高,开箱即用,在 pom.xml 中添加依赖:
<dependency>
<groupId>com.github.whvcse</groupId>
<artifactId>easy-captcha</artifactId>
<version>1.6.2</version>
</dependency>
import com.wf.captcha.SpecCaptcha;
import com.wf.captcha.base.Captcha;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class CaptchaController {
@GetMapping("/captcha")
public void captcha(HttpServletResponse response, HttpSession session) throws Exception {
// 1. 设置响应头
response.setContentType("image/png");
response.setHeader("Pragma", "No-cache");
response.setHeader("Cache-Control", "no-cache");
response.setDateHeader("Expires", 0);
// 2. 生成验证码 (宽度160, 高度50, 字符数4)
SpecCaptcha captcha = new SpecCaptcha(160, 50, 4);
captcha.setCharType(Captcha.TYPE_ONLY_NUMBER); // 可选:纯数字、纯字母、数字字母混合
// 3. 存储验证码到Session
session.setAttribute("captcha_code", captcha.text().toLowerCase()); // 注意大小写
// 4. 输出图片流
captcha.out(response.getOutputStream());
}
}
前端展示与交互(通用HTML/JS)
不论是哪种后端方案,前端代码基本一致:
<form id="loginForm" action="/login" method="post">
<div>
<label>用户名:<input type="text" name="username"></label>
</div>
<div>
<label>验证码:<input type="text" name="captcha_input" placeholder="请输入上图字符" required></label>
<!-- 验证码图片,点击可刷新 -->
<img id="captcha_img" src="/captcha" alt="验证码" onclick="this.src='/captcha?'+Math.random()" style="cursor:pointer;">
<span onclick="document.getElementById('captcha_img').src='/captcha?'+Math.random()" style="cursor:pointer; color:blue;">换一张</span>
</div>
<button type="submit">提交</button>
</form>
重要安全加固建议(生产环境必做)
- 复杂化处理:
- 扭曲变形:对字符进行旋转、波浪形扭曲(Pillow有
transform方法可做)。 - 干扰背景:使用随机颜色、渐变、网格线而非纯色。
- 字体随机:随机从多个字体文件中选取。
- 扭曲变形:对字符进行旋转、波浪形扭曲(Pillow有
- 防自动化识别:
- 复杂度:字符粘连在一起(切割难度大增)、加入噪点块。
- 速度限制:对同一个IP的请求
/captcha做限速(例如1秒内最多5次)。 - 一次性使用:验证码校验成功后,必须立即从Session/Redis中删除,绝对不允许复用。
- 防暴力破解:
- 时效性:验证码有效期通常设为2-5分钟。
- 尝试次数:用户登录失败3次后,前端必须手动刷新验证码(并重置服务端状态)。
- 会话绑定:将验证码与Session ID或用户IP绑定,防止A用户的验证码被B用户使用。
- 前端安全:
- 计算
/?t=timestamp加时间戳,或者直接用Math.random()加上去,防止图片缓存导致用户一直看到旧code。
- 计算
进阶替代方案
如果觉得传统图形验证码不安全或用户体验不好,可以考虑:
- 行为验证:极验、腾讯防水墙等,用户只需点击“我不是机器人”或拖动滑块,体验更好,安全性显著提升,集成方式主要是前端引用SDK + 后端调用API校验。
- Google reCAPTCHA v3:无感验证,根据用户行为给一个风险分数(0.0-1.0),完全无需用户输入字符。
- 短信/邮箱验证码:更强的身份验证机制,常用于关键操作(修改密码、大额转账)。
总结建议
- 如果是学习/小型项目:用Pillow或EasyCaptcha自建即可。
- 如果是正式商业项目:不要自己造轮子处理图形验证码(识别率低、安全性难保障),推荐集成第三方安全服务(如极验3.0)的前端行为验证 + 后端一次校验。
- 如果是要求极低但需要图片:使用开源项目调整参数即可。
标签: 搭建工具
版权声明:NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播