一、需求背景
虽然我经常在很多地方都说过,“前端脱敏,脱裤子放屁。”
但在用户需要截图,或者是数据极其敏感的情况下,不管后端是否脱敏,前端都还是应该给人脱敏一下的。
否则用户截图的话,就需要先截图,再用截图工具抹掉敏感信息后再发送,如果是极其敏感的数据的话。
所以需求来了:
要求在前端表格中的所有敏感数据列都需要脱敏,且需要支持用户点击查看原始数据。
二、需求目标
2.1 实现效果
2.2 如何使用
2.3 自定义脱敏
只需要在需要脱敏的属性上的 @Table()
装饰器中配置脱敏方式即可。当然,也支持直接自定义脱敏:
实现的效果就这样咯:
三、实现过程
3.1 @Table参数声明
我们需要在负责管理表格状态的 @Table()
装饰器的参数中添加下面一些如 2.3
截图中的配置项:
3.1.1 desensitize 脱敏类型
配置这个后才能自动脱敏,该配置是个枚举选项:
static TELEPHONE = new AirDesensitizeType(0, '座机号码', 3, 4)
static MOBILE = new AirDesensitizeType(1, '手机号码', 3, 4)
static ID_CARD = new AirDesensitizeType(2, '身份证号', 6, 4)
static BANK_CARD = new AirDesensitizeType(3, '银行卡号', 4, 4)
static CAR_NUMBER = new AirDesensitizeType(4, '车牌号', 2, 1)
static EMAIL = new AirDesensitizeType(5, '邮箱', 2, 2)
static CHINESE_NAME = new AirDesensitizeType(6, '中文名', 1, 1)
static ADDRESS = new AirDesensitizeType(7, '地址', 3, 0)
static IP_V4 = new AirDesensitizeType(8, 'IPv4地址', 0, 0)
static CUSTOM = new AirDesensitizeType(9, '自定义', 0, 0)
其中,末尾的两个数字代表脱敏的头部保留数量和尾部保留数量。
3.1.2 desensitizeHead
脱敏开始保留的数量,默认为0,可覆盖 3.1.1
中的 倒数第二 个参数。
3.1.3 desensitizeTail
脱敏结尾保留的数量,默认为0,可覆盖 3.1.1
中的 倒数第一 个参数。
3.1.4 desensitizeSymbol
脱敏符号,默认为 *
。
3.2 编写脱敏助手类
我们定义了一个 AirDesensitize
助手类,用于实现脱敏逻辑。
/**
* # 脱敏助手类
*
* @author Hamm.cn
*/
export class AirDesensitize {
/**
* ## `IPv4` 的块长度
*/
private static readonly IP_V4_PART_COUNT = 4
/**
* ## 字符串替换
*
* @param text 原始字符串
* @param head 头部保留长度
* @param tail 尾部保留长度
* @param symbol 中间替换的单个符号
* @return 替换后的字符串
*/
public static replace(text: string, head: number, tail: number, symbol: string): string {
if (head < 0 || tail < 0 || head + tail >= text.length) {
return text
}
let str = ''
for (let i = 0; i < text.length; i += 1) {
if (i >= head && i <= text.length - tail - 1) {
str += symbol
} else {
str += text[i]
}
}
return str
}
/**
* ## `IPv4` 地址脱敏
*
* @param ipv4 `IPv4` 地址
* @param symbol [可选]符号
* @return 脱敏后的 `IPv4` 地址
*/
public static desensitizeIpv4Address(ipv4: string, symbol = AirConstant.ASTERISK): string {
const strings = ipv4.split(AirConstant.DOT)
if (strings.length !== AirDesensitize.IP_V4_PART_COUNT) {
return ipv4
}
const temp = symbol + symbol + symbol
strings[1] = temp
strings[2] = temp
return strings.join(AirConstant.DOT)
}
/**
* ## 文本脱敏
*
* @param valueString 原始文本
* @param type 脱敏类型
* @param head 头部保留
* @param tail 尾部保留
* @param symbol 脱敏符号
* @return 脱敏后的文本
*/
public static desensitize(valueString: string, type: AirDesensitizeType, head = 0, tail = 0, symbol = AirConstant.ASTERISK): string {
switch (type.key) {
case AirDesensitizeType.BANK_CARD.key:
case AirDesensitizeType.ID_CARD.key:
case AirDesensitizeType.MOBILE.key:
case AirDesensitizeType.ADDRESS.key:
case AirDesensitizeType.CAR_NUMBER.key:
case AirDesensitizeType.EMAIL.key:
head = Math.max(type.head, head)
tail = Math.max(type.tail, tail)
break
case AirDesensitizeType.IP_V4.key:
return AirDesensitize.desensitizeIpv4Address(valueString, symbol)
case AirDesensitizeType.CHINESE_NAME.key:
head = Math.max(type.head, head)
tail = Math.max(type.tail, tail)
if (valueString.length <= head + tail) {
tail = 0
}
break
case AirDesensitizeType.TELEPHONE.key:
// 包含区号 前后各留4 不包含则各留2
// eslint-disable-next-line no-case-declarations
const isContainRegionCode = valueString.length > 8 ? 4 : 2
head = Math.max(isContainRegionCode, head)
tail = Math.max(isContainRegionCode, tail)
break
default:
}
return this.replace(valueString, head, tail, symbol)
}
}
3.3 给表格组件添加脱敏逻辑
<el-table-column>
<!-- ...省略... -->
<template v-if="columnConfig.desensitize">
{{ AirDesensitize.desensitize(getColumnRowData(row), columnConfig.desensitize, columnConfig.desensitizeHead, columnConfig.desensitizeTail, columnConfig.desensitizeSymbol) }}
</template>
<template v-else>
getColumnRowData(row)
</template>
<!-- ...省略... -->
</el-table-column>
我们添加一个 v-if
来判断是否需要脱敏,然后使用 AirDesensitize
助手类提供的 desensitize
方法来脱敏即可。
3.4 好的,搞定!
接下来其他同事就只需要美滋滋的去给属性的 @Table()
装饰器标参数即可~
四、总结
我们在 AirPower4T
这个开源项目中完整开源了上面的代码,本文只是提供一些思路供有兴趣的朋友参考。
Github: AirPower4T
今天的分享完毕~
Bye~