跳到主要内容

表单接入

验证组件负责求解并交出令牌,业务接口负责在信任数据之前校验它。两者之间只传递一枚短时效字符串。

用途(scope)​

每张表单都要声明用途,服务端维护一份白名单:

用途表单
login登录
register注册
reset密码找回

用途统一小写,随挑战签发并写进兑换令牌。登录页解出的挑战不能拿去注册或找回密码;新增表单时先登记用途,再挂组件。

提示

为什么要绑定用途 — 如果不绑定,攻击者可以在低价值表单上廉价地批量求解,再把得到的令牌用于高价值接口。用途绑定让每一份答案只能兑换它声明过的那一次提交。

组件生命周期​

  1. 盒子立即渲染,求解器异步加载完成
  2. 组件向挑战接口请求参数,在 Worker 中计算
  3. 把解交回兑换接口,取得令牌
  4. 通过回调把令牌交给表单,盒子显示通过状态
警告

盒子先于求解器实例出现。若用户在实例加载完成前点击,需要一个就绪轮询来兜底,否则会永久停在「正在验证…」。这不是网络慢,而是空窗期没有对象可调用。

令牌的存放​

令牌只放在组件状态里,随表单一次性提交,不写入 Cookie、不写入本地存储、不放进 URL。提交完成后清空并重置组件,成功与失败分支都要清。

接入代码​

<script src="https://your-nscha-host/widget.js" defer></script>

<nscha-widget action="login" name="nschaToken"></nscha-widget>

组件挂载后自动求解。验证通过会往表单里写入隐藏字段 nschaToken,并派发 nscha:success:

document.addEventListener('nscha:success', (ev) => {
// ev.detail = { token, scope, elapsedMs }
console.log('已验证', ev.detail.elapsedMs, 'ms')
})

提交完成后(成功与失败都要)清空并重置:

document.querySelector('nscha-widget').reset()

失败或超时不要把旧令牌留在状态里 —— 过期令牌会污染下一次提交,让错误看起来像「服务端随机拒绝」。设 auto="off" 可以改成用户点击后才开始验证。

继续阅读​