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

Springboot+Jackson自定義注解數據脫敏的項目實踐

 更新時間:2023年08月10日 08:20:06   作者:少年醬105974  
數據脫敏可以對敏感數據比如 手機號、銀行卡號等信息進行轉換或者修改,本文主要介紹了Springboot+Jackson?自定義注解數據脫敏,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧

數據脫敏也叫數據的去隱私化,在我們給定脫敏規(guī)則和策略的情況下,對敏感數據比如 手機號、銀行卡號 等信息,進行轉換或者修改的一種技術手段,防止敏感數據直接在不可靠的環(huán)境下使用。

由于spring 接口返回JSON數據是使用的Jackson組件;我們可以利用Jackson+自定義注解的方式去實現數據脫敏;實現接口返回數據自動進行數據脫敏

一、導入相關依賴

<dependency>  
<groupId>org.springframework.boot</groupId>  
<artifactId>spring-boot-starter</artifactId>  
</dependency>  
<dependency>  
<groupId>com.fasterxml.jackson.core</groupId>  
<artifactId>jackson-databind</artifactId>  
</dependency>  
<dependency>  
<groupId>org.springframework.boot</groupId>  
<artifactId>spring-boot-configuration-processor</artifactId>  
<optional>true</optional>  
</dependency>  
<dependency>  
<groupId>org.springframework.boot</groupId>  
<artifactId>spring-boot-autoconfigure</artifactId>  
</dependency>  
<dependency>  
<groupId>org.apache.commons</groupId>  
<artifactId>commons-lang3</artifactId>  
</dependency>  
<dependency>  
<groupId>org.springframework.boot</groupId>  
<artifactId>spring-boot-starter-web</artifactId>  
</dependency>  
<dependency>  
<groupId>org.projectlombok</groupId>  
<artifactId>lombok</artifactId>  
</dependency>  
<dependency>  
<groupId>cn.hutool</groupId>  
<artifactId>hutool-core</artifactId>  
<version>${hutool.version}</version>  
</dependency>  
<dependency>  
<groupId>cn.hutool</groupId>  
<artifactId>hutool-crypto</artifactId>  
<version>${hutool.version}</version>  
</dependency>  
<dependency>  
<groupId>cn.hutool</groupId>  
<artifactId>hutool-system</artifactId>  
<version>${hutool.version}</version>  
</dependency>  
<dependency>  
<groupId>cn.hutool</groupId>  
<artifactId>hutool-json</artifactId>  
<version>5.8.9</version>  
</dependency>    

二、代碼實現

1、新建一個頂層的脫敏接口

import java.util.function.Function;  
/**  
* 自定義數據脫敏可實現當前接口 或直接在 SensitiveStrategy 中添加枚舉  
*  
* @author minjianguo  
* @date 2023/08/09  
*/  
@FunctionalInterface  
public interface IDesensitizeRule {  
/**  
* 脫敏操作  
*  
* @return {@link String}  
*/  
Function<String, String> desensitize();  
}  

2、創(chuàng)建一個枚舉類并繼承接口

利用枚舉類+函數式接口代替普通策略模式更加方便快捷

import cn.hutool.core.util.DesensitizedUtil;  
import com.poctip.encryption.util.UtilTools;  
import lombok.AllArgsConstructor;  
import java.util.function.Function;  
/**  
* 敏感戰(zhàn)略  
* 脫敏策略  
*  
* @author minjianguo  
* @version 3.6.0  
* @date 2023/08/09  
*/  
@AllArgsConstructor  
public enum SensitiveStrategy implements IDesensitizeRule {  
DEFAULT(s -> s),  
/**  
* 身份證脫敏  
*/  
ID_CARD(s -> DesensitizedUtil.idCardNum(s, 3, 4)),  
/**  
* 手機號脫敏  
*/  
PHONE(DesensitizedUtil::mobilePhone),  
/**  
* 地址脫敏  
*/  
ADDRESS(s -> UtilTools.encryption(s, 2, 0)),  
/**  
* 郵箱脫敏  
*/  
EMAIL(DesensitizedUtil::email),  
/**  
* 銀行卡  
*/  
BANK_CARD(DesensitizedUtil::bankCard),  
/**  
* 名字  
* 適配原有加密組件  
*/  
NAME(s -> UtilTools.encryption(s, 1, 0)),  
TELEPHONE(s -> UtilTools.encryption(s, 1, 3));  
//可自行添加其他脫敏策略  
private final Function<String, String> desensitizer;  
/**  
* 脫敏操作  
*  
* @return {@link String}  
*/  
@Override  
public Function<String, String> desensitize() {  
return desensitizer;  
}  
}    

3、自定義注解

  • strategy: 數據脫敏策略
  • isCustomRule:是否啟用自定義的規(guī)則
  • customRule:自定義的實現類

默認使用 strategy 枚舉里面的策略,如果要新增新的數據脫敏規(guī)則可直接在 SensitiveStrategy 類新增脫敏規(guī)則枚舉;(如果要把這個數據脫敏組件打成一個jar包的時候就沒辦法在 **SensitiveStrategy** 里面新增枚舉了;只能采用另一種方式新增自定義脫敏規(guī)則)示例如下:

  • 新增脫敏規(guī)則類實現 IDesensitizeRule 接口,重寫 desensitize 方法。
  • 在需要脫敏的字段標注 @Sensitive(isCustomRule = true,customRule = UnknownDesensitizeRule.class) UnknownDesensitizeRule 為自己的脫敏規(guī)則類 即可。\

@JacksonAnnotationsInside是一個元注解,它用于注解其他注解,以指示這些注解可以用于Jackson庫的注解處理器中。
@JsonSerialize(using = SensitiveHandler.class)是一個用于屬性或字段的注解,它指示Jackson庫在序列化過程中使用自定義的SensitiveHandler類進行處理。

import com.fasterxml.jackson.annotation.JacksonAnnotationsInside;  
import com.fasterxml.jackson.databind.annotation.JsonSerialize;  
import com.poctip.common.sensitive.core.IDesensitizeRule;  
import com.poctip.common.sensitive.core.SensitiveStrategy;  
import com.poctip.common.sensitive.core.UnknownDesensitizeRule;  
import com.poctip.common.sensitive.handler.SensitiveHandler;  
import java.lang.annotation.ElementType;  
import java.lang.annotation.Retention;  
import java.lang.annotation.RetentionPolicy;  
import java.lang.annotation.Target;  
/**  
* 敏感  
* 數據脫敏注解  
*  
* @author  
* @date 2023/08/09  
*/  
@Retention(RetentionPolicy.RUNTIME)  
@Target(ElementType.FIELD)  
@JacksonAnnotationsInside  
@JsonSerialize(using = SensitiveHandler.class)  
public @interface Sensitive {  
/**  
* 策略  
*  
* @return {@link SensitiveStrategy}  
*/  
SensitiveStrategy strategy() default SensitiveStrategy.DEFAULT;  
/**  
* 是否自定義規(guī)則  
*  
* @return boolean  
*/  
boolean isCustomRule() default false;  
/**  
* 自定義規(guī)則實現類  
*  
* @return {@link Class}<{@link ?} {@link extends} {@link IDesensitizeRule}>  
*/  
Class<? extends IDesensitizeRule> customRule() default UnknownDesensitizeRule.class;  
}  
  • SensitiveMethodMask :標注在要數據脫敏的接口方法上
  • exclude:排除當前接口不需要脫敏的字段
/**  
* 敏感方法面具  
*  
* @author minjianguo  
* @date 2023/08/03  
*/  
@Target(ElementType.METHOD)  
@Retention(RetentionPolicy.RUNTIME)  
public @interface SensitiveMethodMask {  
String[] exclude() default {};  
}    

3.1、創(chuàng)建一個默認的脫敏實現類;不做任何脫敏操作 UnknownDesensitizeRule;目的是為了給注解一個默認值

import java.util.function.Function;  
/**  
* 不處理數據脫敏  
*  
* @author minjianguo  
* @date 2023/08/09  
*/  
public class UnknownDesensitizeRule implements IDesensitizeRule{  
/**  
* 脫敏操作  
*  
* @return {@link String}  
*/  
@Override  
public Function<String, String> desensitize() {  
return s -> s;  
}  
}  

4、核心邏輯代碼

創(chuàng)建一個數據脫敏上下文SensitiveContextHolder

通過 SensitiveContextHolder和 spring 的 攔截器配合使用實現在當前接口總啟用或關閉或排除某些字段不進行序列化

/**  
* 上下文敏感持有人  
*  
* @author minjianguo  
* @date 2023/08/03  
*/  
public class SensitiveContextHolder {  
private static final ThreadLocal<Boolean> THREAD_LOCAL = ThreadLocal.withInitial(() -> false);  
private static final ThreadLocal<String[]> FIELD_THREAD_LOCAL = ThreadLocal.withInitial(() -> new String[0]);  
/**  
* 指定字段不脫敏  
*  
* @param fields 字段  
*/  
public static void filter(String... fields) {  
if (Objects.isNull(fields)) return;  
FIELD_THREAD_LOCAL.set(fields);  
}  
/**  
* 得到過濾字段  
*  
* @return {@link String[]}  
*/  
public static String[] getFilterFields() {  
return FIELD_THREAD_LOCAL.get();  
}  
public static boolean isDisable() {  
return THREAD_LOCAL.get();  
}  
public static void enable() {  
THREAD_LOCAL.set(true);  
}  
public static boolean isSensitive() {  
return THREAD_LOCAL.get();  
}  
/**  
* 禁用  
*/  
public static void disable() {  
THREAD_LOCAL.set(true);  
}  
/**  
* 清晰  
*/  
public static void clear() {  
THREAD_LOCAL.remove();  
FIELD_THREAD_LOCAL.remove();  
}  
}  

新增一個Jackson序列化器

  • 在Jackson庫中,JsonSerializer是一個抽象類,用于自定義對象的序列化過程。它將Java對象轉換為JSON字符串的過程中,允許開發(fā)人員對序列化過程進行自定義控制,并可以處理一些特定的序列化需求。
  • ContextualSerializer是Jackson庫中的一個接口,用于定義上下文相關的序列化器。它擴展了JsonSerializer接口,并添加了一個額外的方法createContextual()。
  • ContextualSerializer接口允許序列化器在序列化過程中獲取上下文信息,并根據上下文進行動態(tài)配置或自定義序列化邏輯。它提供了一種在序列化過程中動態(tài)決定序列化器行為的機制。
import cn.hutool.core.util.StrUtil;  
import com.fasterxml.jackson.core.JsonGenerator;  
import com.fasterxml.jackson.core.JsonStreamContext;  
import com.fasterxml.jackson.databind.BeanProperty;  
import com.fasterxml.jackson.databind.JsonMappingException;  
import com.fasterxml.jackson.databind.JsonSerializer;  
import com.fasterxml.jackson.databind.SerializerProvider;  
import com.fasterxml.jackson.databind.ser.ContextualSerializer;  
import com.poctip.common.sensitive.annotation.Sensitive;  
import com.poctip.common.sensitive.context.SensitiveContextHolder;  
import com.poctip.common.sensitive.core.IDesensitizeRule;  
import lombok.SneakyThrows;  
import lombok.extern.slf4j.Slf4j;  
import java.io.IOException;  
import java.util.Arrays;  
import java.util.Objects;  
/**  
* 數據脫敏json序列化工具  
*  
* @author Yjoioooo  
*/  
@Slf4j  
public class SensitiveHandler extends JsonSerializer<String> implements ContextualSerializer {  
private IDesensitizeRule strategy;  
@Override  
public void serialize(String value, JsonGenerator gen, SerializerProvider serializers) throws IOException {  
try {  
String[] filterFields = SensitiveContextHolder.getFilterFields();  
JsonStreamContext outputContext = gen.getOutputContext();  
String currentName = outputContext.getCurrentName();  
boolean noneMatch = Arrays.stream(filterFields).noneMatch(s -> StrUtil.equals(s, currentName));  
boolean isSensitive = SensitiveContextHolder.isSensitive() && noneMatch;  
if (isSensitive) {  
gen.writeString(strategy.desensitize().apply(value));  
} else {  
gen.writeString(value);  
}  
} catch (Exception e) {  
log.error("脫敏失敗 => {}", e.getMessage());  
gen.writeString(value);  
}  
}  
@SneakyThrows  
@Override  
public JsonSerializer<?> createContextual(SerializerProvider prov, BeanProperty property) throws JsonMappingException {  
Sensitive annotation = property.getAnnotation(Sensitive.class);  
if (Objects.nonNull(annotation) && Objects.equals(String.class, property.getType().getRawClass())) {  
if (annotation.isCustomRule()) {  
Class<? extends IDesensitizeRule> rule = annotation.customRule();  
this.strategy = rule.getDeclaredConstructor().newInstance();  
return this;  
}  
this.strategy = annotation.strategy();  
return this;  
}  
return prov.findValueSerializer(property.getType(), property);  
}  
}    

實現 spring 攔截器

步驟:

  • 在進入請求前判斷當前接口是否要開啟數據脫敏功能。
  • 在數據序列化完成之后 清除上下文,防止內存泄露。
import com.poctip.common.sensitive.annotation.SensitiveMethodMask;  
import com.poctip.common.sensitive.context.SensitiveContextHolder;  
import lombok.extern.slf4j.Slf4j;  
import org.springframework.web.method.HandlerMethod;  
import org.springframework.web.servlet.HandlerInterceptor;  
import javax.servlet.http.HttpServletRequest;  
import javax.servlet.http.HttpServletResponse;  
@Slf4j  
public class SensitiveInterceptor implements HandlerInterceptor {  
@Override  
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {  
// 在 MappingJackson2HttpMessageConverter 后執(zhí)行的自定義方法  
if (handler instanceof HandlerMethod) {  
HandlerMethod handlerMethod = (HandlerMethod) handler;  
SensitiveMethodMask annotation = getSensitiveMethodMaskAnnotation(handlerMethod);  
if (annotation != null) {  
enableSensitiveContext(annotation);  
}  
}  
return true;  
}  
@Override  
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {  
if (handler instanceof HandlerMethod) {  
if (getSensitiveMethodMaskAnnotation((HandlerMethod) handler) != null) {  
SensitiveContextHolder.clear();  
}  
}  
}  
/**  
* 啟用敏感上下文。  
*  
* @param annotation SensitiveMethodMask注解  
*/  
private void enableSensitiveContext(SensitiveMethodMask annotation) {  
SensitiveContextHolder.enable();  
SensitiveContextHolder.filter(annotation.exclude());  
}  
/**  
* 從處理方法中獲取SensitiveMethodMask注解。  
*  
* @param handlerMethod 處理方法  
* @return SensitiveMethodMask注解,如果不存在則返回null  
*/  
private SensitiveMethodMask getSensitiveMethodMaskAnnotation(HandlerMethod handlerMethod) {  
return handlerMethod.getMethod().getAnnotation(SensitiveMethodMask.class);  
}  
}  

新增一個手動脫敏的工具類

在某些場景下可以手動去處理數據脫敏。

import cn.hutool.core.annotation.AnnotationUtil;  
import cn.hutool.core.convert.Convert;  
import cn.hutool.core.util.ReflectUtil;  
import com.poctip.common.sensitive.annotation.Sensitive;  
import com.poctip.common.sensitive.core.SensitiveStrategy;  
import lombok.SneakyThrows;  
import org.apache.commons.lang3.StringUtils;  
import java.lang.reflect.Field;  
import java.util.List;  
import java.util.Objects;  
/**  
* 脫敏工具類  
*  
* @author minjianguo  
* @date 2022/12/02  
*/  
public class SensitiveUtils {  
/**  
* 數據脫敏  
*/  
@SneakyThrows  
public static <T> void dataDesensitization(T t) {  
if (Objects.isNull(t)) return;  
Field[] fields = ReflectUtil.getFields(t.getClass(), field -> field.isAnnotationPresent(Sensitive.class));  
for (Field field : fields) {  
Object fieldValue = ReflectUtil.getFieldValue(t, field);  
if (Objects.isNull(fieldValue)) {  
continue;  
}  
Sensitive sensitive = AnnotationUtil.getAnnotation(field, Sensitive.class);  
SensitiveStrategy strategy = sensitive.strategy();  
String desensitizationString = strategy.desensitize().apply(Convert.convert(String.class,fieldValue));  
if (StringUtils.isNotBlank(desensitizationString)) {  
ReflectUtil.setFieldValue(t, field, desensitizationString);  
}  
}  
}  
/**  
* 數據脫敏列表  
*  
* @param list 列表  
*/  
public static <T> void dataDesensitizationList(List<T> list){  
list.forEach(SensitiveUtils::dataDesensitization);  
}  
@SneakyThrows  
public static String dataDesensitizationString(String value, SensitiveStrategy strategy) {  
if (StringUtils.isBlank(value) || Objects.isNull(strategy)) {  
return value;  
}  
return strategy.desensitize().apply(value);  
}  
}    

三、配置spring 攔截器

配置自定義的攔截器。

@Configuration  
public class SensitiveConfig implements WebMvcConfigurer {  
@Override  
public void addInterceptors(InterceptorRegistry registry) {  
registry.addInterceptor(new SensitiveInterceptor())  
.addPathPatterns("/**"); // 這里可以指定攔截的路徑  
}  
}    

四、使用

在需要數據脫敏的實體類字段標注注解 @Sensitive(,并指定脫敏策略

@Sensitive(strategy = SensitiveStrategy.PHONE)  
private String phone;  

在接口方法上標注 @SensitiveMethodMask 注解

@SensitiveMethodMask  
@GetMapping("/test")  
public UserDemo test() {  
UserDemo userDemo = new UserDemo();  
userDemo.setId(1);  
userDemo.setName("java");  
userDemo.setPhone("18160360561");  
return userDemo;  
}  

測試返回:

{  
"id": 1,  
"name": "java",  
"phone": "181****0561"  
}  

注意事項:
接口的返回值不能是 object 或 Map 等類型,不然在序列化時無法找到 **@Sensitive** 注解使數據脫敏功能失效,一定要正確指定返回的數據類型。

到此這篇關于Springboot+Jackson自定義注解數據脫敏的項目實踐的文章就介紹到這了,更多相關Springboot Jackson數據脫敏內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!

相關文章

  • idea 有時提示找不到類或者符號的解決

    idea 有時提示找不到類或者符號的解決

    這篇文章主要介紹了idea 有時提示找不到類或者符號的解決,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2020-09-09
  • SpringBoot使用Interceptor攔截器的實例

    SpringBoot使用Interceptor攔截器的實例

    這篇文章主要介紹了SpringBoot使用Interceptor攔截器的相關知識,本文通過實例代碼給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2023-03-03
  • Java 遞歸遍歷實現linux tree命令方式

    Java 遞歸遍歷實現linux tree命令方式

    這篇文章主要介紹了Java 遞歸遍歷實現linux tree命令方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-09-09
  • Java中HashMap獲取值的幾種方式匯總

    Java中HashMap獲取值的幾種方式匯總

    這篇文章主要介紹了Java中HashMap獲取值的幾種方式匯總,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2023-03-03
  • 冒泡排序算法原理及JAVA實現代碼

    冒泡排序算法原理及JAVA實現代碼

    關鍵字較小的記錄好比氣泡逐趟上浮,關鍵字較大的記錄好比石塊下沉,每趟有一塊最大的石塊沉底
    2014-01-01
  • Java使用Lambda表達式實現排序功能的實現代碼

    Java使用Lambda表達式實現排序功能的實現代碼

    在 Java 中使用 Lambda 表達式進行排序是一種簡潔且功能強大的方式,特別是在對集合中的對象進行自定義排序時,Lambda 表達式可以與 Collections.sort() 或 List.sort() 方法結合使用,本文給大家介紹了Java使用Lambda表達式實現排序功能的方法,需要的朋友可以參考下
    2025-07-07
  • Java 中責任鏈模式實現的三種方式

    Java 中責任鏈模式實現的三種方式

    本文重點給大家介紹java中如何編寫責任鏈模式。主要從下面3個框架中的代碼中介紹。非常不錯,需要的朋友參考下吧
    2017-09-09
  • Spring中的三級緩存與循環(huán)依賴詳解

    Spring中的三級緩存與循環(huán)依賴詳解

    Spring三級緩存是Spring框架中用于解決循環(huán)依賴問題的一種機制,這篇文章主要介紹了Spring三級緩存與循環(huán)依賴的相關知識,本文給大家介紹的非常詳細,需要的朋友可以參考下
    2024-05-05
  • mybatisPlus?FieldStrategy?策略作用小結

    mybatisPlus?FieldStrategy?策略作用小結

    MyBatis-Plus的FieldStrategy枚舉用于控制實體字段在插入、更新和查詢條件中的空值處理策略,下面就來詳細的介紹一下mybatisPlus?FieldStrategy?策略,感興趣的可以了解一下
    2025-12-12
  • 如何使用Logback日志保存到相對路徑

    如何使用Logback日志保存到相對路徑

    在使用Logback中需要保存輸出日志,但是卻在保存的時候路徑出現問題,下面通過案例代碼講解如何使用Logback日志保存到相對路徑,感興趣的朋友一起看看吧
    2025-05-05

最新評論

米泉市| 泗水县| 会昌县| 龙海市| 屏边| 石台县| 读书| 天门市| 涞源县| 九江市| 桑植县| 钟祥市| 新宁县| 商水县| 浦城县| 五河县| 左权县| 泾阳县| 正镶白旗| 油尖旺区| 黄浦区| 阳东县| 屏东市| 佛山市| 津南区| 丽水市| 喀什市| 桐柏县| 循化| 山阳县| 长寿区| 郯城县| 梨树县| 饶阳县| 栾川县| 资兴市| 水富县| 成安县| 汕尾市| 全州县| 日喀则市|