溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

Java怎么用Jackson序列化實現(xiàn)數(shù)據(jù)脫敏

發(fā)布時間:2023-03-31 11:00:34 來源:億速云 閱讀:137 作者:iii 欄目:開發(fā)技術

這篇“Java怎么用Jackson序列化實現(xiàn)數(shù)據(jù)脫敏”文章的知識點大部分人都不太理解,所以小編給大家總結了以下內容,內容詳細,步驟清晰,具有一定的借鑒價值,希望大家閱讀完這篇文章能有所收獲,下面我們一起來看看這篇“Java怎么用Jackson序列化實現(xiàn)數(shù)據(jù)脫敏”文章吧。

    1.背景

    在項目中有些敏感信息不能直接展示,比如客戶手機號、身份證、車牌號等信息,展示時均需要進行數(shù)據(jù)脫敏,防止泄露客戶隱私。脫敏即是對數(shù)據(jù)的部分信息用脫敏符號(*)處理。

    2.目標

    • 在服務端返回數(shù)據(jù)時,利用Jackson序列化完成數(shù)據(jù)脫敏,達到對敏感信息脫敏展示。

    • 降低重復開發(fā)量,提升開發(fā)效率

    • 形成統(tǒng)一有效的脫敏規(guī)則

    • 可基于重寫默認脫敏實現(xiàn)的desensitize方法,實現(xiàn)可擴展、可自定義的個性化業(yè)務場景的脫敏需求

    3.主要實現(xiàn)

    3.1基于Jackson的自定義脫敏序列化實現(xiàn)

    StdSerializer:所有標準序列化程序所使用的基類,這個是編寫自定義序列化程序所推薦使用的基類。

    ContextualSerializer: 是Jackson 提供的另一個序列化相關的接口,它的作用是通過字段已知的上下文信息定制JsonSerializer。

    package com.jd.ccmp.ctm.constraints.serializer;
    
    
    import com.fasterxml.jackson.core.JsonGenerator;
    import com.fasterxml.jackson.databind.BeanProperty;
    import com.fasterxml.jackson.databind.JsonSerializer;
    import com.fasterxml.jackson.databind.SerializerProvider;
    import com.fasterxml.jackson.databind.ser.ContextualSerializer;
    import com.fasterxml.jackson.databind.ser.std.StdSerializer;
    import com.jd.ccmp.ctm.constraints.Symbol;
    import com.jd.ccmp.ctm.constraints.annotation.Desensitize;
    import com.jd.ccmp.ctm.constraints.desensitization.Desensitization;
    import com.jd.ccmp.ctm.constraints.desensitization.DesensitizationFactory;
    import com.jd.ccmp.ctm.constraints.desensitization.DefaultDesensitization;
    
    
    
    
    import java.io.IOException;
    
    
    
    
    /**
     * 脫敏序列化器
     *
     * @author zhangxiaoxu15
     * @date 2022/2/8 11:10
     */
    public class ObjectDesensitizeSerializer extends StdSerializer<Object> implements ContextualSerializer {
        private static final long serialVersionUID = -7868746622368564541L;
        private transient Desensitization<Object> desensitization;
        protected ObjectDesensitizeSerializer() {
            super(Object.class);
        }
        public Desensitization<Object> getDesensitization() {
            return desensitization;
        }
        public void setDesensitization(Desensitization<Object> desensitization) {
            this.desensitization = desensitization;
        }
        @Override
        public JsonSerializer<Object> createContextual(SerializerProvider prov, BeanProperty property) {
    //獲取屬性注解
            Desensitize annotation = property.getAnnotation(Desensitize.class);
            return createContextual(annotation.desensitization());
        }
        @SuppressWarnings("unchecked")
        public JsonSerializer<Object> createContextual(Class<? extends Desensitization<?>> clazz) {
            ObjectDesensitizeSerializer serializer = new ObjectDesensitizeSerializer();
            if (clazz != DefaultDesensitization.class) {
                serializer.setDesensitization((Desensitization<Object>) DesensitizationFactory.getDesensitization(clazz));
            }
            return serializer;
        }
        @Override
        public void serialize(Object value, JsonGenerator gen, SerializerProvider provider) throws IOException {
            Desensitization<Object> objectDesensitization = getDesensitization();
            if (objectDesensitization != null) {
                try {
                    gen.writeObject(objectDesensitization.desensitize(value));
                } catch (Exception e) {
                    gen.writeObject(value);
                }
            } else if (value instanceof String) {
                gen.writeString(Symbol.getSymbol(((String) value).length(), Symbol.STAR));
            } else {
                gen.writeObject(value);
            }

    注:createContextual可以獲得字段的類型以及注解。當字段擁有自定義注解時,取出注解中的值創(chuàng)建定制的序列化方式,這樣在serialize方法中便可以得到這個值了。createContextual方法只會在第一次序列化字段時調用(因為字段的上下文信息在運行期不會改變),所以無需關心性能問題。

    3.2定義脫敏接口、以及工廠實現(xiàn)

    3.2.1脫敏器接口定義

    package com.jd.ccmp.ctm.constraints.desensitization;
    
    
    /**
     * 脫敏器
     *
     * @author zhangxiaoxu15
     * @date 2022/2/8 10:56
     */
    public interface Desensitization<T> {
        /**
         * 脫敏實現(xiàn)
         *
         * @param target 脫敏對象
         * @return 脫敏返回結果
         */
        T desensitize(T target);
    }

    3.2.2脫敏器工廠實現(xiàn)

    package com.jd.ccmp.ctm.constraints.desensitization;
    
    import java.util.HashMap;
    import java.util.Map;
    
    
    /**
     * 工廠方法
     *
     * @author zhangxiaoxu15
     * @date 2022/2/8 10:58
     */
    public class DesensitizationFactory {
        private DesensitizationFactory() {
        }
        private static final Map<Class<?>, Desensitization<?>> map = new HashMap<>();
    
    
    
    
        @SuppressWarnings("all")
        public static Desensitization<?> getDesensitization(Class<?> clazz) {
            if (clazz.isInterface()) {
                throw new UnsupportedOperationException("desensitization is interface, what is expected is an implementation class !");
            }
            return map.computeIfAbsent(clazz, key -> {
                try {
                    return (Desensitization<?>) clazz.newInstance();
                } catch (InstantiationException | IllegalAccessException e) {
                    throw new UnsupportedOperationException(e.getMessage(), e);
                }
            });

    3.3常用的脫敏器實現(xiàn)

    3.3.1默認脫敏實現(xiàn)

    可基于默認實現(xiàn),擴展實現(xiàn)個性化場景

    package com.jd.ccmp.ctm.constraints.desensitization;
    
    
    /**
     * 默認脫敏實現(xiàn)
     *
     * @author zhangxiaoxu15
     * @date 2022/2/8 11:01
     */
    public interface DefaultDesensitization extends Desensitization<String> {
    }

    3.3.2手機號脫敏器

    實現(xiàn)對手機號中間4位號碼脫敏

    package com.jd.ccmp.ctm.constraints.desensitization;
    import com.jd.ccmp.ctm.constraints.Symbol;
    import java.util.regex.Matcher;
    import java.util.regex.Pattern;
    
    
    /**
     * 手機號脫敏器,保留前3位和后4位
     *
     * @author zhangxiaoxu15
     * @date 2022/2/8 11:02
     */
    public class MobileNoDesensitization implements DefaultDesensitization {
        /**
         * 手機號正則
         */
        private static final Pattern DEFAULT_PATTERN = Pattern.compile("(13[0-9]|14[579]|15[0-3,5-9]|16[6]|17[0135678]|18[0-9]|19[89])\d{8}");
    
    
    
    
        @Override
        public String desensitize(String target) {
            Matcher matcher = DEFAULT_PATTERN.matcher(target);
            while (matcher.find()) {
                String group = matcher.group();
                target = target.replace(group, group.substring(0, 3) + Symbol.getSymbol(4, Symbol.STAR) + group.substring(7, 11));
            }
            return target;

    3.4注解定義

    通過@JacksonAnnotationsInside實現(xiàn)自定義注解,提高易用性

    package com.jd.ccmp.ctm.constraints.annotation;
    import com.fasterxml.jackson.annotation.JacksonAnnotationsInside;
    import com.fasterxml.jackson.databind.annotation.JsonSerialize;
    import com.jd.ccmp.ctm.constraints.desensitization.Desensitization;
    import com.jd.ccmp.ctm.constraints.serializer.ObjectDesensitizeSerializer;
    import java.lang.annotation.*;
    
    
    /**
     * 脫敏注解
     *
     * @author zhangxiaoxu15
     * @date 2022/2/8 11:09
     */
    @Target({ElementType.FIELD, ElementType.ANNOTATION_TYPE})
    @Retention(RetentionPolicy.RUNTIME)
    @JacksonAnnotationsInside
    @JsonSerialize(using = ObjectDesensitizeSerializer.class)
    @Documented
    public @interface Desensitize {
        /**
         * 對象脫敏器實現(xiàn)
         */
        @SuppressWarnings("all")
        Class<? extends Desensitization<?>> desensitization();

    3.4.1默認脫敏注解

    package com.jd.ccmp.ctm.constraints.annotation;
    import com.fasterxml.jackson.annotation.JacksonAnnotationsInside;
    import com.jd.ccmp.ctm.constraints.desensitization.DefaultDesensitization;
    import java.lang.annotation.*;
    
    
    
    
    /**
     * 默認脫敏注解
     *
     * @author zhangxiaoxu15
     * @date 2022/2/8 11:14
     */
    @Target({ElementType.FIELD})
    @Retention(RetentionPolicy.RUNTIME)
    @JacksonAnnotationsInside
    @Desensitize(desensitization = DefaultDesensitization.class)
    @Documented
    public @interface DefaultDesensitize {

    3.4.2手機號脫敏注解

    package com.jd.ccmp.ctm.constraints.annotation;
    import com.fasterxml.jackson.annotation.JacksonAnnotationsInside;
    import com.jd.ccmp.ctm.constraints.desensitization.MobileNoDesensitization;
    import java.lang.annotation.*;
    
    
    /**
     * 手機號脫敏
     *
     * @author zhangxiaoxu15
     * @date 2022/2/8 11:18
     */
    @Target({ElementType.FIELD})
    @Retention(RetentionPolicy.RUNTIME)
    @JacksonAnnotationsInside
    @Desensitize(desensitization = MobileNoDesensitization.class)
    @Documented
    public @interface MobileNoDesensitize {
    }

    3.5定義脫敏符號

    支持指定脫敏符號,例如* 或是 ^_^

    package com.jd.ccmp.ctm.constraints;
    import java.util.stream.Collectors;
    import java.util.stream.IntStream;
    
    
    /**
     * 脫敏符號
     *
     * @author zhangxiaoxu15
     * @date 2022/2/8 10:53
     */
    public class Symbol {
        /**
         * '*'脫敏符
         */
        public static final String STAR = "*";
        private Symbol() {}
        /**
         * 獲取符號
         *
         * @param number 符號個數(shù)
         * @param symbol 符號
         */
        public static String getSymbol(int number, String symbol) {
            return IntStream.range(0, number).mapToObj(i -> symbol).collect(Collectors.joining());
        }

    4.使用樣例&執(zhí)行流程剖析

    Java怎么用Jackson序列化實現(xiàn)數(shù)據(jù)脫敏

    程序類圖

    Java怎么用Jackson序列化實現(xiàn)數(shù)據(jù)脫敏

    **執(zhí)行流程剖析**

     1.調用JsonUtil.toJsonString()開始執(zhí)行序列化

     2.識別屬性mobile上的注解@MobileNoDesensitize(上文3.4.2)

     3.調用ObjectDesensitizeSerializer#createContextual(上文3.1 & 3.2),返回JsonSerializer

     4.調用手機號脫敏實現(xiàn)MobileNoDesensitization#desensitize(上文3.3.2)

     5.輸出脫敏后的序列化結果,{"mobile":"133****5678"}

    不難發(fā)現(xiàn)核心執(zhí)行流程是第3步,但是@MobileNoDesensitize與ObjectDesensitizeSerializer又是如何聯(lián)系起來的呢?

    • 嘗試梳理下引用鏈路:@MobileNoDesensitize -> @Desensitize -> @JsonSerialize -> ObjectDesensitizeSerializer

    • 但是,在ObjectDesensitizeSerializer的實現(xiàn)中,我們似乎卻沒有發(fā)現(xiàn)上述鏈路的直接調用關系

    • 這就不得不說下Jackson元注解的概念

    //**Jackson元注解**
    //1.提到元注解這個詞,大家會想到@Target、@Retention、@Documented、@Inherited
    //2.Jackson也以同樣的思路設計了@JacksonAnnotationsInside
    
    
    /**
     * Meta-annotation (annotations used on other annotations)
     * used for indicating that instead of using target annotation
     * (annotation annotated with this annotation),
     * Jackson should use meta-annotations it has.
     * This can be useful in creating "combo-annotations" by having
     * a container annotation, which needs to be annotated with this
     * annotation as well as all annotations it 'contains'.
     * 
     * @since 2.0
     */
    @Target({ElementType.ANNOTATION_TYPE})
    @Retention(RetentionPolicy.RUNTIME)
    @JacksonAnnotation
    public @interface JacksonAnnotationsInside
    {
    }

    正是通過”combo-annotations”(組合注解、捆綁注解)的機制,實現(xiàn)了指示Jackson應該使用其擁有的元注釋,而不是使用目標注釋,從而實現(xiàn)了自定義脫敏實現(xiàn)設計目標。

    以上就是關于“Java怎么用Jackson序列化實現(xiàn)數(shù)據(jù)脫敏”這篇文章的內容,相信大家都有了一定的了解,希望小編分享的內容對大家有幫助,若想了解更多相關的知識內容,請關注億速云行業(yè)資訊頻道。

    向AI問一下細節(jié)

    免責聲明:本站發(fā)布的內容(圖片、視頻和文字)以原創(chuàng)、轉載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權內容。

    AI