Web OTP API 不支持主动拦截短信,而是用户触发后由浏览器在匹配格式短信到达时自动填充验证码;需HTTPS、正确DOM配置、规范短信格式(含@域名和#验证码)及浏览器支持。

Web OTP API 本身不支持“拦截”短信,也不能在用户未授权、未触发的前提下主动读取或捕获短信内容。它是一个用户主动触发、浏览器受控、一次性的凭证获取机制。所谓“异步拦截”,实际是误解;正确理解应为:在用户点击获取验证码后,由浏览器在收到匹配格式的短信时,自动提供该验证码,并由前端代码异步填充到指定输入框。
关键在于“触发时机 + 短信格式 + DOM 配置 + 权限控制”四者协同,而非后台监听或越权读取。
Web OTP API 自动填充的核心前提
- 必须运行在 HTTPS 环境下(本地
localhost也允许) - 用户已手动触发获取验证码流程(如点击“发送验证码”按钮)
- 浏览器需支持(Chrome 94+、Edge 94+;Safari 使用原生
autocomplete="one-time-code",不走navigator.credentials.get) - 短信内容必须符合 W3C 规范格式,且包含当前网站域名(带
@)和验证码(带#)
正确实现自动填充的三步操作
1. 前端表单输入框必须严格配置
<input
type="text"
inputmode="numeric"
autocomplete="one-time-code"
pattern="\d{6}"
maxlength="6"
id="otp-input"
name="otp"
placeholder="请输入验证码"
>-
type="text"(非number):避免数字键盘出现上下箭头,影响体验 -
inputmode="numeric":唤起数字键盘(iOS/Android 均生效) -
autocomplete="one-time-code":这是浏览器识别 OTP 输入框的唯一信号 -
pattern和maxlength:辅助校验,提升表单健壮性
2. 在用户触发验证码请求后调用 navigator.credentials.get
async function startWebOTP() {
if (!('OTPCredential' in window)) return;
const input = document.getElementById('otp-input');
const form = input?.closest('form');
// 创建 AbortController,用于在用户手动提交时中止等待
const ac = new AbortController();
if (form) {
form.addEventListener('submit', () => ac.abort(), { once: true });
}
try {
const otp = await navigator.credentials.get({
otp: { transport: ['sms'] },
signal: ac.signal
});
if (otp && otp.code) {
input.value = otp.code;
if (form) form.submit();
}
} catch (err) {
// 用户取消、超时、无匹配短信、或被中止,均进入此分支
console.debug('Web OTP 获取失败或被取消:', err.name);
}
}- 调用时机:建议放在「发送验证码按钮点击后」,而非页面加载时
-
signal: ac.signal是关键:防止用户手动填完并提交后,API 还强行覆盖输入值 - 不要重复调用:每次
get()是一次性尝试,失败后需用户再次触发
3. 后端发送的短信必须满足格式规范
短信正文末尾(推荐最后一行)需包含:
@yourdomain.com #123456
-
@yourdomain.com必须与当前网页的 注册域名完全一致(不含www或路径,支持子域但需提前声明) -
#123456中的#符号不可省略,后面紧跟纯数字验证码(长度不限,但前端pattern应匹配) - 示例合法短信:
您的验证码是:889231,请在5分钟内使用。 @login.example.com #889231
注意:Chrome 不解析短信中任意位置的
#,只认末尾行的@domain #code组合;Safari 则依赖autocomplete="one-time-code"+ 系统级短信解析,无需显式域名绑定(但仍有安全限制)。
安全边界与常见误区
- ❌ Web OTP 不能绕过用户交互:必须由用户点击按钮等明确动作触发
get() - ❌ 无法后台监听所有短信:不是 Android 的 SMS Receiver,不申请任何系统权限
- ❌ 不兼容 Safari 的
navigator.credentials.get:Safari 走的是原生表单自动填充逻辑,无需 JS 调用 - ✅ 可与服务端风控联动:比如限制同一手机号 60 秒内只能触发一次 Web OTP 请求
- ✅ 建议搭配服务端验证:即使前端填入了 code,仍需服务端校验时效性、次数、是否已被使用
兜底方案:当 Web OTP 不可用时
- 检测失败后,自动聚焦到输入框,提示“短信已发送,请手动输入”
- 提供「粘贴」快捷入口(监听剪贴板变化,但需用户主动 paste)
- 若检测到 iOS 设备,可额外提示:“请长按验证码,选择‘粘贴到此处’”
不复杂但容易忽略。

















