如果产品经理要求系统中某个页面的输入框做防止篡改处理,你会怎么做呢?
需求梳理
- 首先,什么是防篡改?
简单来说,就是用户输入input框值,我们传给后端的值就是用户输入的
- 正常情况下用户输入input框值,就是我们传递给后端的值,但是部分浏览器插件或者恶意脚本会更改用户输入的值
- 常见针对的是输入的钱包地址,我们需要防范被浏览器插件和恶意脚本更改
- 因为实现的效果需要对已有的业务无任何侵入性,保证原有业务的正常运行
- 这里的需求背景在nuxt2技术栈
最终效果
- 业务方只需要加上这个指令就可以
图片
实现思路
- 我们如何保证对原有的代码没有侵入性,保证不影响原有的功能?对此我们想到一个自定义指令,在指令里面操作。但是一般指令都是直接写在组件里面的,并不是写在真实的 input 标签上,对此,我们要变更我们的指令,让其去找到真正的 input 标签。
// 通常情况下, 这是一个Input组件,我们需要给这个指令找到其真正的 input 标签
<Input v-xxx/>
- 这里的代码实现的是如何找到真实的 input 标签。
图片
- 如何在指令里面发送请求给后端?对此,我们在指令里面使用自定义事件,让真实的 input 标签绑定上自定义事件
- 里面涉及2个知识点
- 如何给绑定过的 input标签解绑事件?
- 如何在指令里面调用请求的方法
- 问题1答案,我们在指令的节点node, 在 vnode绑定上一个自定义函数,此自定义函数在解绑事件的时候在调用
- 问题2答案,我们在vnode.context调用自定义方法tamperFn(); 这里的vnode.context 就是 this,相当于我们调用了 this.tamperFn(); 此处的this就是 Vue实例 (在下面的代码事例中有个属性 isTrusted 至关重要)
图片
图片
- 至此,我们已经实现如何不侵入业务的情况下找到 input 标签 & 如何在找到的 input 标签绑定事件并且发送请求出去 & 解绑事件 (有个核心问题,到目前为止没有看到如何区分提交的表单数据是用户写的还是被浏览器插件恶意改的,且继续往下看)
- js 的 event 有个属性 isTrusted
- 点击链接了解 isTrusted[1]
图片
- 首先要对所有的 input 标签使用Object.getOwnPropertyDescriptor 进行劫持,找到 input 标签的 set 属性,此时,当变更 input 的输入值我们都可以监控到变化,当有js变更input输入框的值都会触发 set 方法。
图片
- 上述的功能代码是一个完整的 config.js, 启动项目的时候直接在 nuxt.config.js 加载这个 config.js 即可,业务团队使用一个指令即可完成需求。
总结
我们来梳理下流程:
- 首先使用Object.getOwnPropertyDescriptor 进行劫持所有的 input 标签, 在里面会触发自定义事件dispatchTamper
- 自定义事件绑定在真实的 input 标签上,在浏览器执行js阶段完成了绑定事件。通过 vnode.context 我们可以调用 Vue.prototype.tamperFn 方法。在 tamperFn 里面拿到 isTrusted 来区分是不是被篡改的值。
- 我们在绑定 input 标签的事情同时,设置了 node.cusFn = cusFn, 用来解绑事件。
Reference
[1]https://developer.mozilla.org/zh-CN/docs/Web/API/Event/isTrusted: https://link.juejin.cn?target=https%3A%2F%2Fdeveloper.mozilla.org%2Fzh-CN%2Fdocs%2FWeb%2FAPI%2FEvent%2FisTrusted