引入组件资源
把样式放在页面的 <head> 中,并在 </body> 前引入脚本。脚本会自动向它所在的域名请求验证接口。
HTML
<link rel="stylesheet" href="https://cap-pow.wuw.li/cap-pow.css">
<script src="https://cap-pow.wuw.li/cap-pow.js" defer></script>让浏览器完成一小段工作量证明,以此拦截自动化滥用。无需图片识别、无需追踪用户,也不依赖第三方验证码服务。
下面以登录表单为例。核心流程是:浏览器获得验证 token,把它随业务表单提交;你的服务端消费 token 后,再处理登录或评论。
把样式放在页面的 <head> 中,并在 </body> 前引入脚本。脚本会自动向它所在的域名请求验证接口。
<link rel="stylesheet" href="https://cap-pow.wuw.li/cap-pow.css">
<script src="https://cap-pow.wuw.li/cap-pow.js" defer></script>复制完整结构,不要删改 cap-* 类名。隐藏字段用于把最终 token 和你的业务表单一起提交。
<form id="login-form" method="post" action="/login">
<input name="email" type="email" required>
<input name="password" type="password" required>
<input id="cap-token" name="cap_token" type="hidden">
<div class="cap-wrap">
<div class="captcha">
<div class="cap-ct" id="cap-ct" role="button" tabindex="0"
onclick="CapPow.go()" aria-label="点击进行人机验证">
<div class="cap-cb">
<div class="cap-check">
<svg viewBox="0 0 24 24">
<polyline points="4,12 9,17 20,6"></polyline>
</svg>
</div>
<svg class="cap-ring" viewBox="0 0 32 32">
<circle class="cap-ring-bg" cx="16" cy="16" r="14"></circle>
<circle class="cap-ring-fg" cx="16" cy="16" r="14"></circle>
</svg>
</div>
<div class="cap-lw">
<span class="cap-label active">验证你是人类</span>
</div>
</div>
<a class="cap-credits" href="https://github.com/tiagorangel1/cap">Cap</a>
</div>
</div>
<button type="submit">登录</button>
</form>验证完成后写入隐藏字段。提交表单前再次检查,避免用户尚未完成验证就发起业务请求。
const form = document.querySelector('#login-form');
const tokenInput = document.querySelector('#cap-token');
CapPow.onDone = function (token) {
tokenInput.value = token;
};
CapPow.onFail = function (message) {
tokenInput.value = '';
console.error('Cap-Pow 验证失败:', message);
};
form.addEventListener('submit', function (event) {
if (!tokenInput.value) {
event.preventDefault();
alert('请先完成人机验证');
}
});业务后端收到 cap_token 后,向 Cap-Pow 的 /api/validate 发起 POST。只有返回 success: true 才继续业务逻辑。
app.post('/login', async (req, res) => {
const response = await fetch('https://cap-pow.wuw.li/api/validate', {
method: 'POST',
headers: { 'content-type': 'application/json' },
body: JSON.stringify({ token: req.body.cap_token })
});
const result = await response.json();
if (!result.success) {
return res.status(403).json({ error: '人机验证失败' });
}
// Token 已验证并消费,现在执行登录逻辑。
return handleLogin(req, res);
});<?php
$payload = json_encode(['token' => $_POST['cap_token'] ?? '']);
$context = stream_context_create(['http' => [
'method' => 'POST',
'header' => "Content-Type: application/json\r\n",
'content' => $payload,
'timeout' => 5,
]]);
$response = file_get_contents(
'https://cap-pow.wuw.li/api/validate', false, $context
);
$result = $response ? json_decode($response, true) : null;
if (!($result['success'] ?? false)) {
http_response_code(403);
exit('人机验证失败');
}
// Token 已验证并消费,现在执行登录逻辑。const form = await request.formData();
const response = await fetch('https://cap-pow.wuw.li/api/validate', {
method: 'POST',
headers: { 'content-type': 'application/json' },
body: JSON.stringify({ token: form.get('cap_token') })
});
const result = await response.json();
if (!result.success) {
return Response.json(
{ error: '人机验证失败' },
{ status: 403 }
);
}
// Token 已验证并消费,现在执行业务逻辑。Challenge 约 5 分钟有效;验证 token 约 20 分钟有效,而且只能成功核验一次。业务请求失败后不要复用旧 token,应让用户重新验证。
// 成功
{ "success": true, "message": "Token validated successfully" }
// 已使用、过期或不存在
{ "success": false, "message": "Token not found" }组件通常会自动完成前两个接口;你的业务后端只需要调用验证接口。
创建一组 PoW 题目和短期 challenge token。
浏览器调用 · 不缓存提交浏览器计算结果,换取一次性验证 token。
浏览器调用 · 不缓存消费并核验验证 token,返回是否允许继续业务。
仅业务后端调用 · 不缓存使用 CDN 绑定域名时,源站 Host 和缓存规则必须明确配置,否则接口可能命中错误缓存或无法回源。