简介:jQuery各种验证码合集完整覆盖了七类常见验证码方案:随机字符、滑块、拼图、图片旋转角度、文字顺序点、选公式计算与图片对象识别。它主要面向网页前端开发者和安全测试人员,解决登录、注册、评论等场景下的自动化机器人骚扰问题,也可作为培训机构或个人学习前端交互的配套素材。压缩包共有九十五个文件,核心代码包括三十五个脚本逻辑文件、十三个可直接运行的页面示例,以及样式表、测试图片、字体文件和数据配置等资源,整体体积只有二点九三兆字节,下载后即可在浏览器中查看效果。每种验证码按独立目录组织,配有示例页面与脚本注释,可重点学习随机字符生成、滑块拖动、图片旋转、点击顺序校验、四则运算公式计算,以及图片对象识别与服务端算法的对接思路。当前已有一百八十六人学习,适合希望快速理解各种人机校验原理、并能基于现成代码二次集成的开发者作为起步参考。
1. 一个 jQuery 验证码合集,能解决什么
有一类旧系统的改造清单里,登录页验证码往往排在“必须动但不想大动”的位置。这个资源包里集成了随机字符、滑块、拼图、图片旋转、文字顺序点、算式计算和图片对象识别七种验证码,全部基于 jQuery 写成,没有后端依赖。对于技术栈锁定在 jQuery 的存量项目,这套合集能在一小时内完成验证码的渐进替换,不用为单个机械请求写一套后端生成逻辑。
它虽然不像现代 SaaS 验证码那样带风控大屏,但胜在代码可读、插件独立、方便按项目裁剪。我通常用它做两类事:一是老业务后台的临时加固,二是给新团队讲“验证码到底拦的是什么”的活教材。下面直接以压缩包里的 index.html 和 12 个 JS 插件为主线,把每一类验证码的实现模型和验证参数拆开讲。
2. 随机字符与算式验证码:基础型验证码的前端生成细节
这一章讲两类最轻量的动态验证码:随机字符和算式计算。它们在 yanzhengma 合集里分别对应 1jqueryCode 和 11jquery_digital_verification 这类插件。先说不依赖图片,只靠 DOM 或 Canvas 就能完成的基础交互。
2.1 随机字符生成:随机源与字符集的取舍
随机字符验证码的核心是生成一段难以预测的字符串,难点不在生成,而在摒弃易混淆字符。常见做法是去掉 0/O、1/I/l,避免用户一眼看错导致反复刷新。我一般用下面的生成函数:
function randomCode(count) {
var chars = 'ABCDEFGHJKLMNPQRSTUVWXYZ23456789';
var len = chars.length;
var code = '';
for (var i = 0; i < count; i++) {
code += chars.charAt(Math.floor(Math.random() * len));
}
return code;
}
这段代码先定义了 32 个可用字符,通过 Math.random() 生成 [0,1) 随机数,再乘以字符长度取整到下标。Math.random() 不是密码学安全随机数,不能用于令牌生成;验证码场景足够,但如果要防高强度付费攻击,建议换成 crypto.getRandomValues()。生成结果要连同时间戳存到全局变量,用户在页面上填完之后,前端只需要做大小写不敏感的字符串比较。
刷新逻辑一般在验证码区域的点击事件上绑定,实际上就是清空容器并把新值填入对应 DOM。这里还要注意:字符串比较别用 toUpperCase() 直接改原值,正确做法是复制一份比较,原值保留供后端二次校验。通常我还会加一点噪声干扰,比如画几条随机线条,参考资源里 4jqueryyanzheng 的做法就是这类。
2.2 算式验证码:生成运算式并处理优先级
算式验证码比纯字符更友好,用户不需要辨识扭曲字母,只需要计算简单算术。资源里 10jqueryHdyz 就是这类。关键是随机生成算式时,减法结果不能为负,乘法参数不宜太大。
function makeFormula() {
var a = Math.floor(Math.random() * 9) + 2;
var b = Math.floor(Math.random() * 9) + 2;
var op = ['+', '-', '*'][Math.floor(Math.random() * 3)];
if (op === '-' && a < b) {
var t = a; a = b; b = t;
}
var answer;
switch (op) {
case '+': answer = a + b; break;
case '-': answer = a - b; break;
case '*': answer = a * b; break;
}
return { text: a + ' ' + op + ' ' + b + ' = ?', answer: answer };
}
这里没有用 eval,而是用 switch 去匹配运算,避免字符串拼接操作符带来的注入风险。注意 a、b 都限制在 2-10 之间,这样心算成本低;如果需要更高强度,可以调整范围。算式的 text 直接放页面,answer 放在闭包变量里,不能用 data- 属性暴露,否则浏览器 DevTools 一查就破。
2.3 动态刷新与验证状态管理
不管是字符还是算式,都涉及“输入错误后刷新”的链路。我处理时会统一封装一个验证对象,内部持有 code/answer、过期时间、错误次数。前端比较通过只提示“本地通过”,真正的入场凭证还是要交给后端重新比对。下表列出两者的实现差异,方便决定上线时选哪个。
| 类型 | 生成成本 | 用户耗时 | 常见破解方式 | 建议防破解手段 |
|---|---|---|---|---|
| 随机字符 | 低,纯 JS | 4-8 秒 | OCR 识别 | 加干扰线+扭曲字体 |
| 算式计算 | 低,纯 JS | 3-6 秒 | DOM 提取 text 正则 | 算式和答案分开传后端 |
所以基础型验证码适合做低频接口的闸门。资源中 1jqueryCode 和 11jquery_digital_verification 的区别主要在于一个画在 Canvas 上、一个直接用文字节点,Canvas 方式抗 DOM 抓取更强。如果后端没有额外接口,建议至少把答案做一次简单的对称加密,别明晃晃放在全局变量里。
3. 滑块与拼图验证码:拖拽交互、轨迹采集与误差判定
这两类是目前最常见的交互式验证码。合集中 7jquerydragverify 和 9slideVerify 对应滑块,5jigsaw 和 6jqslideImage 对应拼图。它们的共同特点是:用户需要把某个元素拖到特定位置,而校验点不在最终位置,也在于拖动过程。
3.1 滑块验证码:别把目标距离写死在前端
滑块验证码的基本交互是按住滑块,沿轨道拖动到缺口位置。资源里 9slideVerify 的实现方式是监听鼠标在滑块上的 mousedown,随后在 document 上跟踪 mousemove,最后 mouseup 时判断。一个可运行的最小模型如下:
var startX = 0, targetX = 120, trail = [];
$(document).on('mousedown', '#slider-btn', function(e) {
startX = e.clientX;
$(document).on('mousemove.slide', function(ev) {
var dx = ev.clientX - startX;
dx = Math.max(0, Math.min(dx, 280)); // 限制在轨道内
$('#slider-btn').css('left', dx + 'px');
trail.push({ x: dx, t: Date.now() });
});
}).on('mouseup.slide', function() {
$(document).off('mousemove.slide').off('mouseup.slide');
var dx = parseInt($('#slider-btn').css('left'), 10);
if (Math.abs(dx - targetX) < 5) {
$('#result').text('本地校验通过');
submitSliderTrail(trail, dx);
} else {
$('#result').text('位置不对');
}
});
这段代码的关键有三处。第一,mousemove 绑定在 document 上,而不是滑块元素,是因为鼠标拖出滑块范围后仍然要跟踪位移。第二,位移限制在 0 到 280 之间,这是一个示例轨道宽度,真实项目应从服务端配置或从容器宽度计算。第三,trail 数组记录每次位移和时间戳,这是后面交给服务端做风控的原始材料,前端不能自校验通过就算数。
目标距离 targetX 不应写死在 JS 里,常规做法是服务端返回正确的缺口位置,并额外返回一个容差范围。例如滑块总长度 280 像素时,服务端返回 expectX=118,容差 ±5。前端只负责把用户拖到的位置上报,校验交给服务端。
3.2 拼图验证码:切割方式与位置还原逻辑
拼图验证码在合集中有 5jigsaw 和 6jqslideImage。前者把图片切割成多个方块,让用户按顺序点击还原;后者更像是滑块和拼图的结合,拖动一块拼图到阴影位置。两者的核心逻辑相同:记录用户拼图的坐标和正确坐标的偏差。
如果完全自己实现,使用 jQuery UI 的 draggable/droppable 可以快速验证,但资源本身是自写绑定,不依赖 jQuery UI。实现思路如下:
var correctTop = 55; // 拼图目标位置
$('.puzzle-piece').on('dragstart', function() {});
$('.puzzle-piece').on('drag', function(e) {
// 将元素位置绑定到鼠标
var piece = $(this);
piece.css({
left: piece.position().left + e.originalEvent.movementX,
top: piece.position().top + e.originalEvent.movementY
});
});
$('.puzzle-piece').on('dragend', function() {
var top = parseInt($(this).css('top'), 10);
if (Math.abs(top - correctTop) < 8) {
$(this).addClass('settled');
}
});
不过原生 drag 事件在移动端支持并不周全,资源里的实现大多是 mousedown + mousemove 模拟拖动。这里要特别说明:dragstart/dragend 是在 HTML5 拖放 API 上的,jQuery 事件绑定可以直接接,但是拖到图片边界时浏览器会显示拖放禁止图标,所以在真实插件中更多的是自己监听坐标。拼图验证码的校验因子比纯滑块多一个维度:水平和垂直方向都要对齐。
3.3 服务端校验参数阈值参考
前端可以判接近,但后端必须重新验。这里把滑块和拼图常用参数列成一张表,供接入时做阈值配置。
| 校验项 | 推荐阈值 | 说明 |
|---|---|---|
| 最终位移误差 | 5-10 px | 小于该值认为位置匹配 |
| 轨迹点数 | 10-30 个 | 一般实现样本不宜过少 |
| 从按下到松开耗时 | 500-6000 ms | 太快会被判定为脚本 |
| 位移变化率峰值 | 80-300 px/s | 模拟人类拖动的加速度曲线 |
阈值需要根据真实用户数据分布去调整,不能照抄。比如移动端触屏拖动的耗时普遍比鼠标长,轨迹点也更稀疏。这部分验证码的目的只是提高攻击成本,并不能彻底阻断,因此我在生产环境中还要叠加 IP 频率限制。
4. 图片旋转与文字顺序点验证码:特殊交互的姿态与序列校验
这两种验证码在合集包里对应 3jqRotateVerify 和 10jqueryHdyz 附近的模块名称。它们把难度从“输入字符串”变成“还原一个视觉状态或点击序列”,对机器人来说,难点是理解图像而不是字符。
4.1 图片旋转验证码:角度差归一化
图片旋转验证码会显示一张被旋转过的图片,用户通过点击“左转/右转”按钮把图摆正。资源里 3jqRotateVerify 的实现方式是 CSS transform: rotate(deg),每次点击角度值变化固定步长,比如 15 度。验证时比较用户最终角度和真实角度的差。
var realAngle = 145;
var currentAngle = 0;
function checkAngle(userAngle) {
var diff = Math.abs(userAngle - realAngle) % 360;
if (diff > 180) diff = 360 - diff;
return diff <= 4;
}
这里角度差的处理比较关键:0 度和 359 度只差 1 度,但直接相减会得到 358 度。所以先对 360 取模,再把超过 180 的差值转为反向差值。这个技巧在处理任意角度校验时都会用到。步长如果太小,用户操作很累,一般 5-10 度合适;如果太大,容差要放大,否则很容易失败。
旋转角度前存储真实角度时,不要直接放在 DOM 的 data 属性里,因为在浏览器控制台能直接看到。比较稳的做法是生成一个随机 crypto token,角度由 token 从服务端换;这是资源和生产系统的一个差距,需要自己补。
4.2 文字顺序点验证码:点击序列的录入与比对
文字顺序点验证码,展示一组乱序文字,要求按指定顺序点击。合集中的 10jqueryHdyz 是一款类似能力插件。实现时,需要维护两个数组:期望顺序数组和用户点击数组。点击过的按钮要置灰,避免重复点击。
var expected = ['蓝', '黄', '红'];
var userSeq = [];
$('.text-item').on('click', function() {
if ($(this).hasClass('clicked')) {
return;
}
userSeq.push($(this).text());
$(this).addClass('clicked').css('opacity', 0.4);
if (userSeq.length === expected.length) {
var pass = expected.every(function(v, i) { return v === userSeq[i]; });
// 不管通过与否,重置状态
userSeq = [];
$('.text-item').removeClass('clicked').css('opacity', 1);
}
});
这个写法里把验证状态和 DOM 状态混在一起,适合小 demo。真实场景下不应在点击完成后立刻清空顺序,而应先提交给后端。expected 顺序用服务端返回的随机 id 关联,而不是字面量,否则用鼠标悬停看 DOM 就能猜出顺序。注意:这类验证码和资源里 2jquery-gesturepw 手势密码的校验模型一样,用户画完手势后,后端要校验顺序而不是最终坐标。
4.3 交互复杂度与安全等级对比
这两种验证码可以共用一套后端校验接口:提交一个 session 标识 + 用户操作轨迹 + 最终值。它们的安全性差异在于前置理解成本。
| 类型 | 用户理解成本 | 破解维度 | 抗 OCR 能力 | 移动端适配 |
|---|---|---|---|---|
| 图片旋转 | 中 | 需要图像理解 | 中 | 低(点按钮还好) |
| 文字顺序点 | 低 | 容易用 OCR 提取文本 | 中高 | 高 |
此外,文字顺序点的期望顺序列表最好每次都从服务端获取,不要缓存在前端。资源目录里 data-*.json 文件存放了一些预置数据,直接引用虽然方便,但部署到公网时这些 JSON 是可下载的,等于把答案泄露了。
5. 图片对象识别验证码:前端坐标采集与后端 AI 校验
最后一种是图片对象识别验证码,也叫“点选图片验证码”。页面显示一张图,包含多个目标物体,用户需要根据提示点击其中所有目标。这种验证码需要结合后端图像识别,前端只做展示和坐标采集。
5.1 前端点击坐标的归一化处理
前端在图片上覆盖一个透明容器,监听点击的位置。由于图片在不同设备上会缩放,坐标应转换成相对值,以图片宽高为基准。
$('#object-map').on('click', function(e) {
var rect = this.getBoundingClientRect();
var relX = (e.clientX - rect.left) / rect.width;
var relY = (e.clientY - rect.top) / rect.height;
clicks.push({
x: Number(relX.toFixed(4)),
y: Number(relY.toFixed(4)),
t: Date.now()
});
// 在点击处画一个圆点,作为用户反馈
var dot = $('<span class="click-dot"></span>');
dot.css({
left: relX * rect.width + 'px',
top: relY * rect.height + 'px'
});
$(this).append(dot);
});
这段代码每次都读取 getBoundingClientRect(),是因为页面滚动或图片大小变化后,它提供的值是实时的。x、y 保留四位小数,既保证准确度,又不至于让坐标变成一个可以被普通后台上限限制的长浮点数。要特别注意:相对坐标必须以原始图片尺寸为基准,如果图片是 background-size: cover 或 object-fit: cover,会引入偏移,最好用一张固定尺寸的定位图。
5.2 服务端识别与免费 API 的接入形态
服务端拿到前端提交的坐标后,需要判定这些坐标是否命中目标区域。轻度场景可以在后端预置热区 JSON,也就是把对象轮廓的顶点坐标存起来。更通用的是用训练好的目标检测模型,比如把图片送给服务端推理,返回每个目标的中心点和 bbox,再和用户点击坐标做包含关系判断。如果不想自己训练,国内有一些免费的验证码 API 可以直接收图片和坐标,返回通过与否;接入时注意用签名认证,不要让接口被直接刷。
这类验证码的校验必须用一次性 token。前端在页面加载时向服务端申请一个 captchaId,提交答案时带上 captchaId,服务端只接受一次校验,这样防止把同样坐标反复提交。合集中的 12verify 插件就留了这样一个接口位,读代码时会看到它要求外部传入 verifyUrl。
5.3 前后端分工与接口参数设计
图片对象识别验证码是整个合集里唯一必须前后端协作的类型。前端负责交互,后端负责判定,两者通过一组明确参数衔接。
| 参数名 | 类型 | 说明 |
|---|---|---|
| captchaId | string | 一次性验证码标识,服务端签发 |
| clickList | array | 点击坐标序列,每个元素含 x、y、t |
| elapsed | number | 从加载到提交的总耗时,单位 ms |
| userAgent | string | 由前端采集,后端日志用 |
把点击坐标放进 clickList 而不是单次提交,是为了支持“点击多个对象”的场景。elapsed 参数在服务端可以做速度过滤,真实用户一般需要 2-10 秒,少于 1 秒的高频提交可以直接拒绝。
6. 验证码合集的定制技巧与常见排错
把 12 个插件放进同一个页面,最容易出问题的就是 jQuery 插件命名冲突、图片路径和移动端事件。最后分享几个我反复踩过的点。
6.1 用 IIFE 包装插件避免全局污染
资源里的插件大多定义成 $.fn.extend,如果同时引用多个插件且内部变量名一致,后加载的会覆盖前面的。建议在使用前统一改成 IIFE 包装:
(function($) {
$.fn.codeCaptcha = function(options) {
var settings = $.extend({}, {
length: 4,
useCanvas: true
}, options);
// 具体实现
return this;
};
})(jQuery);
这样每个插件独立作用域,同时保留链式调用。如果你在同一个页面用了不止一个验证码插件,开局就做这件事能省掉很多排查时间。
6.2 图片路径以实际部署为准
index.html 直接引用 images 目录,如果部署到子目录,图片路径会全部失效。看一下网络请求,404 大多出现在图片加载上。解决办法是把图片合并成一张雪碧图,或让后端接口动态返回图片 URL。另外这些图片本身都是静态资源,如果被爬虫直接拿到原图,对象识别验证码的难度会下降,生产环境建议用后端按会话拼接图片。
6.3 移动端鼠标事件失效
滑块和拼图验证码在移动端不会触发 mousedown,需要监听 touchstart/touchmove/touchend。简单处理是把鼠标事件换成 pointer 事件,或同时绑定,但要注意 event.clientX 在 touch 对象里是 event.touches[0].clientX。我给插件加一个 isTouch 判断,再绑定不同事件,是移植到移动端的第一步。
| 现象 | 原因 | 处理 |
|---|---|---|
| 验证码图片显示不出来 | 路径错误或图片被拦截 | 修正相对路径,或改用 base64 内联 |
| 拖到一半滑块按钮丢失 | mouseup 事件未解绑 | 确认在 document 上绑定,及时 off |
| jQuery 其他插件报错 | noConflict 冲突 | 用 jQuery.noConflict() 隔离 |
| 移动端点击无反应 | touch 事件未绑定 | 补充 touchstart/touchend |
在实际改造里,我通常最后把验证结果用一个隐藏 input 带回表单,提交时和后端 session 数据一起核验,这一步做完,整套验证码才真正算接进业务流程。
转载自 CSDN-专业IT技术社区
原文链接:https://blog.csdn.net/weixin_31240035/article/details/165316147



