数据脱敏,参照阿里脱敏规则与国规
阿里巴巴脱敏规则
国家规则
我的思路是把字段代码(脱敏字段英文名),字段脱敏方式保存到数据库,一次性通过缓存读取,然后所有操作都在缓存进行,有修改数据的操作时修改缓存同步修改到数据库……当然这跟本文标题没啥关系就让我冒个泡.
关于脱敏方面,主要需要的参数是脱敏内容和方式,这个很简单,但是都是在代码中完成的,如果把所有字段的脱敏具体规则存到xml文件中,通过读取xml文件节点来反射类和方法,就提高了可拓展性(虽然我认为这个拓展性没啥大用)
首先是脱敏规则的xml文件
<?xml version="1.0" encoding="UTF-8"?>
<!-- type:0:阿里巴巴规则/1:国家规则 -->
<rules objectName="com.hanweb.cas.util.desensitize.DesentilizeUtil">
<!-- 所有method为reveal的为正则表达式脱敏 -->
<!-- regex为正则,desensilizeRule为替换 -->
<!-- 此部分可扩展 -->
<rule id="identifyCode" fieldName="身份证" method="reveal">
<regex type="0" regex="(\d{1})\d+(\w{1})" desensilizeRule="$1***********$2"/>
<regex type="1" regex="\d+(\w{4})" desensilizeRule="***********$1"/>
</rule>
<rule id="phoneNumber" fieldName="手机号" method="reveal">
<regex type="0" regex="(\d{3})\d{6}(\d{2})" desensilizeRule="$1***********$2"/>
<regex type="1" regex="(\d{3})\d{5}(\d{4})" desensilizeRule="$1***********$2"/>
</rule>
<rule id="email" fieldName="邮箱" method="reveal">
<regex type="0" regex="(\w{3})\w+@(\w+)" desensilizeRule="$1***********$2"/>
<regex type="1" regex="(\w{1})\w+@(\w+)" desensilizeRule="***********$2"/>
</rule>
<rule id="count" fieldName="支付宝/微信号" method="reveal">
<regex type="0" />
<regex type="1" regex="\d+(\w{4})" desensilizeRule="*******$1"/>
</rule>
<!-- 所有method为round的为显示前后各多少个字符 -->
<!-- regex为开头显示多少,desensilizeRule为结尾显示多少 -->
<!-- 此部分可扩展 -->
<rule id="passport" fieldName="护照" method="round">
<regex type="0" regex="1" desensilizeRule="1" />
<regex type="1" />
</rule>
<rule id="vehicleEngineNumber" fieldName="车辆发动机编号" method="round">
<regex type="0" regex="1" desensilizeRule="2" />
<regex type="1" />
</rule>
<rule id="vehicleFrameNumber" fieldName="车辆车架号" method="round">
<regex type="0" regex="3" desensilizeRule="3" />
<regex type="1" />
</rule>
<rule id="plateNumber" fieldName="车牌号" method="round">
<regex type="0" regex="2" desensilizeRule="3" />
<regex type="1" />
</rule>
<rule id="dateTime" fieldName="日期" method="round">
<regex type="0" />
<regex type="1" regex="0" desensilizeRule="4"/>
</rule>
<rule id="nickName" fieldName="昵称" method="round">
<regex type="0" regex="1" desensilizeRule="1"/>
<regex type="1" />
</rule>
<rule id="HMPhone" fieldName="港澳手机号" method="round">
<regex type="0" regex="2" desensilizeRule="2"/>
<regex type="1" />
</rule>
<rule id="TPhone" fieldName="台湾手机号" method="round">
<regex type="0" regex="2" desensilizeRule="3"/>
<regex type="1" />
</rule>
<!-- 所有method以handle开头的为单独方法 -->
<!-- regex与type同值,desensilizeRule不用 -->
<!-- 此部分不可扩展 -->
<rule id="address" fieldName="地址" method="handleAddress">
<regex type="0" regex="0" desensilizeRule=""/>
<regex type="1" regex="1" desensilizeRule="" />
</rule>
<rule id="name" fieldName="中文姓名" method="handleName">
<regex type="0" regex="0" desensilizeRule=""/>
<regex type="1" regex="1" desensilizeRule="" />
</rule>
<rule id="securityCard" fieldName="社保卡/医保卡" method="handleSecurityCard">
<regex type="0" />
<regex type="1" />
</rule>
<rule id="Tel" fieldName="固定电话" method="handleTel">
<regex type="0" />
<regex type="1" />
</rule>
<rule id="Tel" fieldName="固定电话" method="handleTel">
<regex type="0" />
<regex type="1" />
</rule>
<rule id="overSeaTel" fieldName="海外号码" method="handleOverSeaTel">
<regex type="0" regex="0" />
<regex type="1" />
</rule>
<rule id="other" fieldName="其他" method="">
<regex type="0" regex=""/>
<regex type="1" regex=""/>
</rule>
</rules>
id是字段名,method是反射的方法名,反射类在根节点
然后需要读取xml文件获取节点,这里主要使用jsoup,然后需要一个容器……实体类来存储读取的信息方便调用
/**
* @Description:
* @author: amethyst
* @date: 2019-11-16 11:56
* @update_by:
* @tags:
*/
public class Rules {
/**
* 字段代码
*/
private String fieldCode;
/**
* 字段名
*/
private String fielName;
/**
* 函数名
*/
private String method;
/**
* 脱敏方式
*/
private int type;
public String getFieldCode() {
return fieldCode;
}
public void setFieldCode(String fieldCode) {
this.fieldCode = fieldCode;
}
public String getFielName() {
return fielName;
}
public void setFielName(String fielName) {
this.fielName = fielName;
}
public String getMethod() {
return method;
}
public void setMethod(String method) {
this.method = method;
}
public int getType() {
return type;
}
public void setType(int type) {
this.type = type;
}
}
读取xml节点信息
import org.apache.commons.io.FileUtils;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.parser.Parser;
import org.jsoup.select.Elements;
import org.springframework.stereotype.Service;
import org.springframework.util.ResourceUtils;
import java.io.FileNotFoundException;
/**
*
* @Description: 根据xml脱敏规则反射
* @author: amethyst
* @date: 2019-11-16 15:18
* @update_by:
* @tags:
*/
@Service
public class RuleService {
/**
* XML文件路径
*/
private static Document xmlDoc;
/**
* 反射对象
*/
private static String reflectObject;
/**
* 正则
*/
private String regex;
/**
* 替换
*/
private String desensilizeRule;
public String getReflectObject() {
return reflectObject;
}
public String getRegex() {
return regex;
}
public void setRegex(String regex) {
this.regex = regex;
}
public String getDesensilizeRule() {
return desensilizeRule;
}
public void setDesensilizeRule(String desensilizeRule) {
this.desensilizeRule = desensilizeRule;
}
static {
try {
String xml= FileUtils.readFileToString( ResourceUtils.getFile("classpath:desensitize-rules.xml"));
xmlDoc = Jsoup.parse(xml, "", Parser.xmlParser());
reflectObject=xmlDoc.select("rules").attr("objectName");
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
/**
* @Description: 获取xml脱敏信息
* @Author: Amethyst
* @Params: [fieldCode, type]
* @return: com.hanweb.cas.entity.desensitize.Rules
* @Date: 2019/11/18 11:28
* @Update by:
*/
public Rules getRule(String fieldCode,int type){
Rules rules=new Rules();
Elements rule = xmlDoc.select("#"+fieldCode);
rules.setFieldCode(fieldCode);
rules.setFielName(rule.select("rule").attr("fieldName"));
rules.setMethod(rule.select("rule").attr("method"));
rules.setType(type);
Elements regex=rule.select("regex").eq(type);
this.setRegex(regex.select("regex").attr("regex"));
this.setDesensilizeRule(regex.select("regex").attr("desensilizeRule"));
return rules;
}
}
接下来就是对xml文件里方法的实现
/**
* @Notes: 这里的StringUtil和NumberUtil为自己写的工具类,实际只是分割字符和String转int而以,请自行修改
* @Description: 脱敏工具类
* @author: amethyst
* @date: 2019-11-14 16:37
* @update_by:
* @tags:
*/
public class DesentilizeUtil {
/**
* 阿里巴巴标准
*/
public static final int ALIBABA=0;
/**
* 国标
*/
public static final int NATION=1;
public String reveal(String content,String regex,String desensilizeRule){
return content.replaceAll(regex,desensilizeRule);
}
public String round(String content,String first,String last){
int index= NumberUtil.getInt(first);
int end=NumberUtil.getInt(last);
String hideContent=revealRound(content,index,end);
return hideContent;
}
public String handleName(String name,String ruleType,String b){
String handledName=name;
int len=name.length();
if (len<3){
handledName=DesentilizeUtil.revealRight(name,1);
}else {
handledName=DesentilizeUtil.revealRight(name,2);
}
return handledName;
}
public static String handleAddress(String address,String ruleType,String b){
int rule=NumberUtil.getInt(ruleType);
String handledAddress=address;
if (rule==0){
String[] temp=StringUtil.split(address,"区");
temp[1]=DesentilizeUtil.revealLeft(temp[1],0);
handledAddress=temp[0]+"区"+temp[1];
}else if (rule==1){
if (address.length()>=12){
handledAddress=revealLeft(address,6);
}else {
handledAddress=revealLeft(address,address.length()/2-1);
}
}else {
handledAddress=address;
}
return handledAddress;
}
public String handleSecurityCard(String content,String ruleType,String b){
int rule=NumberUtil.getInt(ruleType);
String handledSecurityCard=content;
int len=content.length();
if (rule==0){
if (len%3==0){
handledSecurityCard=round(content,len/3+"",len/3+"");
}else {
handledSecurityCard=round(content,len/3+1+"",len/3+"");
}
}
if (rule==1){
handledSecurityCard=round(content, 0+"",4+"");
}
return handledSecurityCard;
}
public String handleTel(String content,String ruleType,String b){
int rule=NumberUtil.getInt(ruleType);
String handledTel=content;
if (rule==0){
try {
String temp[]=content.split("-");
temp[1]=revealRight(temp[1],4);
handledTel=temp[0]+"-"+temp[1];
} catch (Exception e) {
e.printStackTrace();
}
}
return handledTel;
}
public String handleOverSeaTel(String content,String ruleType,String b){
int rule=NumberUtil.getInt(ruleType);
String handledOverSeaTel=content;
if (rule==0){
try {
String temp[]=handledOverSeaTel.split("-");
handledOverSeaTel=temp[0]+"-"+temp[1]+"-"+temp[2].replaceAll("\\d+(\\d{4})\\d{2}","***$1***");
} catch (Exception e) {
e.printStackTrace();
}
}
return handledOverSeaTel;
}
public static String revealLeft(String fieldContent, int index){
String hideContent=StringUtils.left(fieldContent,index);
return StringUtils.rightPad(hideContent,StringUtils.length(fieldContent),"*");
}
public static String revealRight(String fieldContent,int end){
String hideContent=StringUtils.right(fieldContent,end);
return StringUtils.leftPad(hideContent,StringUtils.length(fieldContent),"*");
}
public static String revealRound(String fieldContent,int index,int end){
String hideContent=StringUtils.left(fieldContent, index).concat(StringUtils.removeStart(StringUtils.leftPad(StringUtils.right(fieldContent, end),
StringUtils.length(fieldContent), "*"), "***"));
return hideContent;
}
}
准备得差不多了,下面就是通过获取参数反射调用方法
import org.springframework.util.ResourceUtils;
import java.io.File;
import java.io.FileNotFoundException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
/**
* @Description: 根据xml信息反射
* @author: amethyst
* @date: 2019-11-16 11:26
* @update_by:
* @tags:
*/
public class InstenceUtil {
public static String invokeMethod(String object,String methodName,Class<?>[] parameterTypes,Object[] args){
String result=null;
try {
Class<?> objectClass = Class.forName(object);
Object entity= objectClass.newInstance();
Method method=objectClass.getMethod(methodName,parameterTypes);
result = (String) method.invoke(entity,args);
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
return result;
}
}
我专写了一个类来存储调用方法时的字段代码和选择规则‘
/**
@Description:
@author: Amethyst
@date: 2019-11-18 10:25
@update_by:
@tags:
*/
public class SelectRule {
private String filedCode;
private int type;
public String getFiledCode() {
return filedCode;
}
public void setFiledCode(String filedCode) {
this.filedCode = filedCode;
}
public int getType() {
return type;
}
public void setType(int type) {
this.type = type;
}
}
接下来就是根据xml的脱敏信息来反射了
package com.hanweb.cas.service.desensitize;
import com.hanweb.cas.entity.desensitize.Rules;
import com.hanweb.common.util.FileUtil;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.parser.Parser;
import org.jsoup.select.Elements;
import org.springframework.stereotype.Service;
import org.springframework.util.ResourceUtils;
import java.io.FileNotFoundException;
/**
* @Description: 根据xml脱敏规则反射
* @author: amethyst
* @date: 2019-11-16 15:18
* @update_by:
* @tags:
*/
@Service
public class RuleService {
/**
* XML文件路径
*/
private static Document xmlDoc;
/**
* 反射对象
*/
private static String reflectObject;
/**
* 正则
*/
private String regex;
/**
* 替换
*/
private String desensilizeRule;
public String getReflectObject() {
return reflectObject;
}
public String getRegex() {
return regex;
}
public void setRegex(String regex) {
this.regex = regex;
}
public String getDesensilizeRule() {
return desensilizeRule;
}
public void setDesensilizeRule(String desensilizeRule) {
this.desensilizeRule = desensilizeRule;
}
static {
try {
String xml= FileUtil.readFileToString( ResourceUtils.getFile("classpath:desensitize-rules.xml"));
xmlDoc = Jsoup.parse(xml, "", Parser.xmlParser());
reflectObject=xmlDoc.select("rules").attr("objectName");
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
/**
* @Description: 获取xml脱敏信息
* @Author: Amethyst
* @Params: [fieldCode, type]
* @return: com.hanweb.cas.entity.desensitize.Rules
* @Date: 2019/11/18 11:28
* @Update by:
*/
public Rules getRule(String fieldCode,int type){
Rules rules=new Rules();
Elements rule = xmlDoc.select("#"+fieldCode);
rules.setFieldCode(fieldCode);
rules.setFielName(rule.select("rule").attr("fieldName"));
rules.setMethod(rule.select("rule").attr("method"));
rules.setType(type);
Elements regex=rule.select("regex").eq(type);
this.setRegex(regex.select("regex").attr("regex"));
this.setDesensilizeRule(regex.select("regex").attr("desensilizeRule"));
return rules;
}
}
最后的调用是最简单的
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
/**
* @Description: 信息脱敏
* @author: Amethyst
* @date: 2019-11-18 10:24
* @update_by:
* @tags:
*/
public class DesensitilizeRpcService{
private DoDesensilize doDesensilize=new DoDesensilize();
public String desensitize(String content, SelectRule rule) {
String s=null;
s=doDesensilize.doDesensilize(content,rule);
return s;
}
}