前言
基本各自项目的需求,数据保密性,以及数据安全问题,这时候又需要考虑客户的使用,那么数据的脱敏就显的尤为重要了。
一、基于注解的脱敏策略思路?
- 自定义注解接口
自定义注解(示例说明):@DataTransForm(excute = true, stargy = IdCardStargy.class) 用于绑定脱敏属性的依据(且自定义设置脱敏策略类,是否脱敏) - 编写脱敏策略类
策略类(示例说明): IdCardStargy 实际的脱敏规则 - 脱敏工具类
脱敏工具类 (示例说明):Utils 用于项目服务层对接口数据处理 - 脱敏策略管理类
脱敏策略管理类(示例说明):Manager层利用反射解析对象Fileds,并获取有@DataTransForm注解的属性, 从策略类静态Map容器(项目启动时init维护)中获取对接的策略类,再利用策略调度器Handle去执行对应的实际策略类IdCardStargy,反射filed.set()方法实现脱敏! - 控制层使用
控制层实际使用示例。
二、代码展示
1.自定义注解
1.1 定义自定义注解两个属性1. excute() 2. stargy() 策略类(代码上有注释就不在一一赘述)
/**
* @program: Data-Transform
* @Author wang dong
* @Date 2021/4/12 17:05
* @Version 1.0
* @description: 脱敏需求处理自定义接口
*/
// 作用运行环境
@Retention(RetentionPolicy.RUNTIME)
// 作用的目标属性TYPE
@Target({ElementType.FIELD, ElementType.ANNOTATION_TYPE})
public @interface DataTransForm {
/**
* 是否脱敏 默认true
*/
boolean excute() default true;
/**
* 脱敏策略类
* @return
*/
Class<? extends TransformStargy> stargy();
}
2.脱敏策略类
2.1 脱敏策略类需要统一实现TransformStargy,实现统一方法transFrom(T args)
/**
* @program: Data-Transform
* @Author wang dong
* @Date 2021/4/12 17:23
* @Version 1.0
* @description: 策略类统一实现接口
*/
public interface TransformStargy<T> {
/**
* 加强实现 -- 实际脱敏规则
* @param args 脱敏参数
* @return
*/
T transFrom(T args);
}
2.2 对应的具体身份证属性的脱敏规则类
/**
* @program: Data-Transform
* @Author wang dong
* @Date 2021/4/13 11:29
* @Version 1.0
* @description: 身份证脱敏类
*/
@Component
public class IdCardStargy implements TransformStargy<String> {
//todo 实际身份证脱敏规则
public String transFrom(String args) {
return trans(args);
}
public static String trans(String args){
char[] chars = args.toCharArray();
if (chars.length > start + end) {
for (int i = start; i < chars.length - end; i++) {
chars[i] = symbol;
}
}
return new String(chars);
}
}
3.脱敏工具类
3.1 对应实际业务层直接采用工具类调用处理结果集合
/**
* @program: Data-Transform
* @Author wang dong
* @Date 2021/4/13 11:12
* @Version 1.0
* @description: 脱敏工具类 - return Object
*/
@Component
public class TransFormUtils {
@Autowired
TransFromManager transFromManager;
public <T> void Desensitization(T t) {
try {
transFromManager.transformType(t);
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
public <T> void DesensitizationList(List<T> tt) {
try {
for (Object object : tt) {
transFromManager.transformType(object);
}
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
4. 脱敏策略管理类
本次脱敏实现的两大核心内容为【策略类Map】和【调度器Handle】
4.1 策略类Map
4.1.1 脱敏策略类静态ContainerMap
4.1.2 项目启动时初始化ContainerMap,为后期可以根据key获取对应策略类做准备(需要注意:各个策略类都需要注入spring容器,否则无法维护ContainerMap init)。
/**
* @program: Data-Transform
* @Author wang dong
* @Date 2021/4/13 11:37
* @Version 1.0
* @description: 脱敏策略类map
*/
public class ContainerMap {
private final HashMap<String, TransformStargy> container = new HashMap();
private static ContainerMap instance = new ContainerMap();
private ContainerMap(){}
public static ContainerMap getInstance(){
return instance;
}
/**
* 添加策略类
*/
public ContainerMap addStargy(TransformStargy stargy){
Objects.requireNonNull(stargy, "不允许往策略容器中添加空策略");
String key = stargy.getClass().getSimpleName();
TransformStargy put = container.put(key, stargy);
if(null != put){
throw new RuntimeException("添加策略类重复");
}
return this;
}
/**
* 获取策略类
*/
public TransformStargy getStargy(Class<? extends TransformStargy> cls){
Objects.requireNonNull(cls, "获取策略类key值为空");
return container.get(cls.getSimpleName());
}
}
/**
* @program: Data-Transform
* @Author wang dong
* @Date 2021/4/13 11:15
* @Version 1.0
* @description: 启动类 维护策略集合
*/
@Configuration
public class TransFormApplication implements ApplicationContextAware {
private ApplicationContext applicationContext;
/**
* 启动时候维护 策略类
*/
@Bean
public ContainerMap containerMap() {
Map<String, TransformStargy> beansOfType = applicationContext.getBeansOfType(TransformStargy.class);
ContainerMap instance = ContainerMap.getInstance();
if (null != beansOfType && beansOfType.size() > 0) {
for (String key : beansOfType.keySet()) {
//todo 添加到容器
instance.addStargy(beansOfType.get(key));
}
}
return instance;
}
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
}
4.2 调度器Handle
4.2.1 策略类的执行调用封装成了handle。(也可以直接用对应策略类调用策略实现方法)
/**
* @program: Data-Transform
* @Author wang dong
* @Date 2021/4/13 11:07
* @Version 1.0
* @description: 脱敏策略调度类
*/
public interface TransformHandle {
<T> Object excute(T source, TransformStargy stargy);
}
/**
* @program: Data-Transform
* @Author wang dong
* @Date 2021/4/13 11:09
* @Version 1.0
* @description: 策略调度实现
*/
public class TransformHandleImpl implements TransformHandle {
public <T> Object excute(T source, TransformStargy stargy) {
return stargy.transFrom(source);
}
}
4.2.2 配置类:组装了Map和handle。(也可以直接用对应实现类调用方法)
/**
* @program: Data-Transform
* @Author wang dong
* @Date 2021/4/13 14:51
* @Version 1.0
* @description: 脱敏配置类 - 提供装配类
*/
@Configuration
public class TransformConfiguration {
private ContainerMap containerMap;
@Autowired
TransformHandleImpl transformHandle;
public TransformConfiguration(ContainerMap containerMap){
this.containerMap = containerMap;
}
@Bean
public TransFromManager transFromManager(){
return new TransFormManagerImpl(containerMap, handle());
}
@Bean
public TransformHandle handle(){
return new TransformHandleImpl();
}
}
4.3 策略类Manager
4.3.1 脱敏策略管理接口
4.3.2 脱敏策略管理实现类:
4.3.2.1 通过反射获取属性集合遍历
4.3.2.1 找到带有注解标记的属性,拿到注解信息
4.3.2.1 通过注解信息里面的策略类class找到Map中的策略类实体对象
4.3.2.1 通过通过调度器Handle调度到对应的策略实现类,实现脱敏。
/**
* @program: Data-Transform
* @Author wang dong
* @Date 2021/4/13 13:56
* @Version 1.0
* @description: 脱敏策略管理
*/
public interface TransFromManager {
Object transformType(Object object) throws IllegalAccessException;
}
/**
* @program: Data-Transform
* @Author wang dong
* @Date 2021/4/13 14:07
* @Version 1.0
* @description: 脱敏策略类实现类
*/
@Service
public class TransFormManagerImpl implements TransFromManager {
private ContainerMap containerMap;
private TransformHandle transformHandle;
public TransFormManagerImpl(ContainerMap containerMap, TransformHandle transformHandle) {
Objects.requireNonNull(containerMap, "策略容器不能为空");
Objects.requireNonNull(transformHandle, "调度器不能为空");
this.containerMap = containerMap;
this.transformHandle = transformHandle;
}
@Override
public Object transformType(Object object) throws IllegalAccessException {
Objects.requireNonNull(object, "muset be not null!");
//此处拿到对象,应该遍历对象所有变量的注解,并使用注解名称获取Map中对应的策略进行数据转换
List<Field> allField = ReflectUtil.getFieldByCurrentAndSuper(object.getClass());
if (null != allField && allField.size() > 0) {
for (int i = 0; i < allField.size(); i++) {
Field field = allField.get(i);
// 设置可访问私有属性
field.setAccessible(true);
if (field.getType() != null && field.getType().getClass().getClassLoader() != null) {
Object o = field.get(object);
if (o != null) {
transformType(o);
}
}
DataTransForm mergedAnnotation = AnnotatedElementUtils.getMergedAnnotation(field, DataTransForm.class);
if (null == mergedAnnotation || (mergedAnnotation != null && !mergedAnnotation.excute())) {
continue;
}
//
TransformStargy stargy = containerMap.getStargy(mergedAnnotation.stargy());
// Object excute = stargy.transFrom(field.get(object));
Object excute = transformHandle.excute(field.get(object), stargy);
// todo 脱敏通过反射的替换
field.set(object, excute);
}
}
return object;
}
}
5.使用
- 控制层编写接口,调用测试, 亲测有效,附上结果
/**
* @program: Data-Transform
* @Author wang dong
* @Date 2021/4/13 20:04
* @Version 1.0
* @description: 控制
*/
@RestController
public class TrnasformController {
@Autowired
TransFormUtils transFormUtils;
@GetMapping("/test")
public Student getStudent(){
Student student = new Student()
.setAge(11)
.setHigh("176cm")
.setName("司藤")
.setSex(1)
.setIdCard("612324199312226732");
System.out.println(student);
transFormUtils.Desensitization(student);
System.out.println("脱敏后数据:" + student);
return student;
}
}
- 实体对象只需要添加注解,以及策略实现类。
/**
* @program: Data-Transform
* @Author wang dong
* @Date 2021/4/12 17:53
* @Version 1.0
* @description: 测试对象
*/
@Data
@Accessors(chain = true)
public class Student {
public Integer age;
@DataTransForm(excute = true, stargy = NameStargy.class)
public String name;
@DataTransForm(excute = true, stargy = IdCardStargy.class)
public String idCard;
public String high;
public Integer sex;
}
- 结果
总结
以上就是今天要讲的内容,本文仅仅是就从代码实现层面讲解了脱敏需求的实现。适合于后期数据优化,优点是代码改动少,但是需要注意接收到该接口数据的地方,数据在操作时需要去敏处理否则会报错哦。- 附件
/**
* @program: Data-Transform
* @Author wang dong
* @Date 2021/4/13 20:04
* @Version 1.0
* @Description 反射工具类
**/
public class ReflectUtil {
/**
* description: 从当前以及父类中获取全部字段
* @param clazz 属性所在类
* @return java.lang.reflect.Field
*/
public static List<Field> getFieldByCurrentAndSuper(Class<?> clazz) {
List<Field> fields = new ArrayList<>();
getFieldByCurrentAndSuper(clazz, fields);
return fields;
}
/**
* description: 从当前以及父类中获取全部字段
* @param clazz 属性所在类
* @return java.lang.reflect.Field
*/
private static List<Field> getFieldByCurrentAndSuper(Class<?> clazz, List<Field> fields) {
Field[] declaredFields = clazz.getDeclaredFields();
for (Field declaredField : declaredFields) {
fields.add(declaredField);
}
if (!clazz.equals(Object.class)) {
return getFieldByCurrentAndSuper(clazz.getSuperclass(), fields);
}
return fields;
}
}