0
点赞
收藏
分享

微信扫一扫

Springboot AOP实现指定敏感字段数据加密


1. 插入数据 自定义注解方式  对 指定接口方法 的 参数的指定字段进行 加密存储;

2.对数据内的加密数据,进行解密返回

先看看效果 : 

数据存入数据库表内, 手机号phone和邮箱email 属于敏感数据,我们需要密文存储 : 

查询解密返回:

1.  自定义注解 加密标识注解  NeedEncrypt.java :

import java.lang.annotation.*;

/**
* @Author JCccc
* @Description 需加密
* @Date 2021/7/23 11:55
*/
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface NeedEncrypt {


}
2.自定义注解 需加密字段标识注解 EncryptField.java :/**
* @Author JCccc
* @Description
* @Date 2021/7/23 11:55
*/
@Target({ElementType.FIELD,ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
public @interface EncryptField {

String[] value() default "";
}
3.加密逻辑的aop处理器 EncryptAspect.java :import com.elegant.dotest.aop.annotation.EncryptField;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.jasypt.encryption.StringEncryptor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.lang.reflect.Field;
import java.util.Objects;

/**
* @Author JCccc
* @Description
* @Date 2021/9/14 8:55
*/
@Slf4j
@Aspect
@Component
public class EncryptAspect {

@Autowired
private StringEncryptor stringEncryptor;

@Pointcut("@annotation(com.elegant.dotest.aop.annotation.NeedEncrypt)")
public void pointCut() {
}

@Around("pointCut()")
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
//加密
encrypt(joinPoint);

return joinPoint.proceed();
}

public void encrypt(ProceedingJoinPoint joinPoint) {
Object[] objects=null;
try {
objects = joinPoint.getArgs();
if (objects.length != 0) {
for (int i = 0; i < objects.length; i++) {
//抛砖引玉 ,可自行扩展其他类型字段的判断
if (objects[i] instanceof String) {
objects[i] = encryptValue(objects[i]);
} else {
encryptObject(objects[i]);
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
}

/**
* 加密对象
* @param obj
* @throws IllegalAccessException
*/
private void encryptObject(Object obj) throws IllegalAccessException {

if (Objects.isNull(obj)) {
log.info("当前需要加密的object为null");
return;
}
Field[] fields = obj.getClass().getDeclaredFields();
for (Field field : fields) {
boolean containEncryptField = field.isAnnotationPresent(EncryptField.class);
if (containEncryptField) {
//获取访问权
field.setAccessible(true);
String value = stringEncryptor.encrypt(String.valueOf(field.get(obj)));
field.set(obj, value);
}
}
}

/**
* 加密单个值
* @param realValue
* @return
*/
public String encryptValue(Object realValue) {
try {
realValue = stringEncryptor.encrypt(String.valueOf(realValue));
} catch (Exception e) {
log.info("加密异常={}",e.getMessage());
}
return String.valueOf(realValue);
}


}
4. 插入user表 使用的 User.java :
import com.elegant.dotest.aop.annotation.EncryptField;
import lombok.Data;
import lombok.experimental.Accessors;

/**
* @Author JCccc
* @Description
* @Date 2021/9/14 8:55
*/
@Data
@Accessors(chain = true)
public class User {

private Integer id;
private String name;
@EncryptField
private String phone;
@EncryptField
private String email;
private Integer age;

}


可以看到,手机号phone 和 邮箱 email 两个字段,我们做了注解 @EncryptField 标识:

ok,我们写个测试接口,使用 @NeedEncrypt 注解标识这个接口需要进行加密拦截 :

使用postman调用一下测试接口:

 可以看下数据库,数据已经加密存储成功:

接下来是查询解密环节:

解密这里其实有些小讲究。 因为查询出来的数据有可能是单个实体,也可能是List (其实甚至是Map或者Set,又或者是 分页数据类)

所以本文将会以 最常用的 单个实体 、 List<实体> 为例子,去做解密。

1.解密自定义注解 NeedDecrypt.java :
 

/**
* @Author JCccc
* @Description 需解密
* @Date 2021/7/23 11:55
*/
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface NeedDecrypt {

}
2. 解密逻辑的aop处理器 DecryptAspect.java :
import com.elegant.dotest.aop.annotation.EncryptField;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.jasypt.encryption.StringEncryptor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Objects;

/**
* @Author JCccc
* @Description
* @Date 2021/9/14 8:55
*/
@Slf4j
@Aspect
@Component
public class DecryptAspect {

@Autowired
private StringEncryptor stringEncryptor;

@Pointcut("@annotation(com.elegant.dotest.aop.annotation.NeedDecrypt)")
public void pointCut() {
}

@Around("pointCut()")
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
//解密
Object result = decrypt(joinPoint);
return result;
}

public Object decrypt(ProceedingJoinPoint joinPoint) {
Object result = null;
try {
Object obj = joinPoint.proceed();
if (obj != null) {
//抛砖引玉 ,可自行扩展其他类型字段的判断
if (obj instanceof String) {
decryptValue(obj);
} else {
result = decryptData(obj);
}
}
} catch (Throwable e) {
e.printStackTrace();
}
return result;
}

private Object decryptData(Object obj) throws IllegalAccessException {

if (Objects.isNull(obj)) {
return null;
}
if (obj instanceof ArrayList) {
decryptList(obj);
} else {
decryptObj(obj);
}


return obj;
}

/**
* 针对单个实体类进行 解密
* @param obj
* @throws IllegalAccessException
*/
private void decryptObj(Object obj) throws IllegalAccessException {
Field[] fields = obj.getClass().getDeclaredFields();
for (Field field : fields) {
boolean hasSecureField = field.isAnnotationPresent(EncryptField.class);
if (hasSecureField) {
field.setAccessible(true);
String realValue = (String) field.get(obj);
String value = stringEncryptor.decrypt(realValue);
field.set(obj, value);
}
}
}

/**
* 针对list<实体来> 进行反射、解密
* @param obj
* @throws IllegalAccessException
*/
private void decryptList(Object obj) throws IllegalAccessException {
List<Object> result = new ArrayList<>();
if (obj instanceof ArrayList) {
for (Object o : (List<?>) obj) {
result.add(o);
}
}
for (Object object : result) {
decryptObj(object);
}
}


public String decryptValue(Object realValue) {
try {
realValue = stringEncryptor.encrypt(String.valueOf(realValue));
} catch (Exception e) {
log.info("解密异常={}", e.getMessage());
}
return String.valueOf(realValue);
}


}


然后我们对一个查询方法进行测试 :

 我们先试一下查询单条数据的:

使用@NeedDecrypt注解标记这个接口需要数据解密:

调用接口看看结果:

 然后是多条数据List<User> 返回的接口:

 调用接口测试看看结果:

举报

相关推荐

0 条评论