mybatis数据加密脱敏

在研发过程中某些业务场景是不允许存储明文的,例银行卡 身份证类。怎么实现自动加密解密呢?可以利用mybatis Interceptor 做处理,输入明文参数通过拦截器自动加密。查询出结果集自动解密。在web层展示可以对敏感字段加*处理。

支持mybatis-generator 生成的代码,考虑到可能重新生成会覆盖。支持配置指定字段加密解密,就不用担心代码丢失。自定义对象建议使用注解方式

直接上代码

/**
 * 
 * @author one.xu
 */
@Target({ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
public @interface EnableEncrypt {
}
@Target({ElementType.FIELD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface EncryptField {
    String value() default "";

    /**
     * 参数为map逗号连接需要加密的key
     * @return
     */
    String keys() default "";
}

/**
 * @author one.xu
 */
@Slf4j
@Intercepts({
        @Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class}),
        @Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class, CacheKey.class, BoundSql.class}),
        @Signature(type = Executor.class, method = "queryCursor", args = {MappedStatement.class, Object.class, RowBounds.class}),
        @Signature(type = Executor.class, method = "update", args = {MappedStatement.class, Object.class})})
public class DataSecurityInterceptor implements Interceptor {
    private EncryptHelper encryptHelper;
    /**
     * {"examples":{"com.xx.BusinessInfoExample":["app_id"]},
     * "resluts":{"com.xx.BusinessInfo":["appId"]}}
     */
    private Map<String, Set<String>> exampleClassConfigMap;
    private ConcurrentHashMap<String, Class> mapperClassMap = new ConcurrentHashMap<>();

    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        invocation.getArgs()[1] = parameterHand(invocation, true);
        Boolean hasCache = invocation.getArgs().length > 2 ? hasCache(invocation) : false;

        Object result = invocation.proceed();
        invocation.getArgs()[1] = parameterHand(invocation, false);
        if (hasCache) {
            return result;
        }
        return resultDecrypt(result, invocation);
    }

    /**
     * 只有 query,queryCursor 进入
     *
     * @param invocation
     * @return
     */
    boolean hasCache(Invocation invocation) {
        Object[] args = invocation.getArgs();
        MappedStatement ms = (MappedStatement) args[0];
        Object parameter = args[1];
        RowBounds rowBounds = (RowBounds) args[2];
//        ResultHandler resultHandler = (ResultHandler) args[3];
        Executor executor = (Executor) invocation.getTarget();
        CacheKey cacheKey;
        BoundSql boundSql;
        //由于逻辑关系,只会进入一次
        if (args.length <= 4) {
            //4 个参数时
            boundSql = ms.getBoundSql(parameter);
            cacheKey = executor.createCacheKey(ms, parameter, rowBounds, boundSql);
        } else {
            //6 个参数时
            cacheKey = (CacheKey) args[4];
            boundSql = (BoundSql) args[5];
        }
        return executor.isCached(ms, cacheKey);
    }


    @Override
    public Object plugin(Object target) {
        return Plugin.wrap(target, this);
    }


    @SneakyThrows
    @Override
    public void setProperties(Properties properties) {
        String encryptKey = properties.getProperty("encryptKey", "N2Y1ZjA0MjhhNjI2ZGNhOQ==");
        if (encryptKey.startsWith("${") && encryptKey.endsWith("}")) {
            encryptKey = SpringContextUtil.getApplicationContext().getEnvironment().getProperty(encryptKey.replace("${", "").replace("}", ""));
        }
        String encryptionService = properties.getProperty("encryptionService", DefaultEncryptionService.class.getName());
        Class<?> cls = Class.forName(encryptionService);
        if (!IEncryptionService.class.isAssignableFrom(cls)) {
            throw new RuntimeException("encryptionService 没有实现IEncryptionService接口");
        }
        this.encryptHelper = new EncryptHelper((IEncryptionService) cls.newInstance(), encryptKey);


        //解析example配置
        this.exampleClassConfigMap = new HashMap<>(16);
        String exampleClass = properties.getProperty("exampleClassConfig");
        if (StringUtils.isBlank(exampleClass)) {
            return;
        }
        JSONObject exampleClassConfigObject = JSON.parseObject(exampleClass);
        for (Map.Entry<String, Object> m1 : exampleClassConfigObject.entrySet()) {
            JSONObject tmp = exampleClassConfigObject.getJSONObject(m1.getKey());
            for (Map.Entry<String, Object> m2 : tmp.entrySet()) {

                String newKey = buildConfigKey(m2.getKey(), "examples".equals(m1.getKey()));
                Set<String> fields = exampleClassConfigMap.get(newKey);
                if (fields == null) {
                    fields = new HashSet<>();
                }
                fields.addAll(tmp.getJSONArray(m2.getKey()).toJavaList(String.class));
                this.exampleClassConfigMap.put(newKey, fields);
            }
        }

    }

    private String buildConfigKey(String key, boolean isExamples) {
        String newKey = isExamples ? "examples" : "resluts";
        return newKey + "#" + key;
    }

    private Object resultDecrypt(Object resultObject, Invocation invocation) throws Throwable {
        if (Objects.isNull(resultObject)) {
            return null;
        }
//        log.info("resultObject:{}",JSONObject.toJSONString(resultObject));
        if (resultObject instanceof List) {
            List<Object> resultList = (List<Object>) resultObject;
            if (CollectionUtils.isEmpty(resultList)) {
                return resultList;
            }
        }
        Set<String> decryptFields = getDecryptFields(resultObject);
        MappedStatement mappedStatement = (MappedStatement) invocation.getArgs()[0];
        Method method = getMapperMethod(mappedStatement);
        EnableEncrypt enableEncrypt = null;
        if (method != null && method.getAnnotation(EnableEncrypt.class) != null) {
            enableEncrypt = method.getAnnotation(EnableEncrypt.class);
        }
        if (enableEncrypt != null && StringUtils.isNotBlank(enableEncrypt.keys())) {
            decryptFields.addAll(Arrays.asList(enableEncrypt.keys().split(",")));
        }

        this.encryptHelper.execute(resultObject, decryptFields, false);
        return resultObject;
    }

    private Set<String> getDecryptFields(Object resultObject) {
        if (resultObject == null) {
            return new HashSet<>();
        }
        boolean isList = resultObject instanceof List;
        Object reallySingleObj = resultObject;
        if (isList) {
            List<Object> resultList = (List<Object>) resultObject;
            if (CollectionUtils.isEmpty(resultList)) {
                return new HashSet<>();
            }
            reallySingleObj = resultList.get(0);
        }
        //提取注解字段
        Set<String> decryptFields = this.encryptHelper.getEncryptField(reallySingleObj);
        //针对继承类处理
        Class tmpClass = reallySingleObj.getClass();
        while (!tmpClass.getName().equals(Object.class.getName())) {
            Set<String> fields = this.exampleClassConfigMap.get(buildConfigKey(tmpClass.getName(), false));
            if (fields != null) {
                decryptFields.addAll(fields);
            }
            tmpClass = tmpClass.getSuperclass();
        }
        return decryptFields;
    }

    private Object parameterHand(Invocation invocation, boolean isEncrypt) throws Throwable {
        Object parameter = invocation.getArgs()[1];
        MappedStatement mappedStatement = (MappedStatement) invocation.getArgs()[0];
        Method method = getMapperMethod(mappedStatement);
        if (parameter == null || method == null) {
            return parameter;
        }
        Object newParameter = parameter;
        //没有EnableEncrypt注解 且 Example,Result没有加密字段
        if (method.getAnnotation(EnableEncrypt.class) == null && !hasExampleOrResult(newParameter)) {
            return parameter;
        }
        log.debug("开始修改方法:{}入参", mappedStatement.getId());
        //扫描方法入参注解
        Map<String, Set<String>> paramAnnotation = searchParamAnnotation(method);
        //多个参数场景
        if (newParameter instanceof MapperMethod.ParamMap) {
            Map<String, Object> map = ((Map<String, Object>) newParameter);
            for (Map.Entry<String, Object> e : map.entrySet()) {
                Object value = e.getValue();
                if (value == null) {
                    continue;
                }
                //没有注解,且无Example Result
                if (!paramAnnotation.containsKey(e.getKey()) && !hasExampleOrResult(value)) {
                    continue;
                }
                if (value instanceof Map) {
                    encryptHelper.execute(value, paramAnnotation.get(e.getKey()), isEncrypt);
                } else if (value instanceof List) {
                    List list = (List) value;
                    if (CollectionUtils.isEmpty(list)) {
                        continue;
                    }
                    boolean isMap = list.get(0) instanceof Map;
                    if (isMap && !CollectionUtils.isEmpty(list)) {
                        encryptHelper.execute(value, paramAnnotation.get(e.getKey()), isEncrypt);
                    } else if (list.get(0).getClass().getClassLoader() != null) {
                        Set<String> encryptFields = getDecryptFields(list.get(0));
                        encryptHelper.execute(list, encryptFields, isEncrypt);
                    } else {
                        int i = 0;
                        for (Object o : list) {
                            list.set(i, encryptHelper.encrypt(String.valueOf(o)));
                            i = i + 1;
                        }
                    }
                } else if (hasExample(value)) {
                    handExample(value, isEncrypt);
                } else if (value.getClass().getClassLoader() != null) {
                    this.encryptHelper.execute(value, getDecryptFields(value), isEncrypt);
                } else {
                    map.put(e.getKey(), isEncrypt ? encryptHelper.encrypt(String.valueOf(value)) : encryptHelper.decrypt(String.valueOf(value)));
                }
            }
        } else if (newParameter instanceof Map) {
            Set<String> encryptFields = Sets.newHashSet();
            for (Set<String> s : paramAnnotation.values()) {
                encryptFields.addAll(s);
            }
            encryptHelper.execute(newParameter, encryptFields, isEncrypt);
        } else if (hasExample(newParameter)) {
            handExample(newParameter, isEncrypt);
        } else if (newParameter.getClass().getClassLoader() == null && paramAnnotation.size() == 1) {
            newParameter = isEncrypt ? encryptHelper.encrypt(String.valueOf(newParameter)) : encryptHelper.decrypt(String.valueOf(newParameter));
        } else {
            this.encryptHelper.execute(newParameter, getDecryptFields(newParameter), isEncrypt);
        }

        return newParameter;
    }

    private boolean hasExampleOrResult(Object parameterObject) {
        boolean isParamMap = parameterObject instanceof MapperMethod.ParamMap;
        if (!isParamMap) {
            return hasExample(parameterObject) || hasResult(parameterObject);
        }
        Map<String, Object> map = ((Map<String, Object>) parameterObject);
        for (Map.Entry<String, Object> e : map.entrySet()) {
            if (hasExample(e.getValue()) || hasResult(e.getValue())) {
                return true;
            }
        }
        return false;
    }

    private boolean hasExample(Object parameterObject) {
        if(parameterObject==null){
            return false;
        }
        return !CollectionUtils.isEmpty(exampleClassConfigMap.get(buildConfigKey(parameterObject.getClass().getName(), true)));
    }

    private boolean hasResult(Object parameterObject) {
        if (parameterObject == null) {
            return false;
        }
        return !CollectionUtils.isEmpty(getDecryptFields(parameterObject));
    }

    private boolean handExample(Object parameterObject, boolean isEncrypt) throws Exception {
        Set<String> fields = null;
        if (!(parameterObject instanceof MapperMethod.ParamMap)) {
            fields = exampleClassConfigMap.get(buildConfigKey(parameterObject.getClass().getName(), true));
            return handExampleParam(parameterObject, isEncrypt, fields);
        }
        Map<String, Object> map = ((Map<String, Object>) parameterObject);
        for (Map.Entry<String, Object> e : map.entrySet()) {
            fields = exampleClassConfigMap.get(buildConfigKey(e.getValue().getClass().getName(), true));
            if (fields != null) {
                return handExampleParam(e.getValue(), isEncrypt, fields);
            }
        }
        return false;
    }

    private boolean handExampleParam(Object parameterObject, boolean isEncrypt, Set<String> fields) throws Exception {

        if (fields == null) {
            return false;
        }
        Method method = parameterObject.getClass().getMethod("getOredCriteria");
        List oredCriterias = (List) method.invoke(parameterObject);
        if (CollectionUtils.isEmpty(oredCriterias)) {
            return false;
        }
        boolean result = false;
        for (Object oj : oredCriterias) {
            method = oj.getClass().getMethod("getCriteria");
            for (Object o : (List) method.invoke(oj)) {
                Field f = o.getClass().getDeclaredField("condition");
                f.setAccessible(true);
                Object v = f.get(o);
                aa:
                for (String f1 : fields) {
                    if (!v.toString().toUpperCase().replace("`", "").startsWith((f1 + " ").toUpperCase())) {
                        continue;
                    }
                    for (String dd : "value,secondValue".split(",")) {
                        f = o.getClass().getDeclaredField(dd);
                        f.setAccessible(true);
                        v = f.get(o);
                        if (v == null) {
                            break aa;
                        }
                        if (v instanceof List) {
                            List tList = (List) v;
                            for (int i = 0; i < tList.size(); i++) {
                                tList.set(i, isEncrypt ? encryptHelper.encrypt(tList.get(i).toString()) : encryptHelper.decrypt(tList.get(i).toString()));
                            }
                            v = tList;
                        } else {
                            v = isEncrypt ? encryptHelper.encrypt(v.toString()) : encryptHelper.decrypt(v.toString());
                        }
                        f.set(o, v);
                        if (!result) {
                            result = true;
                        }
                    }
                }
            }
        }
        return result;
    }

    /**
     * key-属性名
     * value-map类型时加密字段key
     *
     * @param method
     * @return
     */
    private Map<String, Set<String>> searchParamAnnotation(Method method) {

        Map<String, Set<String>> paramNames = Maps.newHashMap();
        if (method == null) {
            return paramNames;
        }
        Annotation[][] pa = method.getParameterAnnotations();
        Parameter[] parameters = method.getParameters();
        for (int i = 0; i < pa.length; i++) {
            for (Annotation annotation : pa[i]) {
                Parameter p = parameters[i];
                if (annotation instanceof EncryptField) {
                    EncryptField encryptField = (EncryptField) annotation;
                    Set<String> ff = null;
                    if (Map.class.isAssignableFrom(p.getType()) && StringUtils.isNotBlank(encryptField.keys())) {
                        ff = new HashSet<>(Arrays.asList(encryptField.keys().split(",")));
                    }
                    Param param = p.getAnnotation(Param.class);
                    String paramName = param == null ? p.getName() : param.value();
                    paramNames.put(paramName, ff);
                }
            }
        }
        return paramNames;
    }

    private Method getMapperMethod(MappedStatement mappedStatement) {
        Class<?> mapperClass = getMapperClass(mappedStatement);
        String methodName = mappedStatement.getId();
        methodName = methodName.substring(methodName.lastIndexOf('.') + 1);
        Method[] methods = mapperClass.getDeclaredMethods();
        Method method = null;
        for (Method m : methods) {
            if (m.getName().equals(methodName)) {
                method = m;
                break;
            }
        }
        return method;
    }

    @SneakyThrows
    private Class<?> getMapperClass(MappedStatement mappedStatement) {
        String mapperClassKey = mappedStatement.getId().substring(0, mappedStatement.getId().lastIndexOf('.'));
        Class<?> mapperClass = mapperClassMap.get(mapperClassKey);
        if (mapperClass == null) {
            mapperClassMap.putIfAbsent(mapperClassKey, Class.forName(mapperClassKey));
            mapperClass = mapperClassMap.get(mapperClassKey);
        }
        return mapperClass;
    }
}
/**
 * @author one.xu
 */
public interface IEncryptionService {
    String encrypt(String content, String password);

    String decrypt(String content, String password);
}
/**
 * @author one.xu
 */
public class DefaultEncryptionService implements IEncryptionService {
    @Override
    public String encrypt(String content, String password) {
        return AesUtil.encrypt(content, password);
    }

    @Override
    public String decrypt(String content, String password) {
        return AesUtil.decrypt(content, password);
    }
}
@Data
public class EncryptHelper {
    public EncryptHelper(IEncryptionService encryptionService, String encryptKey) {
        this.encryptionService = encryptionService;
        this.encryptKey = encryptKey;
    }

    private IEncryptionService encryptionService;
    private String encryptKey;

    public Set<String> getEncryptField(Object param) {
        Set<String> encryptFieldSet = Sets.newHashSet();
        Object object = param;
        boolean isList = object instanceof List;
        if (isList) {
            List<Object> resultList = (List<Object>) object;
            if (CollectionUtils.isEmpty(resultList)) {
                return encryptFieldSet;
            }
            object = resultList.get(0);
        }
        Class<?> objectClass = object.getClass();
        EnableEncrypt enableEncrypt = AnnotationUtils.findAnnotation(objectClass, EnableEncrypt.class);
        if (enableEncrypt == null) {
            return encryptFieldSet;
        }
        for (Field[] fields : getFields(objectClass)) {
            for (Field field : fields) {
                EncryptField encryptField = field.getAnnotation(EncryptField.class);
                if (encryptField == null) {
                    continue;
                }
                encryptFieldSet.add(field.getName());
            }
        }
        return encryptFieldSet;
    }

    private List<Field[]> getFields(Class<?> clazz) {
        List<Field[]> fieldsList = Lists.newArrayList();
        Class<?> resultClass = clazz;
        while (resultClass != null) {
            fieldsList.add(resultClass.getDeclaredFields());
            resultClass = resultClass.getSuperclass();
        }
        return fieldsList;
    }

    public <T> T execute(T result, Set<String> encryptFields, boolean isEncrypt) throws IllegalAccessException {
        if (result == null || CollectionUtils.isEmpty(encryptFields)) {
            return result;
        }
        boolean isList = result instanceof List;
        if (!isList) {
            return executeSingle(result, encryptFields, isEncrypt);
        }
        List<Object> resultList = (List<Object>) result;
        for (Object obj : resultList) {
            executeSingle(obj, encryptFields, isEncrypt);
        }
        return result;
    }

    private <T> T executeSingle(T result, Set<String> encryptFields, boolean isEncrypt) throws IllegalAccessException {
        if (result == null || CollectionUtils.isEmpty(encryptFields)) {
            return result;
        }
        if (result instanceof Map) {
            Map<String, Object> map = ((Map<String, Object>) result);
            for (String f : encryptFields) {
                Object o = map.get(f);
                if (o == null) {
                    continue;
                }
                if (o instanceof List) {
                    List tmpList = (List) o;
                    if (CollectionUtils.isEmpty(tmpList)) {
                        continue;
                    }
                    Set<String> encryptFields2 = getEncryptField(tmpList.get(0));
                    if (CollectionUtils.isEmpty(encryptFields2)) {
                        continue;
                    }
                    for (Object o1 : tmpList) {
                        executeSingle(o1, encryptFields2, isEncrypt);
                    }
                } else if (o.getClass().getClassLoader() != null) {
                    executeSingle(o, getEncryptField(o), isEncrypt);
                } else {
                    String value = String.valueOf(o);
                    value = isEncrypt ? encrypt(value) : decrypt(value);
                    map.put(f, value);
                }
            }
            return result;
        }

        if (result.getClass().getClassLoader() == null) {
            result = (T) (isEncrypt ? encrypt(String.valueOf(result)) : decrypt(String.valueOf(result)));
            return result;
        }
        //自定义对象
        for (Field[] fields : getFields(result.getClass())) {
            for (Field field : fields) {
                if (!encryptFields.contains(field.getName())) {
                    continue;
                }
                field.setAccessible(true);
                Object o = field.get(result);
                if (o == null) {
                    continue;
                }
                if (o instanceof List) {
                    List tmpList = (List) o;
                    if (CollectionUtils.isEmpty(tmpList)) {
                        continue;
                    }
                    Set<String> encryptFields2 = getEncryptField(tmpList.get(0));
                    if (CollectionUtils.isEmpty(encryptFields2)) {
                        continue;
                    }
                    for (Object o1 : tmpList) {
                        executeSingle(o1, encryptFields2, isEncrypt);
                    }
                } else if (o.getClass().getClassLoader() != null) {
                    executeSingle(o, getEncryptField(o), isEncrypt);
                } else {
                    String value = String.valueOf(o);
                    value = isEncrypt ? encrypt(value) : decrypt(value);
                    field.set(result, value);
                }
            }
        }
        return result;
    }

    public String encrypt(String value) {
        return encryptionService.encrypt(value, this.getEncryptKey());
    }

    public String decrypt(String value) {
        return encryptionService.decrypt(value, this.getEncryptKey());
    }
}
public class AesUtil {
    private AesUtil() {

    }

    private static final String KEY_ALGORITHM = "AES";
    private static final String DEFAULT_CIPHER_ALGORITHM = "AES/ECB/PKCS5Padding";
    private static final String KEY_SECURE_RANDOM_ALGORITHM = "SHA1PRNG";
    private static final String CHARSET = "utf-8";

    /**
     * 加密
     *
     * @param content  加密正文
     * @param password 加密密码
     * @return
     */
    public static String encrypt(String content, String password) {
        try {
            // 创建密码器
            Cipher cipher = Cipher.getInstance(DEFAULT_CIPHER_ALGORITHM);

            byte[] byteContent = content.getBytes(CHARSET);

            // 初始化为加密模式的密码器
            cipher.init(Cipher.ENCRYPT_MODE, getSecretKey(password));

            // 加密
            byte[] result = cipher.doFinal(byteContent);

            //通过Base64转码返回
            return Base64.getEncoder().encodeToString(result);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 解密
     *
     * @param content  加密字符串
     * @param password 解密密码
     * @return
     */
    public static String decrypt(String content, String password) {
        try {
            //实例化
            Cipher cipher = Cipher.getInstance(DEFAULT_CIPHER_ALGORITHM);

            //使用密钥初始化,设置为解密模式
            cipher.init(Cipher.DECRYPT_MODE, getSecretKey(password));

            //执行操作
            byte[] result = cipher.doFinal(Base64.getDecoder().decode(content));

            return new String(result, CHARSET);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    private static Key getSecretKey(String password) throws NoSuchAlgorithmException {
        KeyGenerator kg = KeyGenerator.getInstance(KEY_ALGORITHM);

        SecureRandom random = SecureRandom.getInstance(KEY_SECURE_RANDOM_ALGORITHM);
        random.setSeed(password.getBytes());
        //AES 要求密钥长度为 128
        kg.init(128, random);
        //生成一个密钥
        SecretKey secretKey = kg.generateKey();
        // 转换为AES专用密钥
        return new SecretKeySpec(secretKey.getEncoded(), KEY_ALGORITHM);
    }

    public static void main(String[] args) {
        String url = "https://www.baidu.com";
        String password = "kZNJcCWedTon45KfNyiH";
        System.out.println("原始链接:"+url);
        String encrypt = encrypt(url, password);
        System.out.println("加密:"+encrypt);
        String decrypt = decrypt(encrypt, password);
        System.out.println("解密:"+decrypt);
    }
}

配置拦截器

 使用

 至此mybatis层加密解密搞定,web层代码如下

/**
 * @author one.xu
 */
@AllArgsConstructor
@Getter
public enum DesensitionTypeEnum {
    NIK(3, 4),
    EMAIL(1, null),
    OTHER(0,0),
    ;
    /**
     * 前多少位
     */
    private Integer front;
    /**
     * 后多少位
     */
    private Integer end;

}
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface Desensitization {
    /**
     * 脱敏策略
     *
     * @return
     */
    DesensitionTypeEnum  type();

    /**
     * 替换字符串
     *
     * @return
     */
    String replaceStr() default "*";

    /**
     * type为OTHER生效
     * 扩展脱敏策略类型名
     * @return
     */
    String ext() default "";
}

/**
 * @author one.xu
 */
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
public @interface SensitiveObject {
}
/**
 * @author one.xu
 */
@Configuration
@ConditionalOnProperty(value = "xuyw.desensitization.enabled", havingValue = "true")
public class DesensitizationConfig {
    @Bean
    public DefaultDesensitionService defaultDesensitionService() {
        return new DefaultDesensitionService();
    }

    @Bean
    public EmailDesensitionService emailDesensitionService() {
        return new EmailDesensitionService();
    }

    @Bean
    public DesensitizationHelper desensitizationHelper(@Autowired List<IDesensitionService> list) {
        return new DesensitizationHelper(list);
    }

    @Bean("desensitizeValueFilter")
    public DesensitizeValueFilter desensitizeValueFilter(@Autowired DesensitizationHelper desensitizationHelper) {
        return new DesensitizeValueFilter(desensitizationHelper);
    }

    @Bean("fastJsonBeanPostProcessor")
    public FastJsonBeanPostProcessor fastJsonBeanPostProcessor(@Autowired DesensitizeValueFilter desensitizeValueFilter) {
        return new FastJsonBeanPostProcessor(desensitizeValueFilter);
    }
}

本系统使用fastjson为序列化框架,写个过滤器对敏感对象进行脱敏操作

/**
 * @author one.xu
 */
public class DesensitizeValueFilter implements ValueFilter {
    public DesensitizeValueFilter(DesensitizationHelper desensitizationHelper) {
        this.desensitizationHelper = desensitizationHelper;
    }

    private DesensitizationHelper desensitizationHelper;

    @SneakyThrows
    @Override
    public Object process(Object object, String name, Object value) {
        if (null == value || !(value instanceof String) || ((String) value).length() == 0) {
            return value;
        }
        if (object.getClass().getAnnotation(SensitiveObject.class) == null) {
            return value;
        }
        Desensitization annotation;
        //获取字段
        Field field = object.getClass().getDeclaredField(name);
        if (String.class != field.getType() || (annotation = field.getAnnotation(Desensitization.class)) == null) {
            return value;
        }
        return desensitizationHelper.execute(annotation, value.toString());
    }
}
/**
 * 在项目架构中已经统一处理了HttpMessageConverters ,为了不入侵原先代码
 * 在bean初始化之后添加过滤器,如果没有配置HttpMessageConverters 则需要手动配置
 * 
 * @author one.xu
 */
@Slf4j
public class FastJsonBeanPostProcessor implements BeanPostProcessor {

    public FastJsonBeanPostProcessor(DesensitizeValueFilter desensitizeValueFilter) {
        this.desensitizeValueFilter = desensitizeValueFilter;
    }

    private DesensitizeValueFilter desensitizeValueFilter;

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) {
        if (!(bean instanceof HttpMessageConverters)) {
            return bean;
        }
        HttpMessageConverters httpMessageConverters = (HttpMessageConverters) bean;
        for (HttpMessageConverter<?> httpMessageConverter : httpMessageConverters.getConverters()) {
            if (!(httpMessageConverter instanceof FastJsonHttpMessageConverter)) {
                continue;
            }
            FastJsonHttpMessageConverter fastJsonHttpMessageConverter = (FastJsonHttpMessageConverter) httpMessageConverter;
            SerializeFilter[] serializeFilters = fastJsonHttpMessageConverter.getFastJsonConfig().getSerializeFilters();
            if (containsDesensitizeValueFilter(serializeFilters)) {
                continue;
            }
            serializeFilters = ArrayUtils.addAll(serializeFilters, desensitizeValueFilter);
            fastJsonHttpMessageConverter.getFastJsonConfig().setSerializeFilters(serializeFilters);
            log.info("设置fastJson敏感过滤成功:{}",DesensitizeValueFilter.class.getName());
        }
        return bean;
    }

    private boolean containsDesensitizeValueFilter(SerializeFilter[] serializeFiltersr) {
        for (SerializeFilter s : serializeFiltersr) {
            if (s instanceof DesensitizeValueFilter) {
                return true;
            }
        }
        return false;
    }
}
/**
 * @author one.xu
 */
public interface IDesensitionService {

    String type();

    String execute(Desensitization desensitization, String value);
}
@Data
public class DefaultDesensitionService implements IDesensitionService {
    public static final String SERVICE_NAME = "DEFAULT";

    @Override
    public String type() {
        return SERVICE_NAME;
    }

    @Override
    public String execute(Desensitization desensitization, String value) {
        if (StringUtils.isBlank(value)) {
            return value;
        }
        DesensitionTypeEnum desensitionTypeEnum = desensitization.type();
        Integer front = desensitionTypeEnum.getFront();
        if (front == null || front < 0) {
            front = 0;
        }
        Integer end = desensitionTypeEnum.getEnd();
        if (end == null || end < 0) {
            end = 0;
        }

        if ((front + end) > value.length()) {
            return value;
        }
        end = value.length() - end;
        StringBuilder replaceStr = new StringBuilder();
        int i = 0;
        for (char c : value.toCharArray()) {
            replaceStr.append((i >= front && i < end) ? desensitization.replaceStr() : c);
            i = i + 1;
        }
        return replaceStr.toString();
    }
}
public class EmailDesensitionService implements IDesensitionService {
    @Override
    public String type() {
        return DesensitionTypeEnum.EMAIL.name();
    }

    @Override
    public String execute(Desensitization desensitization, String value) {
        if (StringUtils.isBlank(value)) {
            return value;
        }
        DesensitionTypeEnum desensitionTypeEnum = desensitization.type();
        Integer front = desensitionTypeEnum.getFront();
        if (front == null || front < 0) {
            front = 0;
        }
        Integer end = value.lastIndexOf("@");
        if (end == null || end < 0) {
            end = 0;
        }
        if ((front + end) > value.length()) {
            return value;
        }
        end = value.length() - end;
        StringBuilder replaceStr = new StringBuilder();
        int i = 0;
        for (char c : value.toCharArray()) {
            replaceStr.append((i >= front && i < end) ? desensitization.replaceStr() : c);
            i = i + 1;
        }
        return replaceStr.toString();
    }
}

/**
 * @author one.xu
 */
public class DesensitizationHelper {
    private Map<String, IDesensitionService> desensitionServiceMap;

    public DesensitizationHelper(List<IDesensitionService> desensitionServiceList) {
        desensitionServiceMap = new HashMap<>();
        for (IDesensitionService service : desensitionServiceList) {
            if (desensitionServiceMap.containsKey(service.type())) {
                throw new RuntimeException("已存在脱敏策略:" + service.type());
            }
            desensitionServiceMap.put(service.type(), service);
        }
    }

    public String execute(Desensitization desensitization, String value) {
        String key = DesensitionTypeEnum.OTHER == desensitization.type() ? desensitization.ext() : desensitization.type().name();
        IDesensitionService desensitionService = desensitionServiceMap.get(key);
        if (desensitionService == null) {
            desensitionService = desensitionServiceMap.get(DefaultDesensitionService.SERVICE_NAME);
        }
        return desensitionService.execute(desensitization, value);
    }
}

使用

xuyw.desensitization.enabled=true

效果