如何优雅的对input框数据进行动态脱敏

3,691 阅读6分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第19天,点击查看活动详情

说在前面

🎈所谓的数据脱敏,是指在不影响数据分析结果的准确性前提下,对原始数据中的敏感字段进行处理,从而降低数据敏感度和减少个人隐私风险的技术措施。在现在这个大数据时代,个人隐私信息在互联网上传播的几率是很大的,因此作为前端工程师,我们很多时候也需要在视图层面对数据进行脱敏展示处理。

场景

在一个页面上,我们需要客户输入手机号码、身份证号码等敏感信息,客户要求在输入的时候可以对输入内容进行脱敏处理,同时原始数据需要保存到数据库,如下图:

image.png

脱敏格式:保留身份证号码的前四位和后六位,中间使用*号代替

体验

体验地址:jyeontu.xyz/JDemo

难点分析

接到需求后我们需要先对其进行简单分析,可以得出大致有以下几个问题需要处理。

  • 1、input框输入值与展示值分离;
  • 2、对展示值进行修改时,需要同步修改真实值;
  • 3、真实值与展示值同步动态转换。
  • 4、光标位置需要重设;

功能实现

想要实现该功能只需要将前面分析得出的难点一一克服,我们就可以实现该功能。

1、input框输入值与展示值分离

这个问题其实比较好解决,我们可以添加两个变量,分别保存输入的真实数据和脱敏后的展示数据。

<template>
    <div>
        <div class="form-item">身份证号码脱敏(显示前4位和后6位)</div>
        <input class="form-item" 
            v-model="showValue" 
            placeholder="请输入身份证号码"
            @input="desenInputText" 
        />
        <div class="form-item">输入数据</div>
        <div class="form-item"> {{ value }} </div>
    </div>
</template>

如上代码,我们分别使用showValue和value两个变量来保存脱敏后的展示数据和输入的真实数据。

2、对展示值进行修改时,需要同步修改真实值

我们可以给input框绑定input事件,每次输入时对真实值和展示值进行动态转换,具体转换方法后面会说到。

desenInputText(e) {
    //真实值与展示值同步动态转换
},

3、真实值与展示值同步动态转换

3.1 末尾追加 or 删除

这里是该功能真正意义上的第一个难点,我们需要同时维护两个变量的值,主要有以下四种场景: image.png

  • 1、输入单个字符 如上图,此时的展示值为1234***891011,真实值为1234567891011,如果此时我们在最后追加一个字符2,应该需要进行怎样地处理?其实只需要两步:
1)将输入的`2`拼接到真实值后面,此时真实值变为`12345678910112`;

(2)再对真实值进行脱敏得到展示值`1234****910112`
  • 2、复制输入多个字符 如果此时我们并不是在最后追加一个字符,而是需要将剪切板的多个字符(如:234)追加到数据后面呢?我们需要进行以下三步操作:
1)计算展示值和真实值之差n;

(2)将展示值的末尾n位拼接到真实值后面,此时真实值变为`1234567891011234`;

(3)再对真实值进行脱敏得到展示值`1234******011234`
  • 3、删除单个字符 删除单个字符时我们只需要将真实值末尾的字符输出,再重新转换展示值即可。
  • 4、选中删除多个字符 删除多个值的时候,我们需要进行以下三步操作:
(1)计算展示值和真实值之差n;

(2)删除真实值的末尾n位;

(3)对真实值进行脱敏得到展示值。

3.2 非末尾插入 or 删除

前面我们看了在末尾插入或者删除的4种场景,知道了简单场景下的动态转换方法,那么将修改位置移动到字符串中间呢?暂停五分钟思考一下我们应该要怎么做?
5
.
.
.
4
.
.
.
3
.
.
.
2
.
.
.
1
.
.
.
0
有了大致思路了吗?我们接着往下进行:
插入位置变了,那如果我们可以找到插入位置,我们是不是可以将插入的开始位置看成是末尾,在这个位置插入增量数据或者删除掉减量数据即可?所以我们需要进行以下四步:\

(1)获取当前光标位置ind;

(2)计算展示值和真实值之差n;

(3)在输入起始位置(ind-n+1)插入增量数据;

(4)对真实值进行脱敏得到展示值。

具体代码如下

//输入框动态脱敏
desenInputText(e) {
    const ind = e.target.selectionStart - 1;
    let value = this.value;
    const showValue = this.showValue;
    const isAdd = showValue.length > value.length;
    const num = Math.abs(value.length - showValue.length);
    if (isAdd) {
        value =
            value.slice(0, ind-num+1) +
            showValue.slice(ind-num+1,ind+1) +
            value.slice(ind-num+1);
    } else {
        value = value.slice(0, ind + 1) + value.slice(ind + num + 1);
    }
    this.value = value;
    this.showValue = this.desenText(value);
},

4、光标位置需要重设

写完上面的代码后已经可以大致实现数据动态脱敏的功能,但是试了几遍后我们会发现,每次输入后光标都会自动移动到输入框的最后面,我们还需要处理一下这个问题,将光标保持在原来的位置上。

const elem = e.target;
if (elem.setSelectionRange) {
    // 标准浏览器
    elem.setSelectionRange(ind + 1, ind + 1);
} else {
    // IE9-
    const range = elem.createTextRange();
    range.moveStart('character', ind + 1);
    range.moveEnd('character', ind + 1);
    range.select();
}

完整代码

<template>
    <div>
        <div class="form-item">身份证号码脱敏(显示前4位和后6位)</div>
        <input class="form-item" 
            v-model="showValue" 
            @input="desenInputText" 
            placeholder="请输入身份证号码"
        />
        <div class="form-item">输入数据</div>
        <div class="form-item"> {{ value }} </div>
    </div>
</template>

<script>
export default {
    name:'desensitize-input',
    data(){
        return {
            showValue:'',
            value:''
        }
    },
    methods:{
        desenText(str) {
            let res = str;
            const len = str.length;
            let pre4 = '';
            let last6 = '';
            pre4 = str.slice(0, 4);
            last6 = str.slice(Math.max(len - 6, 4));
            const star = Math.max(0, len - 10);
            res = pre4 + '*'.repeat(star) + last6;
            return res;
        },
        //输入框动态脱敏
        desenInputText(e) {
            const ind = e.target.selectionStart - 1;
            let value = this.value;
            const showValue = this.showValue;
            const isAdd = showValue.length > value.length;
            const num = Math.abs(value.length - showValue.length);
            if (isAdd) {
                value =
                    value.slice(0, ind-num+1) +
                    showValue.slice(ind-num+1,ind+1) +
                    value.slice(ind-num+1);
            } else {
                value = value.slice(0, ind + 1) + value.slice(ind + num + 1);
            }
            this.value = value;
            this.showValue = this.desenText(value);
            this.$nextTick(() => {
                const elem = e.target;
                if (elem.setSelectionRange) {
                    // 标准浏览器
                    elem.setSelectionRange(ind + 1, ind + 1);
                } else {
                    // IE9-
                    const range = elem.createTextRange();
                    range.moveStart('character', ind + 1);
                    range.moveEnd('character', ind + 1);
                    range.select();
                }
            });
        },
    }
}
</script>

<style>
.form-item{
    margin-top: 1em;
}
</style>

源码地址

Gitee源码:gitee.com/zheng_yongt…

觉得有帮助的同学可以帮忙给我点个star,感激不尽~~~
有什么想法或者改良可以给我提个pr,十分欢迎~~~
有什么问题都可以在评论告诉我~~~

往期精彩

你还在重复编写表单代码吗?封装一个组件不香吗?

vue封装一个图案手势锁组件

vue封装一个弹幕组件

为了学(mo)习(yu),我竟开发了这样一个插件

程序员的浪漫之——情侣日常小程序

JavaScript双向链表实现LRU缓存算法

JavaScript双向链表实现LFU缓存算法

JavaScript实现前缀树

vue简单实现词云图组件

vue + echarts实现中国地图省份下钻联动

纯CSS实现悬浮提示并封装成vue组件

使用学过的算法做个游戏很酷的好吗

说在后面

🎉这里是JYeontu,现在是一名前端工程师,有空会刷刷算法题,平时喜欢打打羽毛球🏸 ,平时也喜欢写些东西,既为自己记录📋,也希望可以对大家有那么一丢丢的帮助,写的不好望多多谅解🙇,写错的地方望指出,定会认真改进😊,在此谢谢大家的支持,我们下文再见🙌。