最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

使用自定義Json注解實(shí)現(xiàn)輸出日志字段脫敏

 更新時(shí)間:2021年12月18日 10:13:53   作者:worstezreal  
這篇文章主要介紹了使用自定義Json注解實(shí)現(xiàn)輸出日志字段脫敏,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教

自定義Json注解實(shí)現(xiàn)輸出日志字段脫敏

背景

在日志輸出的時(shí)候,有時(shí)會(huì)輸出一些用戶的敏感信息,如手機(jī)號(hào),身份證號(hào),銀行卡號(hào)等,現(xiàn)需要對(duì)這些信息在日志輸出的時(shí)候進(jìn)行脫敏處理

思路

使用fastjson的ValueFilter對(duì)帶有自定義注解的字段進(jìn)行過(guò)濾

/**
 * 敏感信息類型
 *
 * @author worstEzreal
 * @version V1.0.0
 * @date 2017/7/19
 */
public enum SensitiveType {
    ID_CARD,
    BANK_CARD,
    PHONE
}
/**
 * 脫敏字段注解
 *
 * @author worstEzreal
 * @version V1.0.0
 * @date 2017/7/19
 */
@Target({ElementType.TYPE, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface SensitiveInfo {
    SensitiveType type();
}
/**
 * 日志敏感信息脫敏工具
 *
 * @author worstEzreal
 * @version V1.0.0
 * @date 2017/7/19
 */
public class SensitiveInfoUtils {
 
    public static String toJsonString(Object object) {
        return JSON.toJSONString(object, getValueFilter());
    }
 
    private static String desensitizePhoneOrIdCard(String num) {
        if (StringUtils.isBlank(num)) {
            return "";
        }
        return StringUtils.left(num, 3).concat(StringUtils.removeStart(StringUtils.leftPad(StringUtils.right(num, 4), StringUtils.length(num), "*"), "***"));
    }
 
    private static String desensitizeBankCard(String cardNum) {
        if (StringUtils.isBlank(cardNum)) {
            return "";
        }
        return StringUtils.left(cardNum, 4).concat(StringUtils.removeStart(StringUtils.leftPad(StringUtils.right(cardNum, 4), StringUtils.length(cardNum), "*"), "****"));
    }
 
    private static final ValueFilter getValueFilter() {
        return new ValueFilter() {
            @Override
            public Object process(Object obj, String key, Object value) {//obj-對(duì)象  key-字段名  value-字段值
                try {
                    Field field = obj.getClass().getDeclaredField(key);
                    SensitiveInfo annotation = field.getAnnotation(SensitiveInfo.class);
                    if (null != annotation && value instanceof String) {
                        String strVal = (String) value;
                        if (StringUtils.isNotBlank(strVal)) {
                            switch (annotation.type()) {
                                case PHONE:
                                    return desensitizePhoneOrIdCard(strVal);
                                case ID_CARD:
                                    return desensitizePhoneOrIdCard(strVal);
                                case BANK_CARD:
                                    return desensitizeBankCard(strVal);
                                default:
                                    break;
                            }
                        }
                    }
                } catch (NoSuchFieldException e) {
                    //找不到的field對(duì)功能沒有影響,空處理
                }
                return value;
            }
        };
    }
 
    public static void main(String[] args) {
        CardInfo cardInfo = new CardInfo();
        cardInfo.setId("11111111111111111");
        cardInfo.setCardId("6228480402564890018");
        System.out.println(SensitiveInfoUtils.toJsonString(cardInfo));
    }
}

附CardInfo類

public class CardInfo { 
    private String userId;
    private String name;
    @SensitiveInfo(type = SensitiveType.ID_CARD)
    private String certId;
    @SensitiveInfo(type = SensitiveType.BANK_CARD)
    private String cardId;
    private String bank;
    private String phone; 
    public String getUserId() {
        return userId;
    }
 
    public void setUserId(String userId) {
        this.userId = userId;
    }
 
    public String getName() {
        return name;
    }
 
    public void setName(String name) {
        this.name = name;
    }
 
    public String getCertId() {
        return certId;
    }
 
    public void setCertId(String certId) {
        this.certId = certId;
    }
 
    public String getCardId() {
        return cardId;
    }
 
    public void setCardId(String cardId) {
        this.cardId = cardId;
    }
 
    public String getBank() {
        return bank;
    }
 
    public void setBank(String bank) {
        this.bank = bank;
    }
 
    public String getPhone() {
        return phone;
    }
 
    public void setPhone(String phone) {
        this.phone = phone;
    } 
}

java注解式脫敏

隨著互聯(lián)網(wǎng)時(shí)代普及,用戶的信息越來(lái)越重要,我們開發(fā)軟件過(guò)程中也需要對(duì)用戶的信息進(jìn)行脫敏處理活著加密處理,針對(duì)于比較繁雜的工作,個(gè)人來(lái)講解如何實(shí)現(xiàn)注解式脫敏,支持靜態(tài)調(diào)用和aop統(tǒng)一攔截實(shí)現(xiàn)脫敏或者加密返回。

代碼講解

脫敏枚舉類

定義枚舉類,處理所有脫敏和加密等,同時(shí)可擴(kuò)展性,這里只是注解式調(diào)用方法而已,以便編寫樣例。DesensitizationEnum若還需要其他脫敏或者加密方法是,只需要添加下面枚舉類型即可

package com.lgh.common.sensitive;
import com.lgh.common.utils.MaskUtils;
import java.lang.reflect.Method;
/**
 * 若需要新定義一個(gè)掃描規(guī)則,這里添加即可
 *
 * @author lgh
 * @version 1.0
 * @date 2021/1/17
 */
public enum DesensitizationEnum {
    // 執(zhí)行類和脫敏方法名
    PHONE(MaskUtils.class, "maskPhone", new Class[]{String.class});
     private Class<?> clazz;
     private Method method;
    DesensitizationEnum(Class<?> target, String method, Class[] paramTypes) {
        this.clazz = target;
        try {
            this.method = target.getDeclaredMethod(method, paramTypes);
        } catch (NoSuchMethodException e) {
             e.printStackTrace();
        }
    }
    public Method getMethod() {
        return method;
    }
}

脫敏工具

package com.lgh.common.utils;
import org.springframework.util.StringUtils;
/**
 * @author lgh
 * @version 1.0
 * @date 2021/1/17
 */
public class MaskUtils {
    public static String maskPhone(String phone){
        if(StringUtils.isEmpty(phone) || phone.length() < 8){
            return phone;
        }
        return phone.replaceAll("(\\d{3})\\d*(\\d{4})", "$1****$2");
    }
}

注解類編寫

此類添加到需要脫敏的類屬性上即可實(shí)現(xiàn)脫敏,具體是遞歸遍歷此注解,通過(guò)反射機(jī)制來(lái)實(shí)現(xiàn)脫敏功能

package com.lgh.common.sensitive;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
 * 參數(shù)定義注解類
 * @author linguohu
 * @version 1.0
 * @date 2021/1/17
 **/
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface SensitiveValid {
    DesensitizationEnum type();
}

脫敏工具類

特殊聲明,我們遞歸時(shí)是索引遞歸,會(huì)出現(xiàn)死循環(huán)的情況,比如對(duì)象引用了對(duì)象,循環(huán)地址引用,所以會(huì)出現(xiàn)死循環(huán),這里設(shè)置了10層遞歸,一般我們也不允許有那么深的對(duì)象設(shè)置。

package com.lgh.common.utils;
import com.lgh.common.sensitive.DesensitizationEnum;
import com.lgh.common.sensitive.SensitiveValid;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.ReflectionUtils;
import java.lang.reflect.Field;
import java.util.Collection;
import java.util.Map;
/**
 * 對(duì)象脫敏工具
 *
 * @author lgh
 * @version 1.0
 * @date 2021/1/17
 */
public class DesensitizationUtils {
    private static final Logger log = LoggerFactory.getLogger(DesensitizationUtils.class);
    private DesensitizationUtils() {
    }
    /**
     * 掃描對(duì)象注解,脫敏,最高層次8層
     *
     * @param obj
     */
    public static void format(Object obj) {
        DesensitizationUtils.formatMethod(obj, 10);
    }
    /**
     * 遞歸遍歷數(shù)據(jù),因?yàn)榭赡苡袑?duì)象地址應(yīng)用導(dǎo)致循環(huán)問(wèn)題,同時(shí)設(shè)置莫名奇妙的異常,所以設(shè)置遞歸層次,一般不要超過(guò)10層
     *
     * @param obj   需要反射對(duì)象
     * @param level 遞歸層次,必須輸入
     */
    private static void formatMethod(Object obj, int level) {
        if (obj == null || isPrimitive(obj.getClass()) || level <= 0) {
            return;
        }
        if (obj.getClass().isArray()) {
            for (Object object : (Object[]) obj) {
                formatMethod(object, level--);
            }
        } else if (Collection.class.isAssignableFrom(obj.getClass())) {
            for (Object o : ((Collection) obj)) {
                formatMethod(o, level--);
            }
        } else if (Map.class.isAssignableFrom(obj.getClass())) {
            for (Object o : ((Map) obj).values()) {
                formatMethod(o, level--);
            }
        } else {
            objFormat(obj, level);
        }
    }
    /**
     * 只有對(duì)象才格式化數(shù)據(jù)
     *
     * @param obj
     * @param level
     */
    private static void objFormat(Object obj, int level) {
        for (Field field : obj.getClass().getDeclaredFields()) {
            try {
                if (isPrimitive(field.getType())) {
                    SensitiveValid sensitiveValid = field.getAnnotation(SensitiveValid.class);
                    if (sensitiveValid != null) {
                        ReflectionUtils.makeAccessible(field);
                        DesensitizationEnum desensitizationEnum = sensitiveValid.type();
                        Object fieldV = desensitizationEnum.getMethod().invoke(null, field.get(obj));
                        ReflectionUtils.setField(field, obj, fieldV);
                    }
                } else {
                    ReflectionUtils.makeAccessible(field);
                    Object fieldValue = ReflectionUtils.getField(field, obj);
                    if (fieldValue == null) {
                        continue;
                    }
                    formatMethod(fieldValue, level - 1);
                }
            } catch (Exception e) {
                log.error("脫敏數(shù)據(jù)處理異常", e);
            }
        }
    }
    /**
     * 基本數(shù)據(jù)類型和String類型判斷
     *
     * @param clz
     * @return
     */
    public static boolean isPrimitive(Class<?> clz) {
        try {
            if (String.class.isAssignableFrom(clz) || clz.isPrimitive()) {
                return true;
            } else {
                return ((Class) clz.getField("TYPE").get(null)).isPrimitive();
            }
        } catch (Exception e) {
            return false;
        }
    }
}

脫敏AOP的實(shí)現(xiàn)

aop插拔式編程,以便防止有不需要的操作,所以編寫可控制類注解EnableDesensitization

package com.lgh.common.sensitive;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
 * 方法返回值攔截器,需要注解才生效
 * @author lgh
 * @version 1.0
 * @date 2021/1/17
 **/
@Target({ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface EnableDesensitization {
}

最后實(shí)現(xiàn)攔截aop

package com.lgh.common.sensitive;
import com.lgh.common.utils.DesensitizationUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.context.annotation.Configuration;
import org.aspectj.lang.annotation.Aspect;
import java.lang.reflect.Method;
/**
 * @author lgh
 * @version 1.0
 * @date 2021/1/17
 */
@Aspect
@Configuration
public class SensitiveAspect {
    public static final String ACCESS_EXECUTION = "execution(* com.lgh.controller..*.*(..))";
    /**
     * 注解脫敏處理
     *
     * @param joinPoint
     * @return
     * @throws Throwable
     */
    @Around(ACCESS_EXECUTION)
    public Object sensitiveClass(ProceedingJoinPoint joinPoint) throws Throwable {
        return sensitiveFormat(joinPoint);
    }
    /**
     * 插拔式注解統(tǒng)一攔截器。@{link EnableDesensitization } 和 @SensitiveValid
     *
     * @param joinPoint
     * @return
     * @throws Throwable
     */
    public Object sensitiveFormat(ProceedingJoinPoint joinPoint) throws Throwable {
        Object obj = joinPoint.proceed();
        if (obj == null || DesensitizationUtils.isPrimitive(obj.getClass())) {
            return obj;
        }
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        EnableDesensitization desensitization = joinPoint.getTarget().getClass().getAnnotation(EnableDesensitization.class);
        if (desensitization != null || method.getAnnotation(EnableDesensitization.class) != null) {
            DesensitizationUtils.format(obj);
        }
        return obj;
    }
}

實(shí)戰(zhàn)演練

我居于上一章節(jié)的UserDetail對(duì)象增加phone字段,同時(shí)加入注解,如下:

package com.lgh.common.authority.entity;
import com.lgh.common.sensitive.DesensitizationEnum;
import com.lgh.common.sensitive.SensitiveValid;
public class UserDetail {
    private long id;
    private String name;
    @SensitiveValid(type = DesensitizationEnum.PHONE)
    private String phone;
    public long getId() {
        return id;
    }
    public void setId(long id) {
        this.id = id;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public void setPhone(String phone) {
        this.phone = phone;
    }
    public String getPhone() {
        return phone;
    }
}

接下來(lái)controller中啟動(dòng)注解

@GetMapping("/detail")
    @EnableDesensitization
    public IResult<UserDetail> getUser(@AuthenticationPrincipal UserDetail userDetail) {
        return CommonResult.successData(userDetail);
    }

大功告成,接下來(lái)我們實(shí)現(xiàn)一下訪問(wèn)操作

github,以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。

相關(guān)文章

  • 解決SpringBoot內(nèi)嵌Tomcat并發(fā)容量的問(wèn)題

    解決SpringBoot內(nèi)嵌Tomcat并發(fā)容量的問(wèn)題

    這篇文章主要介紹了解決SpringBoot內(nèi)嵌Tomcat并發(fā)容量的問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-06-06
  • 百度Java面試題 前200頁(yè)精選(中)

    百度Java面試題 前200頁(yè)精選(中)

    這篇文章主要為大家分享了Java面試資源中篇,百度“Java面試題”前200頁(yè)都在這里了,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2017-12-12
  • idea中使用maven?archetype新建項(xiàng)目時(shí)卡住問(wèn)題解決方案

    idea中使用maven?archetype新建項(xiàng)目時(shí)卡住問(wèn)題解決方案

    這篇文章主要介紹了idea中使用maven?archetype新建項(xiàng)目時(shí)卡住,解決本問(wèn)題的方法,就是在maven的runner加上參數(shù)-DarchetypeCatalog=local就可以了,不需要下載xml文件再放到指定目錄,需要的朋友可以參考下
    2023-08-08
  • Mybatis pagehelper分頁(yè)插件使用過(guò)程解析

    Mybatis pagehelper分頁(yè)插件使用過(guò)程解析

    這篇文章主要介紹了mybatis pagehelper分頁(yè)插件使用過(guò)程解析,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-02-02
  • 使用SpringBoot 配置Oracle和H2雙數(shù)據(jù)源及問(wèn)題

    使用SpringBoot 配置Oracle和H2雙數(shù)據(jù)源及問(wèn)題

    這篇文章主要介紹了使用SpringBoot 配置Oracle和H2雙數(shù)據(jù)源及問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-11-11
  • java過(guò)濾特殊字符操作(xss攻擊解決方案)

    java過(guò)濾特殊字符操作(xss攻擊解決方案)

    這篇文章主要介紹了java過(guò)濾特殊字符操作(xss攻擊解決方案),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-06-06
  • SpringBoot Admin的簡(jiǎn)單使用的方法步驟

    SpringBoot Admin的簡(jiǎn)單使用的方法步驟

    本文主要介紹了SpringBoot Admin的簡(jiǎn)單使用的方法步驟,文中通過(guò)示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-01-01
  • SpringBoot集成tika實(shí)現(xiàn)word轉(zhuǎn)html的操作代碼

    SpringBoot集成tika實(shí)現(xiàn)word轉(zhuǎn)html的操作代碼

    Tika是一個(gè)內(nèi)容分析工具,自帶全面的parser工具類,能解析基本所有常見格式的文件,得到文件的metadata,content等內(nèi)容,返回格式化信息,本文給大家介紹了SpringBoot集成tika實(shí)現(xiàn)word轉(zhuǎn)html的操作,需要的朋友可以參考下
    2024-06-06
  • 詳解Java中Callable和Future的區(qū)別

    詳解Java中Callable和Future的區(qū)別

    這篇文章主要介紹了Java中Callable和Future的區(qū)別,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2022-11-11
  • 一文掌握spring cloud gateway(總結(jié)篇)

    一文掌握spring cloud gateway(總結(jié)篇)

    Spring Cloud Gateway是Spring Cloud的全新項(xiàng)目,該項(xiàng)目是基于Spring 5.0,Spring WebFlux和Project Reactor等技術(shù)開發(fā)的網(wǎng)關(guān),它旨在為微服務(wù)架構(gòu)提供一種簡(jiǎn)單有效的統(tǒng)一的API路由管理方式,本文通過(guò)實(shí)例代碼總結(jié)介紹spring cloud gateway的相關(guān)知識(shí),感興趣的朋友一起看看吧
    2024-12-12

最新評(píng)論

时尚| 浠水县| 闽侯县| 阜新市| 恩施市| 保亭| 扎赉特旗| 拜城县| 昌吉市| 石嘴山市| 静安区| 监利县| 玛多县| 家居| 土默特左旗| 安宁市| 连州市| 兴安县| 舒城县| 布尔津县| 扎鲁特旗| 双城市| 巧家县| 博爱县| 桐城市| 安顺市| 潜山县| 阳山县| 页游| 垫江县| 宝应县| 山阳县| 通化市| 阳山县| 咸阳市| 四平市| 长泰县| 定陶县| 伽师县| 宕昌县| 邳州市|