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

效果展示


体验地址
http://jyeontu.xyz/jvuewheel/#/JDesensitizationView
指令实现
脱敏函数
参数处理
- 接受三个参数 str(待脱敏的字符串)、config(脱敏配置对象)和 fn(可选的自定义处理函数)。首先对 str 进行了 trim 操作并强制转换为字符串类型,确保输入数据的规范性。
- 若存在自定义函数 fn,则直接调用该函数并返回结果,给予开发者灵活定制脱敏逻辑的空间。
- 当 str 为空字符串时,直接返回空,简洁处理边界情况。
const desensitization = (str, config, fn) => {
str = str.trim() + "";
if (fn) {
return fn(str);
}
if (!str) {
return "";
}
…………
}
配置解析与默认值设定
- 从 config 中解构出 before(脱敏前保留的字符数)和 after(脱敏后保留的字符数),并提供了默认值 0。同时获取 type(数据类型标识,如 phone、email 等)和 ch(用于替换敏感字符的符号,默认为
*)。 - 根据不同的 type,对 before 和 after 进行了智能的默认值调整。例如对于 phone 类型,若字符串长度小于 11,则合理调整 before 为 2,after 为 2,以适配常见的手机号码格式;对于 email 类型,则根据 @ 符号分割字符串来确定 after 的值,精准定位域名部分进行保护。
let { before = 0, after = 0 } = config;
const { type, ch = "*" } = config;
const len = str.length;
switch (type) {
case "phone":
before = "3";
after = "4";
if (len < 11) {
before = 2;
after = 2;
}
break;
case "email":
before = "1";
if (str.split("@").length > 1) after = str.split("@")[1].length + 1;
break;
case "idCard":
before = "3";
after = "4";
break;
case "name":
before = "1";
after = len > 2 ? 1 : 0;
break;
}
构造正则进行脱敏
计算出需要显示的真实字符数 show,基于此构建正则表达式 reg。通过 replace 方法,按照正则表达式的匹配规则,用指定的 ch 符号替换中间的敏感字符,最终返回脱敏后的字符串。
const show = Math.max(len - before - after, 0);
const reg = [
new RegExp(`^(.{${before}}).{${show}}(.{${after}})$`),
`$1${ch.repeat(show)}$2`,
];
return str.replace(reg[0], reg[1]);
指令执行函数
参数提取与初始值获取
从指令绑定的值 binding.value 中提取出 params,进一步解构得到 config 和 fn。同时获取元素 el 的相关属性值,如 innerHTML、getAttribute 获取的原始值 originVal,并初始化 inputOriginVal,为后续处理用户输入做准备。
const params = binding.value || {};
const { config, fn } = params;
let inputOriginVal = "";
let originVal = el.getAttribute("desensitization-originVal") || "";
文本节点脱敏处理
当 el.innerHTML 存在时,意味着是对页面展示的文本内容进行脱敏。首先确保 originVal 的获取,若为空则从 el.innerText 中获取并设置为原始值,存储在自定义属性 desensitization-originVal 中,然后调用 desensitization 函数对 originVal 进行脱敏处理,并更新 el.innerHTML。
if (el.innerHTML) {
if (!originVal) {
el.setAttribute("desensitization-originVal", el.innerText);
originVal = el.innerText;
}
el.innerHTML = desensitization(originVal, config, fn);
return;
}
输入框交互处理
针对输入框元素,先移除之前可能存在的焦点和失焦事件监听器,避免重复绑定。然后分别为 focus 和 blur 事件添加监听器。在 focus 事件中,将输入框的值恢复为原始输入值 inputOriginVal;在 blur 事件中,获取当前输入值 value,更新 inputOriginVal,并调用 desensitization 函数对 value 进行脱敏处理,最后更新输入框的显示值。
if (fouceListerner) {
el.removeEventListener("focus", fouceListerner);
}
fouceListerner = el.addEventListener("focus", (event) => {
event.target.value = inputOriginVal;
});
if (blurListerner) {
el.removeEventListener("blur", blurListerner);
}
blurListerner = el.addEventListener("blur", (event) => {
const value = event.target.value;
inputOriginVal = value;
const newVal = desensitization(value, config, fn);
event.target.value = newVal;
});
指令生命周期钩子
bind
当指令首次绑定到元素上时执行。在此处调用 doDesensitization 函数,尝试进行脱敏处理,若出现错误则捕获并打印 bindErr 相关的错误信息,确保指令初始化的稳定性。
bind: function (el, binding) {
try {
doDesensitization(el, binding);
} catch (err) {
console.error("bindErr", err);
}
}
update
当指令所在组件的数据更新时触发。首先判断元素 el 的 innerText 是否存在,若存在则调用 doDesensitization 函数进行重新脱敏处理,同样对错误进行捕获并打印 updateErr,保证数据更新后的脱敏效果持续有效。
update: function (el, binding) {
if (!el.innerText) return;
try {
doDesensitization(el, binding);
} catch (err) {
console.error("updateErr", err);
}
}
unbind
当指令从元素上移除时执行。主要任务是移除之前绑定的焦点和失焦事件监听器。
unbind: function (el) {
if (fouceListerner) {
el.removeEventListener("focus", fouceListerner);
}
if (blurListerner) {
el.removeEventListener("blur", blurListerner);
}
}
指令使用
电话号码脱敏
内置有电话的脱敏类型,对于11位数的手机号码,脱敏方式是显示前3位和后4位;对于小于11位数的,比如7位数的电话号码,脱敏方式是显示前2位和后2位。


<div class="content">
<input
v-JDesensitization:params="params"
placeholder="输入需要脱敏的字符"
class="input-content"
/>
<div v-JDesensitization:params="params" class="div-content">
15012345678
</div>
</div>
<script>
export default{
data(){
return {
params:{
"config": {
"type": "phone",
"before": ,
"after": "",
"ch": "*"
},
"fn": ""
},
}
}
}
</script>
身份证号码脱敏
内置有身份证号码的脱敏类型,脱敏方式是显示前3位和后4位;


<div class="content">
<input
v-JDesensitization:params="params"
placeholder="输入需要脱敏的字符"
class="input-content"
/>
<div v-JDesensitization:params="params" class="div-content">
330621199909091234
</div>
</div>
<script>
export default{
data(){
return {
params:{
"config": {
"type": "idCard",
"before": ,
"after": "",
"ch": "*"
},
"fn": ""
},
}
}
}
</script>
姓名脱敏
内置有姓名的脱敏类型,两个字的姓名脱敏方式是显示第1位;大于两个字的姓名脱敏方式是显示第一个字和最后一个字。


<div class="content">
<input
v-JDesensitization:params="params"
placeholder="输入需要脱敏的字符"
class="input-content"
/>
<div v-JDesensitization:params="params" class="div-content">
张三风
</div>
</div>
<script>
export default{
data(){
return {
params:{
"config": {
"type": "name",
"before": ,
"after": "",
"ch": "*"
},
"fn": ""
},
}
}
}
</script>
邮箱脱敏
内置有邮箱的脱敏类型,脱敏方式是显示第一位和@及以后的字符。


<div class="content">
<input
v-JDesensitization:params="params"
placeholder="输入需要脱敏的字符"
class="input-content"
/>
<div v-JDesensitization:params="params" class="div-content">
123456@163.com
</div>
</div>
<script>
export default{
data(){
return {
params:{
"config": {
"type": "email",
"before": ,
"after": "",
"ch": "*"
},
"fn": ""
},
}
}
}
</script>
自定义脱敏
可以自定义脱敏代替字符,默认为*
修改 config 中的 ch 参数即可自定义脱敏代替字符
params: {
config: {
type: "phone",
before: "",
after: "",
ch: "~",
},
fn: "",
}

可以自定义脱敏字符头尾显示位数
修改 config 中的 before 和 after 参数即可自定义脱敏显示位数,type 需要修改为内置类型以外的,直接传空即可。
params: {
config: {
type: "",
before: "2",
after: "2",
ch: "~",
},
fn: "",
}

可以自定义脱敏函数进行脱敏
可以直接在参数的 fn 中传入一个脱敏函数,如果传入了 fn,其优先级最高,则其他参数都不会生效,会直接调用传入的 fn函数 进行脱敏,如:
(str) => {
return str[0] + '@'.repeat(str.length - 1);
}

应用场景
用户信息填写,输入框聚焦时显示真实数据供用户进行修改,失焦时显示脱敏后的数据。
(至于为什么会有这个场景,需要去问一下产品经理)
到此这篇关于基于JavaScript实现一个动态脱敏指令的文章就介绍到这了,更多相关JavaScript动态脱敏指令内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!
