UNPKG

maskio

Version:

string and object type data manipulation mask library

297 lines (286 loc) 11.6 kB
'use strict'; /****************************************************************************** Copyright (c) Microsoft Corporation. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ***************************************************************************** */ /* global Reflect, Promise, SuppressedError, Symbol, Iterator */ var __assign = function() { __assign = Object.assign || function __assign(t) { for (var s, i = 1, n = arguments.length; i < n; i++) { s = arguments[i]; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p]; } return t; }; return __assign.apply(this, arguments); }; typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) { var e = new Error(message); return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e; }; exports.EPresetType = void 0; (function (EPresetType) { /** 全掩码 */ EPresetType["ALL"] = "all"; /** 银行卡 */ EPresetType["BANK_CARD"] = "bankCard"; /** 证件号 */ EPresetType["ID_CARD"] = "idCard"; /** 姓名 */ EPresetType["USER_NAME"] = "userName"; /** 邮箱 */ EPresetType["EMAIL"] = "email"; /** 电话 */ EPresetType["PHONE"] = "phone"; /** 固定电话 */ EPresetType["LANDLINE"] = "landline"; /** 金额 */ EPresetType["AMOUNT"] = "amount"; /** 验证码 */ EPresetType["VERIFICATION"] = "verification"; /** 地址 */ EPresetType["ADDRESS"] = "address"; /** IP地址 */ EPresetType["IP"] = "ip"; /** URL */ EPresetType["URL"] = "url"; /** 车牌号 */ EPresetType["LICENSE_PLATE"] = "licensePlate"; })(exports.EPresetType || (exports.EPresetType = {})); /** * 定义掩码规则 * @param all 全掩码: 匹配所有内容并用掩码字符替换。 * @param bankCard 银行卡: 掩码银行卡号,显示最后4位。 * @param idCard 证件号: 掩码证件号,显示前3位和后2-4位。 * @param userName 姓名: 掩码姓名,显示最后一位。 * @param email 邮箱: 掩码邮箱,保留前两个字符和后缀。 * @param phone 电话: 掩码不同地区的电话号码,显示特定的数字。 * @param landline 固定电话: 掩码固定电话,显示区号和后两位。 * @param amount 金额: 全掩码金额信息。 * @param verification 验证码: 全掩码验证码。 * @param address 地址: 全掩码地址信息。 * @param ip IP地址: 掩码IP地址,显示前3段。 * @param url URL: 掩码URL,显示域名和路径。 * @param licensePlate 车牌号: 掩码车牌号,显示最后两位。 */ var maskRules = { // 全掩码 all: ['^(.*)$', '{{*$1}}'], // 匹配所有内容并用掩码字符替换 // 银行卡号:*(实际位数)+显示最后4位,如**********8639 bankCard: ['^(.*)(.{4})$', '{{*$1}}$2'], // 匹配银行卡号,掩码前面的数字 // 证件号:显示前3位+ *(实际位数)+后2-4位+*(最后1位屏蔽) idCard: ['^(.{3})(.*)(.{3})(.{1})$', '$1{{*$2}}$3{{*$4}}'], // 匹配证件号,掩码中间部分 // 姓名:显示最后一位 userName: ['^(.*)(.{1})$', '{{*$1}}$2'], // 匹配姓名,掩码前面的字符 // 掩码邮箱地址,保留前两个字符和后缀 email: { // 如果@前面少于3位,则都显示,再@前面加***,如ab@111.com,显示为 ab***@111.com '^.{1,2}@.*$': ['^(.{1,2})@(.*)$', '$1***@$2'], // 如果@前面多于3位,则掩码@前面3个字符 '^.*@.*$': ['^(.*)(.{3})@(.*)$', '$1{{*$2}}@$3'], // 都匹配不上就全掩码 '^.*$': 'all' }, // 掩码电话号码,保留特定的数字 phone: { // 大陆手机号:1开头,后面跟10个数字,显示前3位和后2位,其他掩码 '^1[0-9]{10}': ['^([0-9]{3})([0-9]*)([0-9]{2})$', '$1{{*$2}}$3'], // 香港手机号:9或6开头后面跟7位数字,显示前2位和后2位,其他掩码 '^([6|9])\\d{7}$': ['^([0-9]{2})([0-9]*)([0-9]{2})$', '$1{{*$2}}$3'], // 澳门手机号:66或68开头后面跟5位数字,显示前2位和后2位,其他掩码 '^[6]([8|6])\\d{5}$': ['^([0-9]{2})([0-9]*)([0-9]{2})$', '$1{{*$2}}$3'], // 台湾手机号:0开头,9开头后面跟8位数字,显示前2位和后2位,其他掩码 '^[0][9]\\d{8}$': ['^([0-9]{2})([0-9]*)([0-9]{3})$', '$1{{*$2}}$3'], // 其他手机号:显示前2和后2 '^.*$': ['^([0-9]{2})([0-9]*)([0-9]{2})$', '$1{{*$2}}$3'] }, // 固定电话掩码规则 landline: { // 默认固定电话,显示区号和后两位 '0\\d{2,3}-[1-9]\\d{6,7}': ['^(0[0-9]{2,3})-([0-9]*)([0-9]{2})', '$1-{{*$2}}$3'], // 其他显示后两位 '^.*$': ['^(.*)(.{2})$', '{{*$1}}$2'] }, // 金额相关信息全掩码 amount: 'all', // 所有金额信息都进行全掩码 // 验证码全掩码 verification: 'all', // 所有验证码信息都进行全掩码 // 地址全掩码 address: 'all', // 所有地址信息都进行全掩码 // IP地址:显示最后一段 ip: ['^(\\d+\\.\\d+\\.\\d+\\.)(\\d+)$', '$1***'], // 掩码IP地址,显示前3段 // URL:显示域名和路径,掩码查询参数 url: ['^(https?://[^/?]+)(/.*)?$', '$1/***'], // 掩码URL,显示域名和路径 // 车牌号:显示前缀两位 licensePlate: ['^(.{2})(.*)$', '$1{{*$2}}'] // 掩码车牌号,显示最后两位 }; // 导出预设选项 var preset = __assign({}, maskRules); /** * 字符串掩码函数 * @param text 要掩码的字符串 * @param config 配置,三种格式 * * 1. 字符串:会匹配对应的预设格式 * 2. 对象:会根据key匹配text找到对应配置 * 3. 数组:第一项是正则匹配,第二项是需要替换的值,特殊情况:第二项如{{*$1}}的字符串,会使用*来获取$1的长度 * * config示例: * 'all' → text: 1234567890,输出**********,根据text的长度进行掩码 * {'^[0-9]$': ['^(.*)$', '***'], '^[a-z]$': ['^(.*)$', '*****']} → text: 1234,输出 ***,text:abcd,输出 ***** * ['^([0-9]{3})(.*)$', '$1{{$2}}'] → text: 123456789,输出 ******789 * * @param defaultConfig 可选的默认配置 * * @returns 掩码后的字符串或原始字符串 */ var maskText = function (text, config, defaultConfig) { // 1. 获取最终的替换规则 var finalConfig = resolveConfig(text, config, defaultConfig); // 2. 没有符合的掩码规则,直接返回原始字符串 if (!Array.isArray(finalConfig) || finalConfig.length !== 2) { console.warn("".concat(JSON.stringify(config), "\u914D\u7F6E\u65E0\u6548\uFF0C\u65E0\u6CD5\u63A9\u7801")); return text; } // 3. 使用掩码规则替换文本 return replaceText(text, finalConfig); }; /** * 解析掩码配置 * @param text 要掩码的字符串 * @param config 掩码配置 * @param defaultConfig 默认掩码配置 */ function resolveConfig(text, config, defaultConfig) { // 深拷贝默认配置,避免修改原始数据 var finalConfig = JSON.parse(JSON.stringify(config)); // 1. 如果是预设的掩码规则,直接返回 if (typeof finalConfig === 'string') { finalConfig = preset[finalConfig]; } // 2. 如果传入的是对象,根据key匹配text找到对应配置 if (!Array.isArray(finalConfig) && typeof finalConfig === 'object') { // 遍历对象,找到匹配的key var key = Object.keys(finalConfig).find(function (key) { return new RegExp(key).test(text); }); // 更新finalConfig为对应的值 finalConfig = finalConfig[key]; } // 3. 匹配的还是字符串 if (typeof finalConfig === 'string') { finalConfig = preset[finalConfig]; } // 4. 最终匹配的是数组,若不是,则采用默认配置 // finalConfig.length !== 2 -> 确保有效的掩码规则: [(掩码规则),(替换的字符串)] if (!Array.isArray(finalConfig) || finalConfig.length !== 2) { // 使用默认配置 finalConfig = defaultConfig; // 如果默认配置是字符串,则转换为预设的掩码规则 if (typeof finalConfig === 'string') { finalConfig = preset[finalConfig]; } } return finalConfig; } /** * 替换文本中的敏感信息 * * @param text - 要处理的原始文本 * @param config - 包含掩码规则的数组,格式为 [掩码规则, 替换字符串] * @returns 处理后的字符串,敏感信息已被替换 */ function replaceText(text, config) { // 替换文本中的敏感信息 var maskData = text.toString().replace(new RegExp(config[0]), config[1]); // 模板字符串解析、提取被 "{{" 和 "}}" 包围的内容 var repeatReg = /\{\{([^{}]+)\}\}/g; return maskData.replace(repeatReg, function (repaetText) { // 替换掉{{}}包裹的内容,并获取其中的内容 var text = repaetText.replace('{{', '').replace('}}', ''); // 第一个字符作为掩码的字符 var maskChar = text.charAt(0); // 去除第一个字符后的源数据 var source = text.slice(1); // 返回掩码后的字符串 return maskChar.repeat(source.length); }); } /** * 递归掩码JSON * @param obj - 待处理的对象或数组 * @param config - 配置项,键为路径,值为掩码配置或字段处理函数 * @returns 处理后的对象 */ var maskObject = function (obj, config) { return recursiveObject(obj, config, []); }; /** * 递归处理对象,支持掩码和自定义处理 * * @param obj - 待处理对象 * @param config - 配置项,键为路径,值为掩码配置或字段处理函数 * @param pathArr - 路径数组,默认为空数组 * @returns 处理后的对象 */ function recursiveObject(obj, config, pathArr) { // 判断是否是对象 if (typeof obj !== 'object') { return handleValue(obj, config, pathArr); } // 创建一个新的对象|数组 newObj var newObj = Array.isArray(obj) ? [] : {}; // 遍历obj, newObj进行赋值 Object.keys(obj).forEach(function (key) { pathArr.push(key); newObj[key] = recursiveObject(obj[key], config, pathArr); pathArr.pop(); }); return newObj; } /** * 处理基本类型值 * * @param value - 待处理值 * @param config - 配置对象 * @param pathArr - 路径数组 * @returns 处理后的值 */ function handleValue(value, config, pathArr) { // 1.判断value数字或字符串, 不是直接返回原值 if (typeof value === 'number' || typeof value === 'string') { // 1.1找到匹配的掩码规则 var maskType = config[Object.keys(config).find(function (key) { return new RegExp(key).test(pathArr.join('.')); })]; // 1.2掩码规则是个函数,返回调用函数处理的值 if (typeof maskType === 'function') { return maskType(value, pathArr); } // 1.3普通的正则表达式,掉用字符串掩码方法,返回掩码数据 if (maskType) { return maskText(value.toString(), maskType); } } // 2.未匹配到规则,返回原数据 return value; } exports.maskObject = maskObject; exports.maskText = maskText;