移动端登录页面里,用户最烦躁的操作往往不是输入手机号,而是收到短信后要切回浏览器、记住六位数字、再手动敲进输入框。整个过程容易出错,尤其在验证码有效期只有60秒的情况下,超时重发的比例相当高。WebOTP API就是为解决这个问题而生的浏览器能力,它可以让网页直接读取符合特定格式的短信内容,把验证码自动填入指定输入框。本文将详细介绍这套API的原理,并用jQuery把它封装成一个即插即用的插件。

WebOTP API的工作原理与短信格式要求
WebOTP全称Web One-Time Password,是Chrome团队主导推出的标准,目前Chrome 84以上版本和部分Android浏览器已经支持。它的核心思路是:短信发送方在短信末尾附加一段特定格式的内容,浏览器识别到这个格式后,弹出一条底部提示条,用户点击即可把验证码回传给当前网页。
短信末尾需要附加的格式叫做域绑定格式,写法如下:
@www.example-ipipp.com #123456
其中@后面是发送方的域名,#后面是验证码本身。这两行内容必须放在短信的最后一行,且域名要与当前页面完全一致。如果短信是第三方平台代发的,务必和服务商确认能不能附加这段尾巴,否则浏览器无法识别。这里有一个常见的坑:域名写的是ipipp.com而页面实际是www.ipipp.com,浏览器会认为域名不匹配而拒绝弹出提示。另外短信内容中不能有额外空行干扰,建议把绑定信息放在最末尾单独一行。
在网页侧,WebOTP通过navigator.credentials.get方法来监听验证码,传入otp类型的选项即可。调用这个方法有几个前提:页面必须运行在HTTPS环境,且navigator.credentials对象存在。下面的封装会把这些检测都做进去。
用jQuery封装一个可复用的WebOTP插件
直接调用原生API每次都要写一堆检测代码和回调处理,把它封装成jQuery插件后,一行调用就能生效。设计思路是:插件接收目标输入框和回调函数,内部处理API检测、调用、超时和异常,并在验证码到达后自动填入输入框、触发input事件让其他逻辑(比如自动提交)能正常响应。
完整的插件代码如下:
(function ($) {
$.fn.webOtp = function (options) {
var defaults = {
timeout: 60000, // 最长等待时间,超时后自动放弃
onReceive: null, // 收到验证码后的回调
onUnsupported: null // 浏览器不支持时的回调
};
var settings = $.extend({}, defaults, options);
return this.each(function () {
var $input = $(this);
// 检测浏览器是否支持WebOTP
if (!('OTPCredential' in window) || !navigator.credentials) {
if ($.isFunction(settings.onUnsupported)) {
settings.onUnsupported.call($input);
}
return;
}
var abortController = new AbortController();
var timer = setTimeout(function () {
abortController.abort(); // 超时后中止等待
}, settings.timeout);
navigator.credentials.get({
otp: { transport: ['sms'] },
signal: abortController.signal
}).then(function (otp) {
clearTimeout(timer);
var code = otp.code;
$input.val(code)
.trigger('input') // 触发input事件,兼容其他监听逻辑
.trigger('change');
if ($.isFunction(settings.onReceive)) {
settings.onReceive.call($input, code);
}
}).catch(function (err) {
clearTimeout(timer);
// AbortError是正常超时或用户离开页面,无需特殊处理
if (err.name !== 'AbortError') {
console.warn('WebOTP获取失败:', err);
}
});
});
};
})(jQuery);使用方式非常简单,在用户点击发送验证码按钮后调用即可:
$('#btn-send').on('click', function () {
sendSmsRequest(); // 先请求后端发送短信
$('#otp-input').webOtp({
timeout: 90000,
onReceive: function (code) {
console.log('收到验证码:', code);
$('#login-form').submit(); // 收到后自动提交
},
onUnsupported: function () {
console.log('当前浏览器不支持,用户需手动输入');
}
});
});有几点设计细节值得说明。第一,使用AbortController来控制超时,比单纯用setTimeout标记状态更干净,浏览器会真正停止监听,节省资源。第二,验证码填入后手动触发input事件,因为通过.val()设置值不会触发任何事件,如果有框架或逻辑在监听输入变化就会失效。第三,插件内部的this.each保证了链式调用和批量绑定都没问题。
跨域iframe场景与兼容性降级方案
很多支付或登录组件是嵌在第三方页面的iframe里运行的,比如商户页面嵌入支付平台的验证模块。默认情况下浏览器出于安全考虑,不允许iframe内的页面读取短信。WebOTP提供了属性授权机制解决这一问题:父页面在iframe标签上声明credentialless不行,正确做法是添加allow属性并指定otp-credentials来源,同时iframe内的页面所在域名要和短信中的域名一致。
<iframe src="https://pay.ipipp.com/verify"
allow="otp-credentials 'src'"></iframe>注意'src'要加引号,表示只授权iframe的src属性指向的那个域名。如果写成*则表示授权任意来源,安全性会下降,不建议这样做。
关于兼容性,WebOTP目前主要在Android平台的Chrome和Edge上可用,iOS Safari暂未支持。所以插件里的onUnsupported回调非常重要,不能把它当成可有可无的参数。不支持的环境下,用户依旧走手动输入流程,页面上的输入框要保留autocomplete="one-time-code"属性,这样即使在iOS上,系统键盘也会弹出短信验证码的快捷填充条,算是另一层降级保障。
<input type="tel" id="otp-input" maxlength="6"
autocomplete="one-time-code"
inputmode="numeric" placeholder="请输入验证码">最后还需要注意用户体验的边界:验证码等待期间不要弹出遮挡性提示,浏览器自带的底部确认条已经是最好的交互;用户拒绝过一次授权后,同一页面短时间内不会再次弹出,此时应在合适时机提示用户手动输入。整套方案上线后,建议在后端统计一下自动填写的成功率与手动输入的转化对比,通常能看到明显的登录完成率提升,这些数据也能为后续优化提供依据。
jQueryWebOTP API短信验证码自动填写修改时间:2026-09-14 12:35:01