数据安全解决方案【拒绝转载】
一、数据传输过程安全
保证数据传输过程的安全性是非常重要的,特别是在互联网和网络通信中。在日常工作中,主要存在内部与内部、内部与外部的网络通信,而内部之间一般采用 RPC 框架进行通信,如常用的 Dubbo、OpenFeign;外部一般采用 http/https 进行通信,通过白名单、秘文传输、报文验签等方式,保证数据传输安全。
1.1 内部RPC调用
1.1.1 Dubbo
1. 定义加解密算法
Java 提供了强大的加密和解密功能,如标准的加密算法 AES、DES、RSA 等。
RSA加解密算法Util
public class RSAUtil {
private RSAUtil() {
}
/**
* RSA最大加密明文大小
*/
private static final int MAX_ENCRYPT_BLOCK = 117;
/**
* RSA最大解密密文大小
*/
private static final int MAX_DECRYPT_BLOCK = 128;
/**
* 算法
*/
private static final String ALGORITHM_NAME = "RSA";
/**
* MD5_RSA
*/
private static final String MD5_RSA = "MD5withRSA";
public static HashMap<String, String> getKeyPairMap() throws Exception {
KeyPairGenerator generator = KeyPairGenerator.getInstance(ALGORITHM_NAME);
generator.initialize(1024);
KeyPair keyPair = generator.generateKeyPair();
// 将公钥私钥进行base64编码、使用encodeBase64进行编译编码,并返回一个byte字节数组
String privateKey = Base64.getEncoder().encodeToString(keyPair.getPrivate().getEncoded());
String publicKey = Base64.getEncoder().encodeToString(keyPair.getPublic().getEncoded());
HashMap<String, String> keyMap = new HashMap<>();
keyMap.put("privateKey", privateKey);
keyMap.put("publicKey", publicKey);
System.out.println("getKeyPairMap>>>>>>>privateKey={}" + privateKey);
System.out.println("getKeyPairMap>>>>>>>publicKey={}" + publicKey);
return keyMap;
}
/**
* 获取私钥
*/
public static PrivateKey getPrivateKey(String privateKey) throws Exception {
byte[] decodedKey = Base64.getDecoder().decode(privateKey.getBytes());
PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(decodedKey);
KeyFactory keyFactory = KeyFactory.getInstance(ALGORITHM_NAME);
// 根据提供的密钥规范(密钥材料)生成公钥对象。
return keyFactory.generatePrivate(keySpec);
}
/**
* 获取公钥
*/
private static PublicKey getPublicKey(String publicKey) throws Exception {
byte[] decodedKey = Base64.getDecoder().decode(publicKey.getBytes());
X509EncodedKeySpec keySpec = new X509EncodedKeySpec(decodedKey);
KeyFactory keyFactory = KeyFactory.getInstance(ALGORITHM_NAME);
// 根据提供的密钥规范(密钥材料)生成公钥对象。
return keyFactory.generatePublic(keySpec);
}
/**
* RSA加密
*
* @param data 待加密数据
* @param publicKey 公钥
*/
public static String encrypt(String data, String publicKey) throws Exception {
//避免前端解密时,出现中文乱码情况,提前将数据进行中编码
data = URLEncoder.encode(data, "UTF-8");
//Cipher此类为加密和解密提供密码功能
//创建 Cipher 对象,应用程序调用 Cipher 的 getInstance 方法并将所请求转换 的名称传递给它
Cipher cipher = Cipher.getInstance(ALGORITHM_NAME);
//RSA加密,获取公钥
PublicKey publicKeyP = getPublicKey(publicKey);
/**
* Cipher对象需要初始化
* init(int opmode, Key key, AlgorithmParameterSpec params)
* (1)opmode :Cipher.ENCRYPT_MODE(加密模式)和 Cipher.DECRYPT_MODE(解密模式)
* (2)key :密匙,使用传入的盐构造出一个密匙,可以使用SecretKeySpec、KeyGenerator和KeyPairGenerator创建密匙,其中
* * SecretKeySpec和KeyGenerator支持AES,DES,DESede三种加密算法创建密匙
* * KeyPairGenerator支持RSA加密算法创建密匙
* (3)params :使用CBC模式时必须传入该参数,该项目使用IvParameterSpec创建iv 对象
*/
cipher.init(Cipher.ENCRYPT_MODE, publicKeyP);
//获取加密内容的长度
int inputLen = data.getBytes().length;
try (
ByteArrayOutputStream out = new ByteArrayOutputStream()) {
int offset = 0;
byte[] cache;
int i = 0;
// 对数据分段加密
while (inputLen - offset > 0) {
if (inputLen - offset > MAX_ENCRYPT_BLOCK) {
//加密或解密
cache = cipher.doFinal(data.getBytes(), offset, MAX_ENCRYPT_BLOCK);
} else {
cache = cipher.doFinal(data.getBytes(), offset, inputLen - offset);
}
out.write(cache, 0, cache.length);
i++;
offset = i * MAX_ENCRYPT_BLOCK;
}
// 获取加密内容使用base64进行编码,并以UTF-8为标准转化成字符串
// 加密后的字符串
return Base64.getEncoder().encodeToString(out.toByteArray());
} catch (Exception e) {
e.printStackTrace();
throw e;
}
}
/**
* RSA解密
*
* @param data 待解密数据
* @param privateKey 私钥
*/
public static String decrypt(String data, String privateKey) throws Exception {
Cipher cipher = Cipher.getInstance(ALGORITHM_NAME);
PrivateKey privateKeyP = getPrivateKey(privateKey);
cipher.init(Cipher.DECRYPT_MODE, privateKeyP);
byte[] dataBytes = Base64.getDecoder().decode(data);
int inputLen = dataBytes.length;
try (ByteArrayOutputStream out = new ByteArrayOutputStream()) {
int offset = 0;
byte[] cache;
int i = 0;
// 对数据分段解密
while (inputLen - offset > 0) {
if (inputLen - offset > MAX_DECRYPT_BLOCK) {
cache = cipher.doFinal(dataBytes, offset, MAX_DECRYPT_BLOCK);
} else {
cache = cipher.doFinal(dataBytes, offset, inputLen - offset);
}
out.write(cache, 0, cache.length);
i++;
offset = i * MAX_DECRYPT_BLOCK;
}
// 解密后的内容
return new String(out.toByteArray(), StandardCharsets.UTF_8);
} catch (Exception e) {
e.printStackTrace();
throw e;
}
}
/**
* RSA签名,签名
*
* @param data 待签名数据
* @param privateKey 私钥
*/
public static String sign(String data, PrivateKey privateKey) throws Exception {
//以主编码格式返回密钥,如果此密钥不支持编码,则返回null。
byte[] keyBytes = privateKey.getEncoded();
PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(keyBytes);
KeyFactory keyFactory = KeyFactory.getInstance(ALGORITHM_NAME);
//根据提供的密钥规范(密钥材料)生成私钥对象。
PrivateKey key = keyFactory.generatePrivate(keySpec);
//Signature类用于提供数字签名,用于保证数据的完整性,用非对称密钥中的私钥签名,公钥验签。
//与 Java Security 中其他基于算法的类一样,Signature 提供了与实现无关的算法,因此,调用方(应用程序代码)
// 会请求特定的签名算法并将它传回给已被正确初始化的 Signature 对象。如果需要,还可以通过特定的提供程序请求特定的算法
//getInstance指定签名算法
Signature signature = Signature.getInstance(MD5_RSA);
//初始化签署签名的私钥
signature.initSign(key);
//根据初始化类型,这可更新要签名或验证的字节
signature.update(data.getBytes());
//signature.sign()签署或验证所有更新字节的签名
return Base64.getEncoder().encodeToString(signature.sign());
}
/**
* 验签
*
* @param srcData 原始字符串
* @param publicKey 公钥
* @param sign 签名
*/
public static boolean verify(String srcData, PublicKey publicKey, String sign) throws Exception {
byte[] keyBytes = publicKey.getEncoded();
X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes);
KeyFactory keyFactory = KeyFactory.getInstance(ALGORITHM_NAME);
PublicKey key = keyFactory.generatePublic(keySpec);
Signature signature = Signature.getInstance(MD5_RSA);
signature.initVerify(key);
signature.update(srcData.getBytes());
//signature.verify签署或验证所有更新字节的签名
return signature.verify(Base64.getDecoder().decode(sign.getBytes()));
}
public static void main(String[] args) {
try {
String data = "data" +
" //Signature类用于提供数字签名,用于保证数据的完整性,用非对称密钥中的私钥签名,公钥验签。\n" +
" //与 Java Security 中其他基于算法的类一样,Signature 提供了与实现无关的算法,因此,调用方(应用程序代码)\n" +
" // 会请求特定的签名算法并将它传回给已被正确初始化的 Signature 对象。如果需要,还可以通过特定的提供程序请求特定的算法\n" +
" //getInstance指定签名算法";
// 生成密钥对
HashMap<String, String> keyPairMap = RSAUtil.getKeyPairMap();
String privateKey = keyPairMap.get("privateKey");
String publicKey = keyPairMap.get("publicKey");
System.out.println("私钥 => " + privateKey + "\n");
System.out.println("公钥 =>" + publicKey + "\n");
//RSA加密
String encryptData = RSAUtil.encrypt(data, publicKey);
System.out.println("加密后内容 => " + encryptData + "\n");
// RSA解密
String decryptData = RSAUtil.decrypt(encryptData, privateKey);
decryptData = java.net.URLDecoder.decode(decryptData, "UTF-8");
System.out.println("解密后内容 => " + decryptData + "\n");
// RSA签名
String sign = RSAUtil.sign(data, RSAUtil.getPrivateKey(privateKey));
// RSA验签
boolean result = RSAUtil.verify(data, RSAUtil.getPublicKey(publicKey), sign);
System.out.println("验签结果 => " + result + "\n");
} catch (Exception e) {
e.printStackTrace();
}
}
}
编写dubbo加解密算法SPI相关类
/**
* 安全算法接口
*/
@SPI("rsa")
public interface SecurityAlgorithm {
/**
* 加密
* @param plaintext plaintext
* @return 密文
*/
String encrypt(String plaintext) throws Exception;
/**
* 解密
* @param ciphertext 密文
* @return 明文
*/
String decrypt(String ciphertext) throws Exception;
/**
* 返回当前加解密模式
*/
String getModel();
}
public class RSASecurityAlgorithm implements SecurityAlgorithm, EnvironmentAware {
protected static final String MODEL = "rsa";
private static final String publicKey = "security-algorithm.rsa.publicKey";
private static final String privateKey = "security-algorithm.rsa.privateKey";
private Environment environment;
@Override
public String encrypt(String plaintext) throws Exception {
if (StringUtils.isEmpty(plaintext)) {
return plaintext;
}
return RSAUtil.encrypt(plaintext, environment.getProperty(publicKey));
}
@Override
public String decrypt(String ciphertext) throws Exception {
if (StringUtils.isEmpty(ciphertext)) {
return ciphertext;
}
return RSAUtil.decrypt(ciphertext, environment.getProperty(privateKey));
}
@Override
public String getModel() {
return MODEL;
}
@Override
public void setEnvironment(Environment environment) {
this.environment = environment;
}
}
**注意:**使用对应算法,需添加对应环境变量,如当前默认 rsa
算法,需在consumer、provider中添加配置:
security-algorithm:
rsa:
privateKey: MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAI7zqWeTCZW5bfiWsXd/+3U3Ia1Xoui+57tansouZoVnVkhyCbSF7jUv0ohO9jrbAxRgpqfIq9iSR+NgE6zbD4oUsdzoiwh1/ChW/1oewe+Qu7Zew6vYHUpvXZzDmLzwwYK3rR4dlyCxXyJzcXXwL1CJQBksdnW0H2AoHkPUfqyNAgMBAAECgYEAhtQxAry/fJWBsMbJKdHvZVYTkIAo6mctABvvyvhKwCaATiIpHzh3PXkWlHQNb+OkGvMyHOx7kPAfl8jFpfKTyxrpZFJALO0jIs3ebaZ/IeTRbsz0mSxyQJfSzeZNsaCZtycFf6O2AnmmJdCeZnAtHp1XVlZHFIhiaVD3e/qq04ECQQDBS9Ky6f5Cfnk9OfHZ6LyguFIIX9oAcTaziMdilX8aRYO/xuiT3hVExz8JBBkt/4PlHm7aIzzdH2ztNubt87ztAkEAvVMEU2vO4GyVeQ/+diu1fKMoLROaHBvtUy11iQ36BwaWxMpHtLrYbUhJTWTr8wePqu+soDili2zucOB+CUhaIQJAYo+QAwyPSkaE+XQ3Xt5ueCEkKwL8/Oa9drQSvrDt226ArGP3KZa/D4/tE2LUmADGtyhaa742BaZFe3tYKxLyTQJASwuvOUoQtREVpulaGol05TTfZg7RFbdUOZZZcrxhkYthFByCk/dEdv0iYVVR2gHzRvS+XrxVkpDZa1DxZMiTQQJAQAAf/wMPduTRaUa+UsbE83OT3s5YIQeoI1RaNFNlcsHq6VVdbVQVk6OeBIvih3BKm6jQg/e4Ui6Z7o64pNMing==
publicKey: MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCO86lnkwmVuW34lrF3f/t1NyGtV6Lovue7Wp7KLmaFZ1ZIcgm0he41L9KITvY62wMUYKanyKvYkkfjYBOs2w+KFLHc6IsIdfwoVv9aHsHvkLu2XsOr2B1Kb12cw5i88MGCt60eHZcgsV8ic3F18C9QiUAZLHZ1tB9gKB5D1H6sjQIDAQAB
2. 定义注解(标识需要加解密字段)
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RpcSecurityAlgorithm {
/**
* value值为 SecurityAlgorithm SPI key
* @return
*/
String value() default "";
}
3. 定义加解密Filter
AbstructSecurityAlgorithmFilter
public abstract class AbstructSecurityAlgorithmFilter implements Filter {
private final ExtensionLoader<SecurityAlgorithm> securityAlgorithmExtensionLoader = ExtensionLoader.getExtensionLoader(SecurityAlgorithm.class);
/**
* 解密
*
* @param arguments
* @throws Exception
*/
protected void decrypt(Object[] arguments) throws Exception {
if (arguments == null || arguments.length == 0) {
return;
}
for (Object argument : arguments) {
this.handle(argument, ((field, target) -> {
// 重新设值
field.set(argument, securityAlgorithmExtensionLoader.getExtension(this.getSecurityAlgorithm(field))
.decrypt((String) field.get(argument)));
}));
}
}
/**
* 加密
*
* @param arguments
* @throws Exception
*/
protected void encrypt(Object[] arguments) throws Exception {
if (arguments == null || arguments.length == 0) {
return;
}
for (Object argument : arguments) {
this.handle(argument, ((field, target) -> {
// 重新设值
field.set(argument, securityAlgorithmExtensionLoader.getExtension(this.getSecurityAlgorithm(field))
.encrypt((String) field.get(argument)));
}));
}
}
protected void handle(Object argument, SecurityFieldHandle securityFieldHandle) throws Exception {
if (argument == null) {
return;
}
if (argument instanceof Map) {
// 处理Map类型
Set<Map.Entry> set = ((Map) argument).entrySet();
if (set.isEmpty()) {
return;
}
for (Map.Entry entry : set) {
this.handle(entry, securityFieldHandle);
}
} else if (argument instanceof Collection) {
// 处理集合类型
Collection collection = (Collection) argument;
for (Object obj : collection) {
this.handle(obj, securityFieldHandle);
}
} else {
Field[] fields = argument.getClass().getDeclaredFields();
if (fields.length < 1) {
return;
}
for (Field field : fields) {
// 注解标识,且为String类型
if (!field.isAnnotationPresent(RpcSecurityAlgorithm.class) || !String.class.equals(field.getType())) {
continue;
}
field.setAccessible(true);
if (Objects.isNull(field.get(argument))) {
continue;
}
securityFieldHandle.handle(field, argument);
}
}
}
/**
* 获取安全加密算法
*
* @param field
* @return
*/
protected String getSecurityAlgorithm(Field field) {
RpcSecurityAlgorithm rpcSecurityAlgorithm = field.getAnnotation(RpcSecurityAlgorithm.class);
// 加解密算法,如果为空,则为默认算法
String securityAlgorithm = rpcSecurityAlgorithm.value();
if (StringUtils.isEmpty(securityAlgorithm)) {
securityAlgorithm = securityAlgorithmExtensionLoader.getDefaultExtensionName();
}
return securityAlgorithm;
}
}
SecurityAlgorithmConsumerFilter
@Activate(group = {CommonConstants.CONSUMER})
public class SecurityAlgorithmConsumerFilter extends AbstructSecurityAlgorithmFilter {
@Override
public Result invoke(Invoker<?> invoker, Invocation invocation) {
if (!(invocation instanceof RpcInvocation)) {
return invoker.invoke(invocation);
}
Result result;
try {
// 加密
encrypt(invocation.getArguments());
// execute method
result = invoker.invoke(invocation);
Object obj = result.getValue();
// 解密
decrypt(new Object[]{obj});
result.setValue(obj);
} catch (Exception e) {
throw new RuntimeException(e.getMessage());
}
return result;
}
}
SecurityAlgorithmProviderFilter
@Activate(group = {CommonConstants.PROVIDER})
public class SecurityAlgorithmProviderFilter extends AbstructSecurityAlgorithmFilter {
@Override
public Result invoke(Invoker<?> invoker, Invocation invocation) {
if (!(invocation instanceof RpcInvocation)) {
return invoker.invoke(invocation);
}
Result result;
try {
// 解密
decrypt(invocation.getArguments());
// execute method
result = invoker.invoke(invocation);
Object obj = result.getValue();
// 加密
encrypt(new Object[]{obj});
result.setValue(obj);
} catch (Exception e) {
throw new RuntimeException(e.getMessage());
}
return result;
}
}
2. 编写请求拦截器
1.2 外部Http调用
实现方法很多,可以使用 WebServlet 提供的Filter、Interceptor,也可使用 Spring IoC机制,大概步骤如下:
- 定义加解密算法
- 拦截外部http请求
- 进行请求秘文解密、验签
- 业务处理
- 加密返回报文进行响应返回
二、数据库(MySQL)数据加解密
1. 定义加解密算法
可以使用标准的加密算法 AES、DES、RSA 等。
2. 定义TypeHandler
public class SecurityFieldTypeHandler extends BaseTypeHandler<String> {
private static final String PUBLIC_KEY = "RSA公钥";
private static final String PRIVATE_KEY = "RSA私钥";
@Override
public void setNonNullParameter(PreparedStatement ps, int i, String parameter, JdbcType jdbcType) throws SQLException {
if (StringUtils.isEmpty(parameter)) {
return;
}
try {
// 加密
ps.setString(i, RSAUtil.encrypt(parameter, PUBLIC_KEY));
} catch (Exception exception) {
exception.printStackTrace();
throw new RuntimeException(exception.getMessage());
}
}
@Override
public String getNullableResult(ResultSet rs, String columnName) throws SQLException {
String value = rs.getString(columnName);
// 解密
return decrypt(value);
}
@Override
public String getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
String value = rs.getString(columnIndex);
// 解密
return decrypt(value);
}
@Override
public String getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
String value = cs.getString(columnIndex);
// 解密
return decrypt(value);
}
protected String decrypt(String value) {
if (value != null) {
try {
// 解密
value = RSAUtil.decrypt(value, PRIVATE_KEY);
} catch (Exception exception) {
throw new RuntimeException(exception.getMessage());
}
}
return value;
}
}
3. 应用
@TableName(value = "sys_user", autoResultMap = true)
@Data
public class UserBean implements Serializable {
private Long id;
private String name;
private String sex;
@TableField(typeHandler = SecurityFieldTypeHandler.class)
private String addr;
}
注意: 需添加 aotoResultMap=true
自动刷新结果集,默认 false。
4. 测试
数据库存储如下:
使用Mapper查询结果如下:
三、数据脱敏
数据脱敏(Data Masking)是一种保护敏感数据隐私的方法,通过对敏感数据进行处理,使其在非生产环境或非授权人员访问时无法识别敏感信息。数据脱敏旨在保护数据的隐私和安全,同时尽量保持数据的可用性。
以下是一些常见的数据脱敏技术和方法:
- 替换脱敏:将敏感数据替换为经过处理的伪随机值或占位符。例如,将真实的姓名替换为随机生成的名称,将身份证号替换为虚拟的身份证号码。
- 加密脱敏:对敏感数据进行加密,只有授权的用户或系统能够解密和访问数据。加密可以使用对称加密或非对称加密算法进行。
- 部分屏蔽脱敏:只显示数据的部分内容,屏蔽敏感信息的一部分。例如,只显示手机号码的后几位数字,屏蔽前面的数字。
数据脱敏应该根据具体的业务需求和数据保护要求来选择适当的方法和技术。在实施数据脱敏时,需要考虑数据的安全性、一致性和可用性,并确保脱敏后的数据仍能满足业务需求和分析要求。同时,需要合规性要求,如符合相关法律法规和隐私规范。
1. 定义脱敏算法
定义标准接口 IDesensitization:
public interface IDesensitization<T> {
T desensitization(Object value);
}
AbstractDesensitization:
public abstract class AbstractDesensitization<T> implements IDesensitization<T> {
/**
* 替换字符串
*
* @param str 需要替换的字符串
* @param startInclude 开始索引
* @param endExclude 接收索引
* @param replacedChar 替换字符
* @return
*/
protected String replace(CharSequence str, int startInclude, int endExclude, char replacedChar) {
if (StringUtils.isEmpty(str)) {
return str(str);
}
final int strLength = str.length();
if (startInclude > strLength) {
return str(str);
}
if (endExclude > strLength) {
endExclude = strLength;
}
if (startInclude > endExclude) {
// 如果起始位置大于结束位置,不替换
return str(str);
}
final char[] chars = new char[strLength];
for (int i = 0; i < strLength; i++) {
if (i >= startInclude && i < endExclude) {
chars[i] = replacedChar;
} else {
chars[i] = str.charAt(i);
}
}
return new String(chars);
}
protected String str(CharSequence cs) {
return null == cs ? null : cs.toString();
}
}
IdCardDefaultDesensitization:身份证脱敏算法
public class IdCardDefaultDesensitization extends AbstractDesensitization<String> {
@Override
public String desensitization(Object value) {
String idCard = (String) value;
// 500*****5331
if (StringUtils.isBlank(idCard)) {
return "";
} else if (7 > idCard.length()) {
return "";
} else {
return this.replace(idCard, 3, idCard.length() - 4, '*');
}
}
}
PhoneDefaultDesensitization:手机号脱敏算法
public class PhoneDefaultDesensitization extends AbstractDesensitization<String> {
@Override
public String desensitization(Object value) {
String phone = (String) value;
// 155***5331
if (StringUtils.isBlank(phone)) {
return "";
} else if (7 > phone.length()) {
return "";
} else {
return this.replace(phone, 3, phone.length() - 4, '*');
}
}
}
2. 定义脱敏算法工厂类(统一管理)
public class DesensitizationFactory {
private static final Logger logger = LoggerFactory.getLogger(DesensitizationFactory.class);
private static final ConcurrentHashMap<String, IDesensitization<?>> desensitizationMap = new ConcurrentHashMap<String, IDesensitization<?>>();
private DesensitizationFactory() {
}
static {
// 添加系统默认脱敏算法
DesensitizationFactory.register(new IdCardDefaultDesensitization());
DesensitizationFactory.register(new PhoneDefaultDesensitization());
}
public static DesensitizationFactory getInstance() {
return DesenerFactorySingletonHelper.INSTANCE;
}
/**
* 获取脱敏算法
* @param clazz
* @return
*/
public IDesensitization<?> getDesensitization(Class<?> clazz) {
IDesensitization<?> desensitization = DesensitizationFactory.desensitizationMap.get(clazz.getName());
if (desensitization == null) {
throw new RuntimeException(clazz.getName() + "desensitization is not exists");
}
return desensitization;
}
/**
* 注册脱敏算法
* @param desensitization
*/
public static void register(IDesensitization<?> desensitization) {
if (DesensitizationFactory.desensitizationMap.contains(desensitization.getClass().getName())) {
logger.info("system desensitization {}", DesensitizationFactory.desensitizationMap.keys());
throw new RuntimeException("desensitization[" + desensitization.getClass().getName() + "] already exists!!!!!");
}
DesensitizationFactory.desensitizationMap.put(desensitization.getClass().getName(), desensitization);
}
/**
* 单例模式
*/
private static class DesenerFactorySingletonHelper {
private static final DesensitizationFactory INSTANCE = new DesensitizationFactory();
}
}
3. 后端架构采用Jackson序列化方式
public class JacksonDesensitizeSerializer extends JsonSerializer<Object> implements ContextualSerializer {
private static final Logger log = LoggerFactory.getLogger(JacksonDesensitizeSerializer.class);
private IDesensitization<?> desensitization;
@Override
public void serialize(Object value, JsonGenerator gen, SerializerProvider provider) throws IOException {
try {
// 脱敏
gen.writeObject(desensitization.desensitization(value));
} catch (Exception e) {
gen.writeObject(value);
log.error("serialize>>>>exception", e);
}
}
@Override
public JsonSerializer<?> createContextual(SerializerProvider serializerProvider, BeanProperty beanProperty) throws JsonMappingException {
// 为空直接跳过
if (beanProperty == null) {
return serializerProvider.findNullValueSerializer(beanProperty);
}
/**********************获取属性上的注解属性,同时返回一个合适的序列化器***************************/
// 获取自定义注解
DesensitizationTag annotation = beanProperty.getAnnotation(DesensitizationTag.class);
// 注解不为空,且标注的字段为String
if (Objects.nonNull(annotation)) {
this.desensitization = DesensitizationFactory.getInstance().getDesensitization(annotation.value());
return this;
}
// 注解为空,寻找合适的序列化器进行处理
return serializerProvider.findValueSerializer(beanProperty.getType(), beanProperty);
}
}
4. 后端架构采用Fastjson序列化方式
编写对应ValueFilter,添加到 Fastjson 对应配置即可。
public class FastjsonDesensitizeValueFilter implements ValueFilter {
private static final Logger log = LoggerFactory.getLogger(FastjsonDesensitizeValueFilter.class);
@Override
public Object process(Object object, String name, Object value) {
if (Objects.isNull(value)) {
return value;
}
try {
Field field = object.getClass().getDeclaredField(name);
field.setAccessible(true);
DesensitizationTag annotation = field.getAnnotation(DesensitizationTag.class);
if (annotation == null || value == null) {
return value;
}
value = DesensitizationFactory.getInstance().getDesensitization(annotation.value());
} catch (Exception e) {
log.error("fastjsonDesensitizeValueFilter field class:{},name:{},value:{}", object.getClass(), name, value);
}
return value;
}
}
5. 提供直接脱敏处理器
public class DesensitizeHandler {
private static final Logger logger = LoggerFactory.getLogger(DesensitizeHandler.class);
private DesensitizeHandler() {
}
public static void desensitize(Object obj) {
if (obj == null) {
return;
}
if (obj instanceof Map) {
Set<Map.Entry> set = ((Map) obj).entrySet();
if (set.isEmpty()) {
return;
}
for (Map.Entry entry : set) {
DesensitizeHandler.desensitize(entry.getValue());
}
} else if (obj instanceof Collection) {
Collection collection = (Collection) obj;
for (Object item : collection) {
DesensitizeHandler.desensitize(item);
}
} else {
Field[] fields = obj.getClass().getDeclaredFields();
if (fields.length < 1) {
return;
}
for (Field field : fields) {
Object value = null;
try {
field.setAccessible(true);
DesensitizationTag annotation = field.getAnnotation(DesensitizationTag.class);
value = field.get(obj);
if (annotation == null || value == null) {
continue;
}
IDesensitization<?> desensitization = DesensitizationFactory.getInstance().getDesensitization(annotation.value());
field.set(obj, desensitization.desensitization(value));
} catch (Exception var12) {
logger.error("desensitizeHandle field class:{},name:{},value:{}", obj.getClass().getName(), field.getName(), value);
}
}
}
}
}