@sbsbok/slider-captcha
v1.0.0
Published
A lightweight slider captcha component for web applications with PC and mobile support
Maintainers
Readme
Slider Captcha 滑块式验证码
English | 中文
项目概述
Slider Captcha 是一款轻量级的滑块式验证码组件,支持 PC 端及移动端 Web 应用。用户通过拖动滑块完成拼图验证,组件会记录拖动行为的时间、精度和滑动轨迹等信息,可发送至服务器进行后台算法验证,有效防止自动化脚本攻击。
本项目基于 TypeScript 开发,提供简洁的 API 接口,易于集成到各类 Web 项目中。
核心功能与特性
- 拖动验证:用户通过拖动滑块填充拼图完成校验
- 轨迹检测:记录用户滑动轨迹,可用于后台行为分析
- 响应式设计:完美适配 PC 端和移动端设备
- 触摸支持:支持 touch 事件,适配移动端浏览器
- 多种验证模式:支持本地验证和远程服务器验证
- 灵活配置:提供丰富的配置选项,可自定义外观和行为
- 图片加载容错:支持远程图片和本地备用图片自动切换
- Canvas 渲染:使用 Canvas 绘制拼图和滑块,效果清晰
环境要求
- 现代浏览器(Chrome、Firefox、Safari、Edge 等)
- 支持 HTML5 Canvas API
- 支持 ES6+ JavaScript
- 可选:jQuery(仅在使用默认远程验证功能时需要)
安装与配置
方式一:npm / pnpm 安装(推荐)
# 使用 npm 安装
npm install @sbsbok/slider-captcha
# 或使用 pnpm 安装
pnpm add @sbsbok/slider-captcha在项目中引入
import SliderCaptcha from '@sbsbok/slider-captcha';
import '@sbsbok/slider-captcha/dist/slidercaptcha.min.css';方式二:直接引入(适用于原生 HTML 项目)
1. 引入 CSS 样式
<link rel="stylesheet" href="./src/slidercaptcha.min.css" />将上述代码添加到 <head> 标签中,并确保在其他样式表之前引入。
2. 引入 JavaScript 脚本
<script src="./src/longbow.slidercaptcha.ts"></script>将脚本标签放置在 </body> 之前。
方式三:在 Vue / React / TypeScript 项目中使用
import SliderCaptcha from '@/common/longbow.slidercaptcha';
import '@/common/slidercaptcha.min.css';快速开始
基础用法
在页面中添加验证码容器元素:
<div id="captcha"></div>通过 JavaScript 初始化验证码:
sliderCaptcha({
id: 'captcha',
});在 Vue 3 中的使用示例
<template>
<div class="card-body relative">
<div v-if="showCaptcha" class="absolute w-full h-full top-[50%] left-[50%] translate-x-[-50%] translate-y-[-50%] z-50">
<el-icon class="absolute text-center cursor-pointer top-[10%] left-[75%] z-80" color="black" size="35" @click="refresh">
<Refresh />
</el-icon>
<div id="captcha" class="bg-white rounded-lg shadow-xl p-4"></div>
</div>
</div>
</template>
<script setup lang="ts">
import { ref, nextTick } from 'vue';
import SliderCaptcha from '@/common/longbow.slidercaptcha';
const showCaptcha = ref(true);
const captcha = ref<SliderCaptcha | null>(null);
const initCaptcha = async () => {
await nextTick();
captcha.value = new SliderCaptcha(document.querySelector('#captcha')!, {
width: 280,
height: 150,
sliderL: 42,
sliderR: 9,
offset: 5,
loadingText: '正在加载中...',
failedText: '再试一次',
barText: '向右滑动填充拼图',
repeatIcon: 'iconfont iconfont-shuaxin',
onSuccess: () => {
console.log('验证成功');
showCaptcha.value = false;
captcha.value = null;
},
onFail: () => {
console.log('验证失败');
},
onRefresh: () => {
console.log('验证码刷新');
},
});
};
const refresh = () => {
captcha.value?.reset();
};
initCaptcha();
</script>API 接口说明
配置选项(Options)
| 参数 | 类型 | 默认值 | 说明 |
| -------------- | -------- | --------------------------- | ------------------------------------------------- |
| id | string | 必填 | 容器元素的 ID |
| width | integer | 280 | 验证码背景图片宽度(单位:像素) |
| height | integer | 155 | 验证码背景图片高度(单位:像素) |
| sliderL | integer | 42 | 滑块拼图宽度(单位:像素) |
| sliderR | integer | 9 | 滑块拼图突出半径(单位:像素) |
| offset | integer | 5 | 验证容错偏差值,默认为 5 个像素偏差即认为验证通过 |
| loadingText | string | "正在加载中..." | 图片加载过程中显示的文本信息 |
| failedText | string | "再试一次" | 验证失败时显示的文本信息 |
| barText | string | "向右滑动填充拼图" | 拖动滑块前显示的提示文本信息 |
| repeatIcon | string | "iconfont iconfont-shuaxin" | 刷新按钮图标类名,需引用对应的图标库 |
| maxLoadCount | integer | 3 | 图片最大加载重试次数 |
| setSrc | function | 随机图片 | 设置图片加载路径,返回图片 URL |
| localImages | function | 本地备用图片 | 图片加载失败时调用的回调,返回本地备用图片路径 |
| remoteUrl | string | null | 服务器端验证请求地址,设置为 null 则使用本地验证 |
| verify | function | 本地验证逻辑 | 自定义服务器端验证方法 |
| onSuccess | function | - | 验证成功时的回调函数,参数为滑动轨迹数组 |
| onFail | function | - | 验证失败时的回调函数 |
| onRefresh | function | - | 点击刷新按钮时的回调函数 |
使用示例
sliderCaptcha({
id: 'captcha',
width: 280,
height: 150,
sliderL: 42,
sliderR: 9,
offset: 5,
loadingText: '正在加载中...',
failedText: '再试一次',
barText: '向右滑动填充拼图',
repeatIcon: 'iconfont iconfont-shuaxin',
setSrc: function () {
return 'https://imgs.blazor.zone/images/Pic' + Math.round(Math.random() * 136) + '.jpg';
},
onSuccess: function (trail) {
console.log('验证成功,滑动轨迹:', trail);
},
onFail: function () {
console.log('验证失败');
},
onRefresh: function () {
console.log('验证码已刷新');
},
});方法(Methods)
| 方法名 | 说明 |
| --------- | ------------------------------------------ |
| reset() | 重置验证码组件,重新加载图片并恢复初始状态 |
使用示例
// 初始化验证码
let captcha = sliderCaptcha({
id: 'captcha',
});
// 点击刷新按钮时调用
const refresh = () => {
captcha.reset();
};服务器端验证
工作原理
客户端在用户完成拖动操作后,会将滑动轨迹数据(trail 数组)发送到服务器进行验证。服务器通过分析轨迹数据的离散程度来判断是否为人为操作——人类拖动过程中会产生自然的 Y 轴抖动,而机器自动化操作的轨迹通常较为规律。
客户端配置
配置 remoteUrl 和 verify 两个选项即可启用服务器端验证:
sliderCaptcha({
id: 'captcha',
repeatIcon: 'iconfont iconfont-shuaxin',
setSrc: function () {
return 'https://imgs.blazor.zone/images/Pic' + Math.round(Math.random() * 136) + '.jpg';
},
onSuccess: function () {
console.log('验证成功');
},
remoteUrl: '/api/Captcha/Verify',
verify: function (trail, remoteUrl) {
let result = false;
$.ajax({
url: remoteUrl,
data: JSON.stringify(trail),
async: false,
cache: false,
type: 'POST',
contentType: 'application/json',
dataType: 'json',
success: function (data) {
result = data;
},
});
return result;
},
});参数说明
| 参数 | 类型 | 说明 |
| ----------- | -------- | ----------------------------- |
| trail | number[] | 客户端拖动滑块的 Y 轴轨迹数组 |
| remoteUrl | string | 服务器端验证请求地址 |
服务器端代码示例
.NET Core WebApi
using Microsoft.AspNetCore.Mvc;
[Route("api/[controller]")]
[ApiController]
[AllowAnonymous]
public class CaptchaController : ControllerBase
{
[HttpPost]
public bool Verify([FromBody] List<int> trail)
{
if (trail == null || trail.Count == 0)
return false;
var sum = trail.Sum();
var avg = sum * 1.0 / trail.Count;
var stddev = trail.Select(v => Math.Pow(v - avg, 2)).Sum() / trail.Count;
return stddev != 0;
}
}Java Spring Boot
@RestController
@RequestMapping("/sliderCaptcha")
public class SliderCaptchaController {
@PostMapping("/verify")
public boolean verify(@RequestBody List<Integer> trail) {
if (trail == null || trail.isEmpty()) {
return false;
}
int sum = 0;
for (Integer data : trail) {
sum += data;
}
double avg = sum * 1.0 / trail.size();
double sum2 = 0.0;
for (Integer data : trail) {
sum2 += Math.pow(data - avg, 2);
}
double stddev = sum2 / trail.size();
return stddev != 0;
}
}验证算法说明
服务器端验证的核心原理是基于滑动轨迹的方差计算:
- 计算平均值:求出轨迹数组所有 Y 轴偏移值的平均值
- 计算方差:求出每个值与平均值的偏差的平方和,再除以样本数量
- 判断结果:如果方差为 0,说明 Y 轴没有偏移(机器操作),返回
false;如果方差不为 0,说明有人为抖动(人为操作),返回true
常见问题
Q: 服务器端验证返回结果为什么一直都是 true?
A:示例代码中使用 Y 轴平方差进行校验。当方差为 0 时返回 false,否则返回 true。
返回 true 表示 Y 轴存在偏移,系统认为此操作是人为操作。因为人手拖动过程中必然会产生 Y 轴抖动,而机器自动化操作的轨迹理论上不会有 Y 轴偏移。因此可以根据此返回值判断是否为真实用户操作。
Q: 图片加载失败怎么办?
A:组件内置了图片加载容错机制。当远程图片加载失败次数超过 maxLoadCount(默认 3 次)后,会自动调用 localImages 配置的回调函数加载本地备用图片。默认本地图片路径为 images/Pic0.jpg 至 images/Pic4.jpg。
Q: 如何自定义图片源?
A:通过 setSrc 配置项自定义图片加载逻辑:
sliderCaptcha({
id: 'captcha',
setSrc: function () {
return 'https://your-image-server.com/images/' + Math.round(Math.random() * 100) + '.jpg';
},
});Q: 验证失败后如何自动重置?
A:组件会在验证失败 1 秒后自动重置。如需手动控制重置,调用 reset() 方法即可。
发布 npm 包
准备工作
- 注册 npm 账号:https://www.npmjs.com/signup
- 在本地登录 npm:
npm login - 确保 package.json 中的包名唯一(当前使用
@sbsbok/slider-captcha)
发布流程
# 1. 安装依赖
npm install
# 2. 构建包
npm run build
# 3. 添加npm用户身份(如果没有登录)
npm login
# 4. 发布包(scoped 包需要添加 --access public)
npm publish --access public版本管理
# 更新补丁版本(1.0.0 -> 1.0.1)
npm version patch
# 更新小版本(1.0.0 -> 1.1.0)
npm version minor
# 更新大版本(1.0.0 -> 2.0.0)
npm version major发布后验证
# 在项目中测试安装
npm install @sbsbok/slider-captcha参与贡献
欢迎提交 Issue 和 Pull Request 来帮助改进本项目。
贡献流程
- Fork 本项目
- 创建功能分支 (
git checkout -b feature/AmazingFeature) - 提交更改 (
git commit -m 'Add some AmazingFeature') - 推送到分支 (
git push origin feature/AmazingFeature) - 创建 Pull Request
许可证
本项目遵循 MIT 许可证。
联系方式
- 邮箱:[email protected]
- 项目地址:https://gitee.com/LongbowEnterprise/SliderCaptcha
如有问题或建议,请在 Issue 页面提交。
