不能直接用原生input做支付密码输入,因iOS/Android软键盘控制权限不同,易致明文暴露、自动补全、粘贴泄露等风险;需用view模拟键盘+自定义逻辑实现安全输入。

uni-app里为什么不能直接用原生input做支付密码输入
因为iOS和Android对软键盘的控制权限不同,原生在部分安卓机型上会弹出带字母的全键盘,iOS则可能触发自动补全或粘贴,存在密码明文暴露风险。微信小程序虽有safe-area-inset-bottom兼容处理,但uni-app跨端编译后无法保证所有平台都走同一套安全逻辑。
- 真机测试发现:华为EMUI 12+、小米MIUI 13下
inputmode="numeric"常被忽略,仍弹出九宫格+符号键盘 - 支付宝/微信支付SDK要求密码输入必须为纯数字、不可复制、不可长按选中、不触发系统输入法预测
- uni-app的
v-model绑定会把值同步到data中,若未做内存清理,可能被内存扫描工具捕获
用view模拟键盘 + 自定义input框的核心实现要点
放弃标签,改用展示6位圆点,用data-password临时存加密后的密文片段(如SHA-256哈希前8位),每次按键只更新一位并重绘DOM。
- 键盘布局用
flex+justify-content: space-between避免百分比错位,禁用user-select: none防止长按选中文本 - 每个数字按钮绑定
@tap="onKeyTap(1)",不传$event——避免事件对象携带原始键值,减少攻击面 - 输入框区域用
position: relative+z-index: 2盖住系统键盘,需在onKeyboardHeightChange中动态调整bottom值(仅App端支持) - 安卓端需调用
uni.hideKeyboard()主动收起系统键盘;iOS端要监听focus事件后立即blur()防误触
<view class="pwd-box"><view v-for="(dot, i) in 6" :key="i" class="pwd-dot" :class="{ active: i < passwordLen }"></view></view><view class="num-keyboard"><view v-for="n in [1,2,3,4,5,6,7,8,9,'',0,'del']" :key="n" class="key">{{ n === '' ? '' : n }}</view></view>
uni-app条件编译下各端的安全增强策略
不同平台能力差异大,不能写一套代码跑所有端。必须用#ifdef区分逻辑,尤其涉及键盘控制和加密环节。
-
#ifdef APP-PLUS:可调用nativePlugins接入原生安全键盘SDK(如银联云闪付提供的UPOPKeyboard),此时只需透传密文hash,不存明文 -
#ifdef MP-WEIXIN:启用input的confirm-type="done"+adjust-position="false",配合bindkeyboardheightchange动态隐藏键盘浮层 -
#ifdef H5:必须禁用contextmenu和selectstart事件,CSS加-webkit-user-select: none; user-select: none;,且密码字段不用v-model而用:value+@input手动截断 - 所有端都要在
onUnload生命周期里清空this.password、this.tempHash等敏感变量,避免页面缓存残留
容易被忽略的三个细节:光标、粘贴、退格逻辑
用户习惯性点击输入框想唤起光标、双击想粘贴、连按退格想清空——这些行为在自定义键盘里必须显式拦截,否则会暴露底层实现漏洞。
- 光标:用
animation模拟闪烁效果,禁止caret-color设置,动画帧用transform: scaleY(0)而非opacity,避免GPU渲染残留 - 粘贴:全局监听
@paste.prevent并stopPropagation(),H5端还要在document上监听beforepaste事件并preventDefault() - 退格:不能简单
password.pop(),要先校验当前是否处于最后一位有效输入(即passwordLen > 0),且退格后需触发一次uni.hideKeyboard()防止键盘意外弹出
真正难的不是画出键盘,而是让6个圆点的行为在iOS、安卓、微信、支付宝、H5五端都一致地“不像输入框”。很多项目卡在MIUI下长按空白处仍能呼出系统键盘,本质是没在touchstart里调用event.preventDefault()——这个细节只有真机反复点按测试才能暴露。











