SpringBoot項目數據脫敏(自定義注解)

文章目錄

  • 前言
  • 一.配置
    • 1.脫敏類型枚舉:DesensitizeType
    • 2.注解:Desensitize
    • 3.序列化類:DesensitizeJsonSerializer
    • 4.工具類:DesensitizeUtil
  • 二、測試:DesensitizeTest
  • 三、效果展示
  • 總結

前言

在互聯網應用中,用戶隱私數據(如姓名、身份證號、手機號、郵箱等)的保護至關重要。為了防止敏感信息在日志、接口返回、前端展示等環節泄露,數據脫敏 成為系統開發中的必備功能

一.配置

1.脫敏類型枚舉:DesensitizeType

import lombok.Getter;
/*** 脫敏類型枚舉*/
@Getter
public enum DesensitizeType {NAME(0, "中文姓名"),ID_CARD(1, "身份證號"),EMAIL(2, "郵箱"),PHONE(3, "手機號"),CUSTOM(4, "自定義");private final int code;private final String description;DesensitizeType(int code, String description) {this.code = code;this.description = description;}/*** 通過 code 反查枚舉*/public static DesensitizeType fromCode(int code) {for (DesensitizeType type : values()) {if (type.getCode() == code) {return type;}}throw new IllegalArgumentException("未知的脫敏類型 code: " + code);}
}

使用 @Getter 自動生成 getter,fromCode 支持通過 code 查找枚舉,便于后續擴展配置化脫敏策略

2.注解:Desensitize

import com.fasterxml.jackson.annotation.JacksonAnnotationsInside;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.fc.enums.DesensitizeType;
import com.fc.serializer.DesensitizeJsonSerializer;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;/*** 自定義數據脫敏注解*/
@Target(ElementType.FIELD)//用于字段
@Retention(RetentionPolicy.RUNTIME)
@JacksonAnnotationsInside
@JsonSerialize(using = DesensitizeJsonSerializer.class) // 該注解使用序列化的方式
public @interface Desensitize {DesensitizeType type();//脫敏數據類型(必須指定類型)int prefixNoMaskLen() default 3; // 手機號通常保留前3位int suffixNoMaskLen() default 4; // 手機號通常保留后4位String symbol() default "*";//替換符號
}

3.序列化類:DesensitizeJsonSerializer

import com.fasterxml.jackson.core.JsonGenerator;
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.fc.anno.Desensitize;
import com.fc.enums.DesensitizeType;
import com.fc.utils.DesensitizeUtil;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import java.io.IOException;
import java.util.EnumMap;
import java.util.Objects;
import java.util.function.UnaryOperator;
@EqualsAndHashCode(callSuper = true)
@Data
@NoArgsConstructor
@AllArgsConstructor
public class DesensitizeJsonSerializer extends JsonSerializer<String> implements ContextualSerializer {private DesensitizeType type;private int prefixLen;private int suffixLen;private String mask;/* 策略表:DesensitizeType → 函數 */private static final EnumMap<DesensitizeType, UnaryOperator<String>> STRATEGY = new EnumMap<>(DesensitizeType.class);static {STRATEGY.put(DesensitizeType.NAME, DesensitizeUtil::hideName);STRATEGY.put(DesensitizeType.ID_CARD, DesensitizeUtil::hideIdCard);STRATEGY.put(DesensitizeType.EMAIL, DesensitizeUtil::hideEmail);STRATEGY.put(DesensitizeType.PHONE, DesensitizeUtil::hidePhone);}@Overridepublic void serialize(String value, JsonGenerator gen, SerializerProvider serializers)throws IOException {if (value == null) {                // 1) 空值透傳gen.writeNull();return;}if (type == DesensitizeType.CUSTOM) {gen.writeString(DesensitizeUtil.customMask(value, prefixLen, suffixLen, mask));} else {UnaryOperator<String> func = STRATEGY.get(type);if (func == null) {throw new IllegalArgumentException("未注冊的策略:" + type);}gen.writeString(func.apply(value));}}/* 讀取注解信息,構造專屬序列化器 */@Overridepublic JsonSerializer<?> createContextual(SerializerProvider prov, BeanProperty property)throws JsonMappingException {if (property != null && Objects.equals(property.getType().getRawClass(), String.class)) {Desensitize anno = property.getAnnotation(Desensitize.class);if (anno != null) {return new DesensitizeJsonSerializer(anno.type(), anno.prefixNoMaskLen(), anno.suffixNoMaskLen(), anno.symbol());}}return prov.findValueSerializer(property.getType(), property);}
}

ContextualSerializer 允許在序列化前讀取字段的注解信息。
每個帶 @Desensitize 的字段都會生成一個“專屬”序列化器,攜帶注解參數。
使用 EnumMap 存儲策略,性能優于 if-else 或 switch。

4.工具類:DesensitizeUtil

import org.apache.commons.lang.StringUtils;
import java.util.regex.Pattern;
/*** 脫敏工具類*/
public class DesensitizeUtil {/* 預編譯正則表達式(提升性能) */private static final Pattern PHONE_PATTERN = Pattern.compile("(\\d{3})\\d{4}(\\d{4})");private static final Pattern EMAIL_PATTERN = Pattern.compile("(^.)[^@]*(@.*$)");private static final Pattern ID_CARD_PATTERN = Pattern.compile("(\\d{4})\\d{10}(\\w{4})");/*** 中文姓名:張三 → 張**/public static String hideName(String name) {if (StringUtils.isBlank(name)) return name;return name.charAt(0) + "*".repeat(Math.max(0, name.length() - 1));}/*** 手機號:13812345678 → 138****5678*/public static String hidePhone(String phone) {if (StringUtils.isBlank(phone)) return phone;return PHONE_PATTERN.matcher(phone).replaceFirst("$1****$2");}/*** 郵箱:zhangsan@163.com → z****@163.com*/public static String hideEmail(String email) {if (StringUtils.isBlank(email)) return email;return EMAIL_PATTERN.matcher(email).replaceFirst("$1****$2");}/*** 身份證:123456789012345678 → 1234****5678*/public static String hideIdCard(String idCard) {if (StringUtils.isBlank(idCard)) return idCard;return ID_CARD_PATTERN.matcher(idCard).replaceFirst("$1****$2");}/*** 通用脫敏:保留前后指定長度,中間用符號填充*/public static String customMask(String origin, int prefix, int suffix, String mask) {if (origin == null || origin.isEmpty()) return origin;int len = origin.length();if (prefix + suffix >= len) return origin; // 不脫敏String prefixStr = origin.substring(0, prefix);String suffixStr = origin.substring(len - suffix);String masked = mask.repeat(len - prefix - suffix);return prefixStr + masked + suffixStr;}
}

使用 StringUtils.isBlank 來安全判斷空值;正則預編譯提升性能

二、測試:DesensitizeTest

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fc.anno.Desensitize;
import com.fc.enums.DesensitizeType;
import lombok.Builder;
import lombok.Data;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import static org.assertj.core.api.Assertions.assertThat;
@SpringBootTest
public class DesensitizeTest {@Data@Builderpublic static class UserVO {private Long id;private Long roleId;@Desensitize(type = DesensitizeType.ID_CARD)private String idCard;@Desensitize(type = DesensitizeType.NAME)private String name;@Desensitize(type = DesensitizeType.PHONE)private String phoneNumber;@Desensitize(type = DesensitizeType.EMAIL)private String email;@Desensitize(type = DesensitizeType.CUSTOM, prefixNoMaskLen = 2, suffixNoMaskLen = 2, symbol = "#")private String bankCard;private String username;}@Testpublic void testDesensitizeWithInlineVO() throws JsonProcessingException {// 創建測試數據UserVO user = UserVO.builder().id(1L).roleId(101L).idCard("110105199003075678").name("歐陽鋒").phoneNumber("13812345678").email("ouyangfeng@shaguyin.com").bankCard("6228480031567890123").username("ofeng").build();// 創建 ObjectMapper(確保注冊了自定義序列化器)ObjectMapper mapper = new ObjectMapper();String json = mapper.writeValueAsString(user);System.out.println("序列化結果:");System.out.println(json);// 斷言脫敏效果assertThat(json).contains("\"idCard\":\"1101****5678\"");assertThat(json).contains("\"name\":\"歐**\"");assertThat(json).contains("\"phoneNumber\":\"138****5678\"");assertThat(json).contains("\"email\":\"o****@shaguyin.com\"");assertThat(json).contains("\"bankCard\":\"62###############23\"");assertThat(json).contains("\"username\":\"ofeng\"");}
}

在這里插入圖片描述

三、效果展示

在這里插入圖片描述

import com.fc.anno.Desensitize;
import com.fc.enums.DesensitizeType;
import lombok.Builder;
import lombok.Data;import java.time.LocalDateTime;@Data
@Builder
public class UserVO {private Long id;private Long roleId;@Desensitize(type = DesensitizeType.ID_CARD)private String idCard;private String username;private String name;private String phoneNumber;private LocalDateTime createTime;private LocalDateTime lastLogin;
}

在這里插入圖片描述

總結

本文實現了一套基于 Jackson 序列化 + 注解 + 策略模式 的輕量級數據脫敏框架,具備以下優點:
? 無侵入性:僅需在 VO 字段添加注解
? 高性能:使用 EnumMap + 預編譯正則
? 易擴展:新增類型只需添加枚舉和策略
? 靈活配置:支持自定義前后綴與掩碼符號
? 無縫集成:適用于 Spring Boot + Jackson 項目

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/pingmian/91776.shtml
繁體地址,請注明出處:http://hk.pswp.cn/pingmian/91776.shtml
英文地址,請注明出處:http://en.pswp.cn/pingmian/91776.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

PSO-TCN-BiLSTM-MATT粒子群優化算法優化時間卷積神經網絡-雙向長短期記憶神經網絡融合多頭注意力機制多特征分類預測/故障診斷Matlab實現

基本介紹 1.Matlab實現PSO-TCN-BiLSTM-MATT粒子群算法優化時間卷積神經網絡-雙向長短期記憶神經網絡融合多頭注意力機制多特征分類預測&#xff0c;PSO-TCN-BiLSTM-Multihead-Attention&#xff1b; 多頭自注意力層 (Multihead-Self-Attention)&#xff1a;Multihead-Self-Atte…

第一篇:Linux 運維入門:虛擬機部署與基礎環境配置

目錄 一、準備工作與環境規劃 二、虛擬機網絡配置 1、虛擬網絡編輯器設置 2、系統網絡配置 3、主機名配置 三、Hosts 文件與 SSH 免密配置 配置 hosts 文件編輯/etc/hosts文件實現主機名解析&#xff1a; 分發 hosts 文件到其他節點 SSH 免密登錄配置在 zhangsan101 上…

(一)全棧(react配置/https支持/useState多組件傳遞/表單提交/React Query/axois封裝/Router)

文章目錄 項目地址 一、基礎配置 1.1 支持https 1. 安裝所需要的包 2. 配置 1.2 常用 1. 字符串拼接 二、組件 2.1 useState組件傳遞 1. App里初始化useState 2. useState和方法的傳遞 3. 接收傳遞來的狀態和方法 2.2 表單提交 1. 表單組件處理用戶輸入數據 2. App傳來的submit…

【abc417】E - A Path in A Dictionary

Problem StatementYou are given a simple connected undirected graph G with N vertices and M edges. The vertices of G are numbered vertex 1, vertex 2, …, vertex N, and the i-th (1≤i≤M) edge connects vertices Ui? and Vi?.Find the lexicographically smalle…

linux火焰圖

火焰圖簡介火焰圖是一種性能分析的可視化工具&#xff0c;它將CPU的調用棧&#xff08;Call Stack&#xff09;信息以矩形火焰的形式展現出來。Y軸&#xff1a;代表調用棧的深度&#xff08;函數A調用了函數B&#xff0c;B就疊在A上面&#xff09;。X軸&#xff1a;代表CPU的抽…

解剖 .NET 經典:從 Component 到 BackgroundWorker

1?? 背景與定位在 .NET Framework 2.0 時代&#xff0c;微軟引入了 BackgroundWorker 來解決 WinForm/WPF 場景下“耗時操作阻塞 UI 線程”的問題&#xff1b;而 Component 早在 1.0 就已存在&#xff0c;是所有可視化/非可視化設計器的“基類”。理解這兩者的源碼與機制&…

桌面端界面設計 |貨物 TMS 系統 - SaaS UI UX 設計:審美積累之境

在物流數字化的浪潮中&#xff0c;貨物 TMS 系統的 SaaS 化與 UI/UX 設計正構建著獨特的審美坐標系。這不僅是技術與功能的融合&#xff0c;更是一場關于效率美學的深度探索&#xff0c;為行業審美積累注入了鮮活的實踐樣本。SaaS 模式賦予貨物 TMS 系統輕盈而強大的特質&#…

多架構鏡像整合全攻略:在Docker中實現單一鏡像支持同時支持amd64和arm64架構

多架構支持的挑戰 &#xff1a;隨著異構計算&#xff08;如 ARM、x86、RISC-V 等&#xff09;的普及&#xff0c;開發者需要為不同硬件平臺提供對應的鏡像&#xff0c;傳統方式需維護多個版本&#xff08;如 image:v1-amd64 和 image:v1-arm64 &#xff09;&#xff0c;導致版本…

Linux730 tr:-d /-s;sort:-r,-n,-R,-o,-t,-k,-u;bash;cut:-d,-c;tee -a;uniq -c -i

回顧 sort sort [選項] 文件-u&#xff1a;唯一&#xff0c;去除重復 -r:按數字大小&#xff0c;倒序排序&#xff0c;大到小 -o:輸出文件 -n:按數字大小&#xff0c;順序排序&#xff0c;小到大 -t: -t后加分割符&#xff0c;按分割符為標準&#xff0c;進行篩選 -k:k后加數字…

力扣457:環形數組是否存在循環

力扣457:環形數組是否存在循環題目思路代碼題目 存在一個不含 0 的 環形 數組 nums &#xff0c;每個 nums[i] 都表示位于下標 i 的角色應該向前或向后移動的下標個數&#xff1a; 如果 nums[i] 是正數&#xff0c;向前&#xff08;下標遞增方向&#xff09;移動 |nums[i]| 步…

在 Elasticsearch 中落地 Learning to Rank(LTR)

1 為什么要引入 LTR&#xff1f; 常規檢索&#xff08;BM25、語義檢索、Hybrid、RRF …&#xff09;往往只能基于少量信號&#xff08;關鍵詞命中、向量相似度&#xff09;排序。 Learning-to-Rank 通過機器學習模型把多維度特征&#xff08;文檔屬性、查詢屬性、查詢-文檔相關…

Socket編程——TCP協議

文章目錄一、TCP傳輸二、相關接口三、多進程版本四、多線程版本一、TCP傳輸 TCP和UDP類似&#xff0c;但是在傳輸中TCP有輸入&#xff0c;輸出緩沖區&#xff0c;看下面的傳輸圖片 可以理解為TCP之間的數據傳輸都是依賴各自的socket&#xff0c;socket就充當傳輸的中介吧。 而…

GitHub使用小記——本地推送、外部拉取和分支重命名

GitHub 項目推送與拉取等操作使用隨記 本小記適用于個人項目或組織項目&#xff0c;涵蓋 GitHub 推送、拉取、分支管理、.gitignore 設置等常見需求。 1. 將已有本地工程推送至 GitHub 新倉庫 1.1 前提條件 本地項目結構完整&#xff0c;已準備好&#xff1b;本地已安裝 Git…

RabbitMQ 延時隊列插件安裝與使用詳解(基于 Delayed Message Plugin)

RabbitMQ 延時隊列插件安裝與使用詳解&#xff08;基于 Delayed Message Plugin&#xff09;&#x1f4cc; 一、什么是 RabbitMQ 延時隊列&#xff1f;&#x1f680; 二、安裝前準備? RabbitMQ 環境要求&#x1f527; 三、安裝延時隊列插件&#x1f9e9; 插件名稱&#xff1a;…

Vue項目使用ssh2-sftp-client實現打包自動上傳到服務器(完整教程)

告別手動拖拽上傳&#xff01;本教程將手把手教你如何通過ssh2-sftp-client實現Vue項目打包后自動上傳到服務器&#xff0c;提升部署效率300%。&#x1f680;一、需求場景與解決方案在Vue項目開發中&#xff0c;每次執行npm run build后都需要手動將dist目錄上傳到服務器&#…

《質光相濟:Three.js中3D視覺的底層交互邏輯》

在Three.js搭建的虛擬維度中,光照與材質的關系遠非技術參數的簡單疊加,當光線以數字形態穿越虛空,與物體表面相遇的瞬間,便開始書寫屬于這個世界的物理敘事——每一縷光斑的形狀、每一塊陰影的濃淡、每一寸肌理的反光,都是對現實光學規律的轉譯與重構。理解這種交互的深層…

無刷電機在汽車領域的應用與驅動編程技術

文章目錄引言一、核心應用場景1. 新能源汽車動力系統2. 底盤控制系統3. 車身與舒適系統4. 智能駕駛與安全系統二、無刷電機的技術優勢解析三、無刷電機驅動編程基礎1. 驅動原理2. 驅動架構四、核心控制算法與實現1. 六步換向法&#xff08;梯形波控制&#xff09;算法流程圖C語…

【游戲引擎之路】登神長階(十八):3天制作Galgame引擎《Galplayer》——無敵之道心

游戲引擎開發記錄&#xff1a;2024年 5月20日-6月4日&#xff1a;攻克2D物理引擎。 2024年 6月4日-6月13日&#xff1a;攻克《3D數學基礎》。 2024年 6月13日-6月20日&#xff1a;攻克《3D圖形教程》。 2024年 6月21日-6月22日&#xff1a;攻克《Raycasting游戲教程》。 2024年…

kotlin kmp 跨平臺環境使用sqldelight

歡迎訪問我的主頁: https://heeheeaii.github.io/ 1. 項目結構 SQLDelightKMPDemo/ ├── shared/ │ ├── src/ │ │ ├── commonMain/kotlin/ │ │ ├── androidMain/kotlin/ │ │ ├── desktopMain/kotlin/ │ │ └── commonMain/sqldel…

機器學習【五】decision_making tree

決策樹是一種通過樹形結構進行數據分類或回歸的直觀算法&#xff0c;其核心是通過層級決策路徑模擬規則推理。主要算法包括&#xff1a;ID3算法基于信息熵和信息增益選擇劃分屬性&#xff1b;C4.5算法改進ID3&#xff0c;引入增益率和剪枝技術解決多值特征偏差&#xff1b;CART…