站点停靠验证 (CheckpointCaptcha)
基于按压蓄力的交互式验证码。用户需要将滑块拖动到指定站点,并长按进行"充电",待电量充满(变绿)后即可完成验证。这种交互方式能有效采集用户的按压时长和微小抖动,极大提高人机识别准确率。
交互流程
- 用户拖动滑块至中间的"蓝色站点"位置。
- 按住鼠标左键(或手指不离开屏幕),此时会出现充电进度提示。
- 当圆圈变绿(充电完成)后,物理阻挡消失。
- 继续向右拖动滑块至终点释放,完成验证。
基础用法
html
<div id="captcha-container"></div>
<script>
// 创建 CheckpointCaptcha 实例
const checkpoint = new WlzShield.CheckpointCaptcha({
siteKey: 'your-site-key', // 必填:网站唯一标识
containerId: '#captcha-container', // 必填:挂载容器ID
width: '100%' // 组件宽度(默认100%)
});
// 可选:监听验证结果
checkpoint.config.onSuccess = function (data) {
console.log('验证成功:', data);
// 将验证凭证提交到您的后端
};
</script>在线体验
配置参数 (API)
基础配置
| 参数名 | 类型 | 默认值 | 是否必填 | 说明 |
|---|---|---|---|---|
siteKey | string | - | 必填 | 网站唯一标识,用于区分不同站点的验证请求 |
containerId | string | - | 必填 | DOM 容器 ID 选择器 (如 #captcha-box) |
challengeUrl | string | /api/challenge | 可选 | 后端验证接口地址 |
width | number | string | '100%' | 可选 | 组件宽度,支持数字(px)或CSS字符串(如 '100%', '320px') |
lang | string | zh | 可选 | 语言设置:'zh' (中文) 或 'en' (英文) |
mode | 'embed' | 'popup' | 'embed' | 可选 | 展示模式:'embed' (嵌入式) 或 'popup' (弹窗式) |
timeout | number | 10000 | 可选 | 请求超时时间(毫秒) |
高级配置
| 参数名 | 类型 | 默认值 | 说明 |
|---|---|---|---|
customTexts | Record<string, string> | {} | 自定义文案,覆盖默认的按钮文本、提示信息等 |
customStyles | Partial<CSSStyleDeclaration> | {} | 自定义主容器样式,应用于整个验证码组件 |
popupStyles | Partial<CSSStyleDeclaration> | {} | 仅对popup模式有效,自定义弹窗样式 |
回调函数
| 参数名 | 类型 | 默认值 | 说明 |
|---|---|---|---|
onReady | () => void | undefined | 组件加载完成时触发 |
onOpen | () => void | undefined | 仅popup模式有效,弹窗打开时触发 |
onClose | () => void | undefined | 仅popup模式有效,弹窗关闭时触发 |
onSuccess | (data: any) => void | undefined | 验证通过时触发 |
onFailure | (data: any) => void | undefined | 验证失败时触发(逻辑拒绝,如轨迹异常) |
onError | (error: Error) => void | undefined | 系统/网络错误时触发(如 404, 500, 网络断开) |
详细配置说明
模式选择 (mode)
CheckpointCaptcha 支持两种展示模式:
javascript
// 嵌入式模式(默认):直接在当前容器中显示验证码
const embedCaptcha = new WlzShield.CheckpointCaptcha({
siteKey: 'your-site-key',
containerId: '#captcha-box',
mode: 'embed', // 默认值
width: '100%'
});
// 弹窗式模式:显示触发按钮,点击后弹出验证窗口
const popupCaptcha = new WlzShield.CheckpointCaptcha({
siteKey: 'your-site-key',
containerId: '#captcha-box',
mode: 'popup',
customTexts: {
buttonText: '点击验证' // 弹窗触发按钮文本
}
});自定义文案 (customTexts)
您可以覆盖以下默认文案:
javascript
const checkpoint = new WlzShield.CheckpointCaptcha({
siteKey: 'your-site-key',
containerId: '#captcha-container',
customTexts: {
// 覆盖描述文本(支持HTML)
desc: "拖动滑块到<b>蓝色充电站</b>,按住充电",
// 覆盖充电中文本
charging: "蓄力中",
// 覆盖成功文本
success: "✅ 验证成功",
// 覆盖失败文本
fail: "验证失败",
// 覆盖重试文本
retry: "点击重试",
// 弹窗模式下的按钮文本(仅popup模式有效)
buttonText: "开始验证",
// 弹窗标题(仅popup模式有效)
popupTitle: "安全验证"
}
});自定义样式 (customStyles)
您可以调整验证码组件的外观样式:
javascript
const checkpoint = new WlzShield.CheckpointCaptcha({
siteKey: 'your-site-key',
containerId: '#captcha-container',
mode: 'embed',
customStyles: {
// 修改背景色
backgroundColor: '#f0f9ff',
// 添加边框
border: '1px solid #bae6fd',
// 调整圆角
borderRadius: '12px',
// 调整内边距
padding: '20px',
// 修改字体
fontFamily: '"Segoe UI", system-ui, sans-serif',
// 添加阴影
boxShadow: '0 4px 12px rgba(0,0,0,0.08)'
}
});弹窗样式自定义 (popupStyles)
仅当 mode: 'popup' 时有效:
javascript
const checkpoint = new WlzShield.CheckpointCaptcha({
siteKey: 'your-site-key',
containerId: '#captcha-container',
mode: 'popup',
popupStyles: {
// 弹窗宽度
maxWidth: '420px',
// 弹窗背景色
backgroundColor: '#ffffff',
// 弹窗边框
border: 'none',
// 弹窗圆角
borderRadius: '16px',
// 弹窗阴影
boxShadow: '0 10px 40px rgba(0,0,0,0.15)'
}
});完整示例
示例1:表单验证集成(嵌入式模式)
html
<!DOCTYPE html>
<html>
<head>
<title>站点停靠验证示例</title>
<style>
body {
font-family: Arial, sans-serif;
padding: 40px;
max-width: 500px;
margin: 0 auto;
}
.form-container {
background: #f8fafc;
padding: 30px;
border-radius: 12px;
border: 1px solid #e2e8f0;
}
.form-group {
margin-bottom: 20px;
}
label {
display: block;
margin-bottom: 8px;
font-weight: 600;
color: #334155;
}
input {
width: 100%;
padding: 12px;
border: 1px solid #cbd5e1;
border-radius: 6px;
font-size: 14px;
}
button {
width: 100%;
padding: 14px;
background: #3b82f6;
color: white;
border: none;
border-radius: 8px;
font-size: 16px;
cursor: pointer;
}
button:disabled {
background: #94a3b8;
cursor: not-allowed;
}
#captcha-box {
margin: 25px 0;
}
.hint {
font-size: 12px;
color: #64748b;
margin-top: 5px;
}
</style>
<script src="https://captcha.wangluozhe.com/static/js/vendors.min.js"></script>
<script src="https://captcha.wangluozhe.com/static/js/wlzshield.min.js"></script>
</head>
<body>
<div class="form-container">
<h2>注册账号</h2>
<form id="registerForm">
<div class="form-group">
<label>邮箱地址</label>
<input type="email" id="email" required>
</div>
<div class="form-group">
<label>设置密码</label>
<input type="password" id="password" required>
</div>
<!-- 验证码容器 -->
<div class="form-group">
<label>安全验证</label>
<div id="captcha-box"></div>
<div class="hint">请将滑块拖动到充电站,按住充电直到变绿后继续拖动</div>
</div>
<button type="submit" id="submitBtn" disabled>注册账号</button>
</form>
</div>
<script>
let captchaInstance = null;
let isVerified = false;
let verificationData = null;
// 初始化验证码
captchaInstance = new WlzShield.CheckpointCaptcha({
siteKey: 'your-site-key',
containerId: '#captcha-box',
mode: 'embed',
width: '100%',
customTexts: {
desc: "拖动至<b>蓝色充电站</b>停下充电,变绿后继续"
},
customStyles: {
backgroundColor: '#ffffff',
border: '1px solid #e2e8f0',
borderRadius: '8px',
padding: '15px'
},
onSuccess: function (data) {
console.log('验证成功:', data);
isVerified = true;
verificationData = data;
document.getElementById('submitBtn').disabled = false;
// 显示成功提示
const submitBtn = document.getElementById('submitBtn');
submitBtn.innerHTML = '✓ 验证成功,点击注册';
submitBtn.style.background = '#10b981';
},
onFailure: function (data) {
console.warn('验证失败:', data);
isVerified = false;
document.getElementById('submitBtn').disabled = true;
document.getElementById('submitBtn').innerHTML = '注册账号';
document.getElementById('submitBtn').style.background = '#3b82f6';
// 显示错误提示
alert('验证失败:' + (data.msg || '请重试'));
},
onError: function (error) {
console.error('系统错误:', error);
alert('验证系统错误,请刷新页面重试');
},
onReady: function () {
console.log('站点停靠验证码已加载完成');
}
});
// 表单提交处理
document.getElementById('registerForm').addEventListener('submit', function (e) {
e.preventDefault();
if (!isVerified) {
alert('请先完成安全验证');
return;
}
// 收集表单数据
const formData = {
email: document.getElementById('email').value,
password: document.getElementById('password').value,
// 传递验证数据给后端
captchaData: verificationData
};
console.log('提交注册数据:', formData);
// 这里发送到您的后端进行验证
// 模拟提交
document.getElementById('submitBtn').innerHTML = '注册中...';
document.getElementById('submitBtn').disabled = true;
setTimeout(() => {
alert('注册成功!');
// 重置表单和验证码
document.getElementById('registerForm').reset();
captchaInstance.reset();
isVerified = false;
document.getElementById('submitBtn').innerHTML = '注册账号';
document.getElementById('submitBtn').disabled = true;
document.getElementById('submitBtn').style.background = '#3b82f6';
}, 1000);
});
</script>
</body>
</html>示例2:弹窗模式应用
html
<!DOCTYPE html>
<html>
<head>
<title>弹窗验证示例</title>
<style>
body {
font-family: Arial, sans-serif;
padding: 40px;
}
.comment-box {
max-width: 600px;
margin: 0 auto;
}
textarea {
width: 100%;
height: 100px;
padding: 12px;
border: 1px solid #ddd;
border-radius: 6px;
margin-bottom: 15px;
}
.toolbar {
display: flex;
justify-content: space-between;
align-items: center;
}
button {
padding: 10px 24px;
border: none;
border-radius: 6px;
cursor: pointer;
}
#submitComment {
background: #3b82f6;
color: white;
}
#submitComment:disabled {
background: #94a3b8;
cursor: not-allowed;
}
#captcha-trigger {
background: #f1f5f9;
color: #475569;
border: 1px solid #cbd5e1;
}
</style>
<script src="https://captcha.wangluozhe.com/static/js/vendors.min.js"></script>
<script src="https://captcha.wangluozhe.com/static/js/wlzshield.min.js"></script>
</head>
<body>
<div class="comment-box">
<h2>发表评论</h2>
<textarea id="comment" placeholder="请输入您的评论..."></textarea>
<div class="toolbar">
<button id="captcha-trigger">验证身份</button>
<button id="submitComment" disabled>提交评论</button>
</div>
</div>
<!-- 验证码容器(隐藏,仅用于弹窗模式) -->
<div id="captcha-container" style="display: none;"></div>
<script>
let captchaInstance = null;
let isVerified = false;
// 初始化验证码(弹窗模式)
captchaInstance = new WlzShield.CheckpointCaptcha({
siteKey: 'your-site-key',
containerId: '#captcha-container',
mode: 'popup',
customTexts: {
buttonText: '点击验证',
popupTitle: '安全验证',
desc: "拖动至<b>蓝色充电站</b>停下充电,变绿后继续"
},
popupStyles: {
maxWidth: '380px',
borderRadius: '12px'
},
onSuccess: function (data) {
console.log('验证成功:', data);
isVerified = true;
document.getElementById('submitComment').disabled = false;
document.getElementById('captcha-trigger').innerHTML = '✓ 已验证';
document.getElementById('captcha-trigger').style.background = '#dcfce7';
document.getElementById('captcha-trigger').style.color = '#16a34a';
document.getElementById('captcha-trigger').style.borderColor = '#86efac';
},
onFailure: function (data) {
console.warn('验证失败:', data);
isVerified = false;
document.getElementById('submitComment').disabled = true;
alert('验证失败,请重试');
},
onOpen: function () {
console.log('验证弹窗已打开');
},
onClose: function () {
console.log('验证弹窗已关闭');
}
});
// 触发验证按钮
document.getElementById('captcha-trigger').addEventListener('click', function () {
if (!isVerified) {
// 弹窗模式会自动显示验证窗口
// 验证成功后会触发onSuccess回调
} else {
alert('您已经通过验证');
}
});
// 提交评论
document.getElementById('submitComment').addEventListener('click', function () {
if (!isVerified) {
alert('请先完成身份验证');
return;
}
const comment = document.getElementById('comment').value;
if (!comment.trim()) {
alert('请输入评论内容');
return;
}
console.log('提交评论:', comment);
// 这里发送到后端
// 清空表单
document.getElementById('comment').value = '';
document.getElementById('submitComment').disabled = true;
document.getElementById('captcha-trigger').innerHTML = '验证身份';
document.getElementById('captcha-trigger').style.background = '#f1f5f9';
document.getElementById('captcha-trigger').style.color = '#475569';
document.getElementById('captcha-trigger').style.borderColor = '#cbd5e1';
isVerified = false;
alert('评论提交成功!');
});
</script>
</body>
</html>示例3:与Vue.js集成
vue
<template>
<div class="checkout-page">
<h2>订单支付</h2>
<div class="order-summary">
<p>商品总额:¥{{ totalAmount }}</p>
<p>运费:¥{{ shippingFee }}</p>
<p class="total">应付总额:¥{{ totalAmount + shippingFee }}</p>
</div>
<!-- 验证码区域 -->
<div class="captcha-section">
<h3>安全验证</h3>
<div ref="captchaContainer" class="captcha-box"></div>
<p class="hint" v-if="!verified">请完成验证以继续支付</p>
<p class="hint success" v-else>✓ 验证通过,可以支付</p>
</div>
<button
@click="handlePayment"
:disabled="!verified"
:class="{ 'disabled': !verified }"
>
{{ verified ? '确认支付' : '请先完成验证' }}
</button>
</div>
</template>
<script>
import {ref, onMounted} from 'vue';
export default {
setup() {
const captchaContainer = ref(null);
const verified = ref(false);
const totalAmount = ref(199.99);
const shippingFee = ref(10.00);
let captchaInstance = null;
onMounted(() => {
if (captchaContainer.value) {
// 动态设置containerId
captchaContainer.value.id = 'checkpoint-captcha-' + Date.now();
captchaInstance = new WlzShield.CheckpointCaptcha({
siteKey: 'your-site-key',
containerId: '#' + captchaContainer.value.id,
mode: 'embed',
width: '100%',
customTexts: {
desc: "拖动滑块到<b>蓝色充电站</b>,按住充电直到变绿"
},
onSuccess: (data) => {
console.log('验证成功:', data);
verified.value = true;
},
onFailure: () => {
verified.value = false;
},
onError: (error) => {
console.error('验证错误:', error);
verified.value = false;
}
});
}
});
const handlePayment = () => {
if (!verified.value) {
alert('请先完成安全验证');
return;
}
// 执行支付逻辑
console.log('开始支付流程...');
alert('支付成功!');
// 重置验证码
if (captchaInstance) {
captchaInstance.reset();
verified.value = false;
}
};
return {
captchaContainer,
verified,
totalAmount,
shippingFee,
handlePayment
};
}
};
</script>
<style scoped>
.checkout-page {
max-width: 500px;
margin: 0 auto;
padding: 30px;
}
.order-summary {
background: #f8fafc;
padding: 20px;
border-radius: 8px;
margin: 20px 0;
}
.total {
font-weight: bold;
font-size: 18px;
color: #3b82f6;
}
.captcha-section {
margin: 30px 0;
}
.captcha-box {
margin: 15px 0;
}
.hint {
font-size: 12px;
color: #64748b;
margin-top: 8px;
}
.hint.success {
color: #10b981;
}
button {
width: 100%;
padding: 16px;
background: #3b82f6;
color: white;
border: none;
border-radius: 8px;
font-size: 16px;
cursor: pointer;
}
button.disabled {
background: #94a3b8;
cursor: not-allowed;
}
</style>实例方法
reset()
重置验证码到初始状态。该方法会重新生成随机的"站点位置"和"充电目标时长",并重置UI状态。
javascript
// 1. 保存实例引用
const myCaptcha = new WlzShield.CheckpointCaptcha({
siteKey: 'your-site-key',
containerId: '#captcha-box'
});
// 2. 在需要的时候调用
function handleFormError() {
// 重置验证码
myCaptcha.reset();
// 重置表单状态
document.getElementById('submitBtn').disabled = true;
document.getElementById('submitBtn').innerText = '提交';
}openModal() 和 closeModal()
仅适用于popup模式:手动打开或关闭验证弹窗。
javascript
const popupCaptcha = new WlzShield.CheckpointCaptcha({
siteKey: 'your-site-key',
containerId: '#captcha-box',
mode: 'popup'
});
// 手动打开弹窗
document.getElementById('custom-trigger').addEventListener('click', () => {
popupCaptcha.openModal();
});
// 手动关闭弹窗
document.getElementById('cancel-btn').addEventListener('click', () => {
popupCaptcha.closeModal();
});事件回调详解
onSuccess
验证成功时触发,返回后端验证数据。
javascript
new WlzShield.CheckpointCaptcha({
// ... 其他配置
onSuccess: function (data) {
console.log('验证成功,完整响应:', data);
// 存储验证凭证
localStorage.setItem('wsToken', data.wsToken || '');
}
});onFailure
验证失败时触发,可能是充电时间不足、过长或轨迹异常。
javascript
new WlzShield.CheckpointCaptcha({
// ... 其他配置
onFailure: function (data) {
console.warn('验证失败,原因:', data.msg || '未知原因');
// 根据错误类型提供不同的用户提示
if (data.msg?.includes('充电时间')) {
alert('充电时间不符合要求,请重试');
} else if (data.msg?.includes('轨迹')) {
alert('操作轨迹异常,请正常拖动滑块');
} else {
alert('验证失败,请重试');
}
// 自动重置验证码
setTimeout(() => {
this.reset();
}, 1500);
}
});onError
系统错误时触发,通常是网络问题或配置错误。
javascript
new WlzShield.CheckpointCaptcha({
// ... 其他配置
onError: function (error) {
console.error('验证系统错误:', error);
// 根据错误类型提供不同的处理
if (error.message.includes('Network')) {
alert('网络连接失败,请检查网络设置');
} else if (error.message.includes('Timeout')) {
alert('请求超时,请重试');
} else if (error.message.includes('siteKey')) {
alert('验证配置错误,请联系网站管理员');
} else {
alert('验证系统异常,请刷新页面');
}
}
});onReady, onOpen, onClose
组件生命周期回调:
javascript
new WlzShield.CheckpointCaptcha({
// ... 其他配置
onReady: function () {
console.log('验证码组件已加载完成,可以开始验证');
// 可以在这里显示加载完成的提示
},
onOpen: function () {
console.log('验证弹窗已打开');
// 弹窗打开时,可以暂停页面上的其他交互
document.body.style.overflow = 'hidden';
},
onClose: function () {
console.log('验证弹窗已关闭');
// 弹窗关闭时,恢复页面交互
document.body.style.overflow = '';
// 如果未验证成功,显示提示
if (!this.isVerified) {
alert('验证未完成,请完成验证以继续');
}
}
});注意事项
随机参数生成:每次验证会随机生成站点位置(20-80%)和充电时间(800-2000ms),确保每次验证都不同。
物理阻挡机制:未充满电时,滑块无法越过站点右侧2%的位置,提供真实的物理反馈。
移动端适配:组件完全支持触摸操作,在移动设备上表现良好。
性能优化:充电动画使用requestAnimationFrame实现,确保流畅的60fps动画效果。
安全特性:自动启用鼠标轨迹追踪,采集用户操作行为数据用于人机识别。
多实例支持:可以在同一页面创建多个CheckpointCaptcha实例,每个实例独立工作。
故障排查
常见问题
滑块无法拖动
- 检查容器是否正确初始化
- 查看控制台是否有JavaScript错误
- 确保没有其他JavaScript库干扰事件监听
充电不启动
- 确保滑块在站点±3%范围内
- 检查是否已充满电(站点变绿)
- 查看是否有控制台警告
验证总是失败
- 确认
siteKey是否正确 - 检查网络连接,确保能访问验证接口
- 查看后端验证服务是否正常运行
- 确认
弹窗模式不显示
- 确认
mode设置为'popup' - 检查容器是否隐藏(弹窗模式容器通常隐藏)
- 确保没有CSS样式覆盖弹窗显示
- 确认
调试技巧
javascript
// 创建实例时捕获错误
try {
const captcha = new WlzShield.CheckpointCaptcha({
// 配置
});
} catch (error) {
console.error('初始化失败:', error);
// 显示友好的错误提示
document.getElementById('captcha-container').innerHTML =
'<div class="error">验证码加载失败,请刷新页面或联系管理员</div>';
}
// 监听所有可能的事件
const captcha = new WlzShield.CheckpointCaptcha({
// ... 配置
onReady: () => console.log('组件就绪'),
onOpen: () => console.log('弹窗打开'),
onClose: () => console.log('弹窗关闭'),
onSuccess: (data) => console.log('验证成功', data),
onFailure: (data) => console.log('验证失败', data),
onError: (error) => console.log('系统错误', error)
});浏览器兼容性
- Chrome 60+
- Firefox 63+
- Safari 12+
- Edge 79+
- iOS Safari 12+
- Android Chrome 60+
组件使用现代Web API,对于不支持某些API的旧浏览器会自动降级处理。
通过以上详细文档,您可以全面了解CheckpointCaptcha的使用方法和配置选项。站点停靠验证提供了独特的按压蓄力交互,在保证用户体验的同时提供了强大的安全防护能力。如有进一步问题,请参考源码或联系技术支持。