Jackson匿名化输出

Jackson匿名化输出,第1张

Jackson匿名化输出
  • 背景
    在项目的开发中,经常会把第三方接口的返回值以json字符串的形式打印到日志中方便出现问题后进行定位,但是在一些项目中会有一些敏感的信息不能打印到日志中,需要进行匿名化的处理,下面介绍两种常见的实现方式
  • 方案一
    直接在需要匿名化的字段上使用 @JsonSerialize(using = AnonymizeJsonSerializer.class)注解的形式-实现和下面一样,由AnonymizeJsonSerializer来实现序列化
    • 优点
      1、对所有的ObjectMapper有效
      2、容易理解、方便使用
    • 缺点:如果转换为json之后的对象在后面提交给第三方的时候还需要使用的话就会出现传递给第三方的值被序列化了
  • 方案二
    重新定义一个ObjectMapper,在ObjectMapper中注入支持匿名化的module,在module中实现匿名化的逻辑,
    • 优点:匿名化的打印逻辑相对独立,不影响其他调用的正常实现
    • 缺点:实现优点麻烦,不太容易理解
  • 实现代码如下
package com.hkk.demo.utils;

import com.fasterxml.jackson.annotation.JacksonAnnotation;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.Version;
import com.fasterxml.jackson.core.util.VersionUtil;
import com.fasterxml.jackson.databind.BeanDescription;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.Module;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationConfig;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.ser.BeanPropertyWriter;
import com.fasterxml.jackson.databind.ser.BeanSerializerModifier;
import com.fasterxml.jackson.databind.type.MapType;
import com.google.common.collect.Lists;
import java.io.IOException;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.util.ArrayList;
import java.util.List;
import lombok.Data;
import lombok.experimental.UtilityClass;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;

@Slf4j
@UtilityClass
public class AnonymizeJsonUtil {

    private static final String EMPTY_JSON = "{}";
    // 字段名称包含这些字段的需要匿名化
    private static final List ANONYMIZE_NAMES = Lists.newArrayList("cash");
    public static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();

    static {
        // 反序列化时忽略不存在的字段
        OBJECT_MAPPER.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
        // 注册处理敏感字段的扩展模块
        OBJECT_MAPPER.registerModule(new AnonymizeModule());
    }

    
    public static String toJsonString(Object object) throws JsonProcessingException {
        return OBJECT_MAPPER.writevalueAsString(object);
    }

    
    public static String toJsonStringOrEmpty(Object object) {
        try {
            return toJsonString(object);
        } catch (JsonProcessingException e) {
            log.error("json to string error", e);
        }
        return EMPTY_JSON;
    }


    private static class AnonymizeModule extends Module {

        private static final String MODULE_NAME = "jackson-anonymize-module";

        private final Version version = VersionUtil.parseVersion("0.0.1", "com.hkk", MODULE_NAME);

        @Override
        public String getModuleName() {
            return MODULE_NAME;
        }

        @Override
        public Version version() {
            return version;
        }

        @Override
        public void setupModule(SetupContext setupContext) {
            setupContext.addBeanSerializerModifier(new FieldAnonymizeModifier());
        }

        public static class FieldAnonymizeModifier extends BeanSerializerModifier {

            @Override
            public List changeProperties(SerializationConfig config,
                BeanDescription beanDesc, List beanProperties) {
                beanProperties = super.changeProperties(config, beanDesc, beanProperties);
                List newWriters = new ArrayList<>();
                for (BeanPropertyWriter writer : beanProperties) {
                    if (needAnonymize(writer)) {
                        // 如果带有 @Sensitive 注解,并且是字符串,则使用自定义处理
                        JsonSerializer serializer = new AnonymizeJsonSerializer(
                            writer.getSerializer());
                        writer.assignSerializer(serializer);

                    }
                    newWriters.add(writer);
                }

                return newWriters;
            }
            
            // 判断是否需要匿名化
            private boolean needAnonymize(BeanPropertyWriter writer) {
                return writer.getAnnotation(Anonymize.class) != null || ANONYMIZE_NAMES.stream()
                    .anyMatch(s -> StringUtils.containsIgnoreCase(s, writer.getName()));
            }

            @Override
            public JsonSerializer modifyMapSerializer(SerializationConfig config,
                MapType valueType,
                BeanDescription beanDesc, JsonSerializer serializer) {
                return super.modifyMapSerializer(config, valueType, beanDesc, serializer);
            }

        }

        private static class AnonymizeJsonSerializer extends JsonSerializer {

            private final JsonSerializer serializer;

            public AnonymizeJsonSerializer(JsonSerializer serializer) {
                this.serializer = serializer;
            }

            @Override
            public void serialize(Object value, JsonGenerator jsonGenerator,
                SerializerProvider serializerProvider) throws IOException {
                if (value != null && canAnonymize(value)) {
                    value = processSensitiveField(value.toString());
                } else if (value != null) {
                    throw new RuntimeException("匿名化只支持字符串和数字类型");
                }
                if (this.serializer == null) {
                    serializerProvider.defaultSerializevalue(value, jsonGenerator);
                } else {
                    this.serializer.serialize(value, jsonGenerator, serializerProvider);
                }
            }

            private boolean canAnonymize(Object value) {
                return value instanceof String
                    || value instanceof Number
                    || value.getClass().isPrimitive();
            }

            private static String processSensitiveField(String input) {
                if (StringUtils.isBlank(input)) {
                    return input;
                }
                return "****";
            }
        }

    }
    // 匿名化的注解,需要匿名化的字段可以添加这个注解
    @JacksonAnnotation
    @Retention(RetentionPolicy.RUNTIME)
    @Target({ElementType.FIELD})
    public @interface Anonymize {

    }


    public static void main(String[] args) {
        AnonymizeTest data = new AnonymizeTest();
        data.setPassword("1234");
        data.setUsername("123456");
        data.setSalary(10000);
        data.setCash(32);
        System.out.println(AnonymizeJsonUtil.toJsonStringOrEmpty(data));
    }

    @Data
    public static class AnonymizeTest {

        @Anonymize
        private String password;
        private String username;
        @Anonymize
        private Integer salary;
        private int cash;
    }
}

					
										


					

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/zaji/5696998.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-12-17
下一篇 2022-12-17

发表评论

登录后才能评论

评论列表(0条)