zoukankan      html  css  js  c++  java
  • 使用jackson进行json序列化时进行敏感字段脱敏(加密)或者忽略

    需求:

    1、通过注解的方式指定字段在序列化时进行脱敏或者加密;
    2、通过注解的方式指定字段在序列化时忽略掉;
    3、某些情况下需要处理的类不是我们可以修改的,但是也要实现上述两项需求;

    实现如下:

    工具类SensitiveJsonUtil:

    package com.example.jackson;
    
    import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
    import com.fasterxml.jackson.core.JsonProcessingException;
    import com.fasterxml.jackson.databind.DeserializationFeature;
    import com.fasterxml.jackson.databind.ObjectMapper;
    
    public class SensitiveJsonUtil {
    
        private static ObjectMapper objectMapper = new ObjectMapper();
    
        static {
            // 反序列化时忽略不存在的字段
            objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
    
            // 注册处理敏感字段的扩展模块
            objectMapper.registerModule(new SensitiveFieldProcessModule());
    
            // 通过mixIn功能来按字段名忽略一些字段
            objectMapper.addMixIn(Object.class, IgnoreSensitiveFieldsMixin.class);
        }
    
        @JsonIgnoreProperties(
                value = {
                        "password",
                        "secret",
                        "token"
                }
        )
        static class IgnoreSensitiveFieldsMixin {
        }
    
        public static String toJsonString(Object object) {
            try {
                return objectMapper.writeValueAsString(object);
            } catch (JsonProcessingException e) {
                throw new RuntimeException(String.format("toJsonString error, %s", e.getMessage()), e);
            }
        }
    
    }
    

    扩展模块类SensitiveFieldProcessModule(这里仅为demo,所以一些相关的类直接以嵌套类放在了一起)

    package com.example.jackson;
    
    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.*;
    import com.fasterxml.jackson.databind.ser.BeanPropertyWriter;
    import com.fasterxml.jackson.databind.ser.BeanSerializerModifier;
    import com.fasterxml.jackson.databind.type.MapType;
    import org.apache.commons.lang3.StringUtils;
    
    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;
    
    public class SensitiveFieldProcessModule extends Module {
    
        private static final String MODULE_NAME = "jackson-sensitive-field-process-module";
    
        private Version version = VersionUtil.parseVersion("0.0.1", "com.example", MODULE_NAME);
    
        @Override
        public String getModuleName() {
            return MODULE_NAME;
        }
    
        @Override
        public Version version() {
            return version;
        }
    
        @Override
        public void setupModule(SetupContext setupContext) {
            setupContext.addBeanSerializerModifier(new SensitiveFieldModifier());
        }
    
        public static class SensitiveFieldModifier extends BeanSerializerModifier {
    
            @Override
            public List<BeanPropertyWriter> changeProperties(SerializationConfig config, BeanDescription beanDesc, List<BeanPropertyWriter> beanProperties) {
                List<BeanPropertyWriter> newWriters = new ArrayList<>();
                for (BeanPropertyWriter writer : beanProperties) {
                    if (writer.getAnnotation(Sensitive.class) != null && writer.getType().isTypeOrSubTypeOf(String.class)) {
                        // 如果带有 @Sensitive 注解,并且是字符串,则使用自定义处理
                        JsonSerializer<Object> serializer = new SensitiveJsonSerializer(writer.getSerializer());
                        writer.assignSerializer(serializer);
    
                    }
                    newWriters.add(writer);
                }
    
                return newWriters;
    
                // super.changeProperties(config, beanDesc, beanProperties);
            }
    
            @Override
            public JsonSerializer<?> modifyMapSerializer(SerializationConfig config, MapType valueType, BeanDescription beanDesc, JsonSerializer<?> serializer) {
                return super.modifyMapSerializer(config, valueType, beanDesc, serializer);
            }
    
        }
    
        @JacksonAnnotation
        @Retention(RetentionPolicy.RUNTIME)
        @Target({ElementType.FIELD})
        public @interface Sensitive {
        }
    
        public static class SensitiveJsonSerializer extends JsonSerializer<Object> {
    
            private final JsonSerializer<Object> serializer;
    
            public SensitiveJsonSerializer(JsonSerializer<Object> serializer) {
                this.serializer = serializer;
            }
    
            @Override
            public void serialize(Object object, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException, JsonProcessingException {
                if (object != null && object instanceof String) {
                    String str = (String) object;
                    if (StringUtils.isNotBlank(str)) {
                        object = processSensitiveField(str);
                    }
                }
    
                if (this.serializer == null) {
                    serializerProvider.defaultSerializeValue(object, jsonGenerator);
                } else {
                    this.serializer.serialize(object, jsonGenerator, serializerProvider);
                }
            }
    
            private static String processSensitiveField(String input) {
                if (StringUtils.isBlank(input)) {
                    return input;
                }
    
                input = StringUtils.trim(input);
                int strLen = input.length();
    
                if (strLen <= 1) {
                    return "*";
                } else if (strLen == 2) {
                    return String.format("%s*", input.charAt(0));
                } else if (strLen == 3) {
                    return String.format("%s*%s", input.charAt(0), input.charAt(strLen - 1));
                } else {
                    int left = strLen / 4;
                    int right = strLen / 3;
                    return String.format("%s%s%s",
                            StringUtils.left(input, left),
                            StringUtils.repeat('*', (strLen - left - right)),
                            StringUtils.right(input, right));
                }
    
            }
        }
    
    }
    

    使用示例:

    package com.example;
    
    import com.example.jackson.SensitiveFieldProcessModule;
    import com.example.jackson.SensitiveJsonUtil;
    
    import java.util.HashMap;
    import java.util.Map;
    
    public class App {
    
        public static void main(String[] args) {
            Person person = new Person();
            person.setUsername("张一二");
            person.setIdNumber("1000000000112245");
            person.setPassword("123456(password)");
            Map<String, String> otherInfo = new HashMap<>();
            otherInfo.put("nation", "CN");
            otherInfo.put("secret", "(secret)");
            person.setOtherInfo(otherInfo);
            System.out.println(SensitiveJsonUtil.toJsonString(person));
            // 输出:{"username":"张一二","idNumber":"1000*******12245","otherInfo":{"nation":"CN"}}
        }
    
        static class Person {
    
            private String username;
    
            @SensitiveFieldProcessModule.Sensitive
            private String idNumber;
    
            private String password;
    
            private Map<String, String> otherInfo;
    
            // ... 省略getter/setter
        }
    }
    

    参考文档:

    1. 使用Jackson加密/解密JSON字段 : 通过自定义模块来进行指定字段的加解密
    2. Serialize Only Fields that meet a Custom Criteria with Jackson
    3. baeldung jackson教程
    4. jackson官网文档链接
    5. jackson官网文档-JacksonMixInAnnotations
    本博客(liqipeng)除非已明确说明转载,否则皆为liqipeng原创或者整理,转载请保留此链接:https://www.cnblogs.com/liqipeng/p/15448825.html

    本博客(liqipeng)除非已明确说明转载,否则皆为liqipeng原创或者整理,转载请保留此链接:https://www.cnblogs.com/liqipeng/p/15448825.html
    如果你觉得这篇文章对你有帮助或者使你有所启发,请点击右下角的推荐按钮,谢谢,:)
  • 相关阅读:
    极简代码搞定视频剪辑
    python 遍历本地文件
    安装Anaconda需要知道的pc信息
    ng4 路由多参数传参以及接收
    Js之设置日期时间 判断日期是否在范围内
    VsCode显示左边折叠代码+-按钮
    H5+.Net Webapi集成微信分享前后端代码 微信JS-SDK wx.onMenuShareTimeline wx.onMenuShareAppMessage
    压测工具之JMeter之环境配置及运行
    C# 交集、差集、并集、去重
    Nginx初学者指南
  • 原文地址:https://www.cnblogs.com/liqipeng/p/15448825.html
Copyright © 2011-2022 走看看