java自定义脱敏字段、脱敏规则

自定义脱敏字段、脱敏规则,不能像普通的脱敏一样在字段上加注解的实现方式,需要对方法返回每个属性进行判断,还要考虑对象嵌套问题

方案一:

/**
 * 数据权限注解拦截
 * @Author: zz
 **/

@Slf4j
@Aspect
@Component
public class DataPermissionAspect {


    /**
     * PointCut表示这是一个切点,@annotation表示这个切点切到一个注解上,后面带该注解的全类名
     * 切面最主要的就是切点,所有的故事都围绕切点发生
     * logPointCut()代表切点名称
     */
    @Pointcut("@annotation(DataPermission)")
    private void logPointCut() {
    }

    /**
     * 目标方法调用之前执行
     * 注意这里不能使用 ProceedingJoinPoint
     *
     * @param joinPoint
     */
    @Before("logPointCut()")
    public void doBefore(JoinPoint joinPoint) {
    }

    /**
     * 目标方法调用之后执行
     * 注意这里不能使用 ProceedingJoinPoint
     *
     * @param joinPoint
     */
    @After("logPointCut()")
    public void doAfter(JoinPoint joinPoint) {
    }

    /**
     * 环绕通知
     *
     * @param proceedingJoinPoint
     */
    @Around("logPointCut()")
    public Object doAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        // roleId 查询角色脱敏规则
        List<UcRoleColumnMask> columnMaskList = null;
        //继续执行方法
        Object result = proceedingJoinPoint.proceed();
        if (result != null && !CollectionUtils.isEmpty(columnMaskList)) {
            // 脱敏字段对应脱敏规则
            Map<String, UcRoleColumnMask> columnMaskMap = columnMaskList.stream().collect(Collectors.toMap(UcRoleColumnMask::getColumnAliss, UcRoleColumnMask -> UcRoleColumnMask));
            // 数据字段脱敏
            ObjectMapper objectMapper = new ObjectMapper();
            JsonNode jsonNode = null;
            // 分页结构数据处理
            if (result instanceof IPage) {
                IPage page = (IPage) result;
                if (!CollectionUtils.isEmpty(page.getRecords())) {
                    List<Object> list = new ArrayList<>();
                    for (Object record : page.getRecords()) {
                        jsonNode = objectMapper.valueToTree(record);
                        for (Map.Entry<String, UcRoleColumnMask> entry : columnMaskMap.entrySet()) {
                            if (jsonNode.has(entry.getKey())) {
                                // 节点处理
                                this.nodeDis((ObjectNode) jsonNode, entry);
                            }
                        }
                        list.add(objectMapper.convertValue(jsonNode, record.getClass()));
                    }
                    page.setRecords(list);
                    return page;
                }
            } else {
            	// 对象处理,或者讲对象转为map也可
                jsonNode = objectMapper.valueToTree(result);
                // 最外层
                for (Map.Entry<String, UcRoleColumnMask> entry : columnMaskMap.entrySet()) {
                    if (jsonNode.has(entry.getKey())) {
                        // 节点处理
                        this.nodeDis((ObjectNode) jsonNode, entry);
                    }
                }
                return objectMapper.convertValue(jsonNode, result.getClass());
            }
        }
        return result;

    }


    /**
    * 节点嵌套处理
    * @Param: [nextNode, columnMaskMap]
    * @Return: void
    * @Author: zz
    */
    private void nodeDis(ObjectNode jsonNode, Map.Entry<String, UcRoleColumnMask> entry) {
        // nextNode 只能拿到数据
        JsonNode nextNode = jsonNode.get(entry.getKey());
		// 根据数据类型处理
        if (nextNode instanceof ArrayNode) {
            Iterator<JsonNode> it = nextNode.iterator();
            while (it.hasNext()) {
                JsonNode arrayNode = it.next();
                // 递归
                nodeDis((ObjectNode) arrayNode, entry);
            }
        } else if (nextNode instanceof ObjectNode) {
                cloumnMask((ObjectNode) nextNode, entry);
        } else {
            cloumnMask(jsonNode, entry);
        }
    }


    /**
     * 敏感字段 规则处理
     * @param jsonNode
     * @param entry
     */
    private void cloumnMask(ObjectNode jsonNode, Map.Entry<String, UcRoleColumnMask> entry) {
        try {
            // 脱敏规则
            UcRoleColumnMask mask = entry.getValue();
            String maskStr = null;
            String value = jsonNode.get(entry.getKey()).textValue();
            if (value != null) {
                switch (mask.getType()) {
                    // todo 自己的脱敏规则
                }
                jsonNode.put(entry.getKey(), maskStr);
            } else {
                jsonNode.putNull(entry.getKey());
            }

        } catch (IllegalArgumentException e) {
            log.error("无脱敏字段{}:{}", entry.getKey(), e.getMessage());
        }
    }

}

方案二:
无意中看到MyBatis-Flex框架,他的“字段权限”实现方式,通过重写mybatis 的 BeanWrapper.set()方法实现,


import com.mybatisflex.core.table.TableInfo;
import com.mybatisflex.core.table.TableInfoFactory;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.property.PropertyTokenizer;
import org.apache.ibatis.reflection.wrapper.BeanWrapper;
import org.apache.ibatis.reflection.wrapper.ObjectWrapper;
import org.apache.ibatis.reflection.wrapper.ObjectWrapperFactory;

import java.util.Collection;
import java.util.Map;

public class EntityWrapperFactory implements ObjectWrapperFactory {

    @Override
    public boolean hasWrapperFor(Object object) {
        Class<?> objectClass = object.getClass();
        if (Map.class.isAssignableFrom(objectClass) ||
            Collection.class.isAssignableFrom(objectClass)) {
            return false;
        }
        // entityTableMap tableName: 列名、@SetListener[] (listener:BeanWrapper.set时调用类对应的listenr方法)
        return TableInfoFactory.ofEntityClass(objectClass) != null;
    }

    @Override
    public ObjectWrapper getWrapperFor(MetaObject metaObject, Object object) {
        return new FlexBeanWrapper(metaObject, object);
    }

    static class FlexBeanWrapper extends BeanWrapper {

        private final Object entity;
        private final TableInfo tableInfo;

        public FlexBeanWrapper(MetaObject metaObject, Object object) {
            super(metaObject, object);
            this.entity = object;
            this.tableInfo = TableInfoFactory.ofEntityClass(object.getClass());
        }

        @Override
        public void set(PropertyTokenizer prop, Object value) {
            // 根据自己的业务去处理value,flex是使用了设计模式:监听模式(具体实现自己请引包看源码)
            Object v = tableInfo.invokeOnSetListener(entity, prop.getName(), value);
            // mybatis 值设置
            super.set(prop, v);
        }

    }

}

以上代码有个问题,就是你只实现了逻辑,他怎么才能生效呢?需要将 EntityWrapperFactory 注入到spring 容器


import com.baomidou.mybatisplus.autoconfigure.ConfigurationCustomizer;
import com.baomidou.mybatisplus.core.MybatisConfiguration;

/**
 * @Author: zz
 * mybatis-plus:ObjectWrapperFactory 设置
 **/
//@Component
public class Z {
//    @Bean
    public ConfigurationCustomizer mybatisConfigurationCustomizer(){
        return new ConfigurationCustomizer() {
            @Override
            public void customize(MybatisConfiguration configuration) {
                configuration.setObjectWrapperFactory(new EntityWrapperFactory());
            }
        };
    }
}

你可能感兴趣的:(开发问题记录,java)