MyBatis攔截器插件:實現敏感數據字段加解密

文章目錄

  • 一、寫在前面
  • 二、編碼實現
    • 1、注解
    • 2、攔截器插件
    • 3、配置插件
    • 4、實體類
    • 5、測試
  • 三、擴展
    • 1、優化點

一、寫在前面

日常開發中,經常有一些敏感數據,直接寫入數據庫的話,很容易泄露。
本文基于mybatis攔截器插件,實現敏感數據的加解密。

二、編碼實現

1、注解

import java.lang.annotation.*;@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface EncryptedField {String algorithm() default "AES";
}

2、攔截器插件


import com.example.encryption.annotation.EncryptedField;
import com.example.encryption.service.EncryptionService;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.SqlCommandType;
import org.apache.ibatis.plugin.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.lang.reflect.Field;
import java.util.Properties;@Component
@Intercepts({@Signature(type = Executor.class, method = "update", args = {MappedStatement.class, Object.class}),@Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, org.apache.ibatis.session.RowBounds.class, org.apache.ibatis.session.ResultHandler.class})
})
public class EncryptDecryptInterceptor implements Interceptor {@Autowiredprivate EncryptionService encryptionService;@Overridepublic Object intercept(Invocation invocation) throws Throwable {MappedStatement ms = (MappedStatement) invocation.getArgs()[0];Object parameter = invocation.getArgs()[1];// 加密處理 INSERT/UPDATEif (ms.getSqlCommandType() == SqlCommandType.INSERT || ms.getSqlCommandType() == SqlCommandType.UPDATE) {handleEncryption(parameter);}Object result = invocation.proceed();// 解密處理 SELECTif (ms.getSqlCommandType() == SqlCommandType.SELECT) {handleDecryption(result);}return result;}private void handleEncryption(Object parameter) throws Exception {if (parameter == null) return;for (Field field : parameter.getClass().getDeclaredFields()) {if (field.isAnnotationPresent(EncryptedField.class)) {field.setAccessible(true);Object value = field.get(parameter);if (value instanceof String) {field.set(parameter, encryptionService.encrypt((String) value));}}}}private void handleDecryption(Object result) throws Exception {if (result == null) return;if (result instanceof java.util.Collection) {for (Object obj : (java.util.Collection<?>) result) {decryptObject(obj);}} else {decryptObject(result);}}private void decryptObject(Object obj) throws Exception {for (Field field : obj.getClass().getDeclaredFields()) {if (field.isAnnotationPresent(EncryptedField.class)) {field.setAccessible(true);Object value = field.get(obj);if (value instanceof String) {field.set(obj, encryptionService.decrypt((String) value));}}}}@Overridepublic Object plugin(Object target) {return Plugin.wrap(target, this);}@Overridepublic void setProperties(Properties properties) {}
}

3、配置插件

import com.example.encryption.interceptor.EncryptDecryptInterceptor;
import org.mybatis.spring.boot.autoconfigure.ConfigurationCustomizer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;@Configuration
public class MyBatisConfig {@Beanpublic ConfigurationCustomizer configurationCustomizer(EncryptDecryptInterceptor interceptor) {return configuration -> configuration.addInterceptor(interceptor);}
}

4、實體類


import com.example.encryption.annotation.EncryptedField;public class User {private Long id;private String username;@EncryptedFieldprivate String idCard;@EncryptedFieldprivate String phoneNumber;// Getters and Setterspublic Long getId() { return id; }public void setId(Long id) { this.id = id; }public String getUsername() { return username; }public void setUsername(String username) { this.username = username; }public String getIdCard() { return idCard; }public void setIdCard(String idCard) { this.idCard = idCard; }public String getPhoneNumber() { return phoneNumber; }public void setPhoneNumber(String phoneNumber) { this.phoneNumber = phoneNumber; }// toString@Overridepublic String toString() {return "User{" +"id=" + id +", username='" + username + '\'' +", idCard='" + idCard + '\'' +", phoneNumber='" + phoneNumber + '\'' +'}';}}

5、測試


import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;import com.example.encryption.entity.User;
import com.example.encryption.mapper.UserMapper;@SpringBootApplication
public class EncryptionDemoApplication {public static void main(String[] args) {ConfigurableApplicationContext run = SpringApplication.run(EncryptionDemoApplication.class, args);UserMapper userMapper = run.getBean(UserMapper.class);// 增User user = new User();user.setId(1L);user.setUsername("test1");user.setIdCard("111111111111");user.setPhoneNumber("1311111");userMapper.insert(user);System.out.println(userMapper.selectById(1L));;// 改user.setUsername("test2");user.setIdCard("2222222222");user.setPhoneNumber("1322222222");userMapper.updateById(user);System.out.println(userMapper.selectById(1L));;System.out.println(userMapper.selectById(1L));;}
}

在這里插入圖片描述

三、擴展

1、優化點

1、插件使用反射對類進行賦值、獲取值,為了提高性能,可以考慮將字段進行緩存(使用ConcurrentHashMap)
2、加解密方法,可以考慮擴展成接口,加密方式可擴展。
3、本內容只支持MyBatis簡單場景,MyBatisPlus、分頁場景、參數為List、Map或者復雜對象,需要對參數進一步遞歸處理。

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

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

相關文章

C++_Hello算法_隊列

隊列&#xff08;queue&#xff09;是一種遵循先入先出規則的線性數據結構。顧名思義&#xff0c;隊列模擬了排隊現象&#xff0c;即新來的人不斷加入隊列尾部&#xff0c;而位于隊列頭部的人逐個離開。 如圖 5-4 所示&#xff0c;我們將隊列頭部稱為“隊首”&#xff0c;尾部…

LeetCode 1.

問題描述 倆數之和&#xff1a; 給定一個整數數組 nums 和一個整數目標值 target&#xff0c;請你在該數組中找出 和為目標值 target 的那 兩個 整數&#xff0c;并返回它們的數組下標。你可以假設每種輸入只會對應一個答案&#xff0c;并且你不能使用兩次相同的元素。你可以按…

c練習-c基礎

#include <stdio.h>int main() {//打印數組中的最大值int arr[10];int max,i;for (i 0; i < 10; i){scanf_s("%d", &arr[i]);}max arr[0];for (i 0; i < 10; i){if(max < arr[i 1]){max arr[i 1];}}printf("數組中最大值&#xff1a;%…

Numpy科學計算(五分鐘小白從入門到精通)

2.1 numpy介紹numpy是Python中科學計算的基礎包。它是一個Python庫&#xff0c;提供多維數組對象、各種派生對象&#xff08;例如掩碼數組和矩陣&#xff09;以及用于對數組進行快速操作的各種方法&#xff0c;包括數學、邏輯、形狀操作、排序、選擇、I/O 、離散傅里葉變換、基…

從零掌握微服務通信安全:mTLS全解析

&#x1f525;「炎碼工坊」技術彈藥已裝填&#xff01; 點擊關注 → 解鎖工業級干貨【工具實測|項目避坑|源碼燃燒指南】 在云原生時代&#xff0c;微服務架構的普及帶來了靈活性和可擴展性&#xff0c;但也讓服務間通信的安全性成為核心挑戰。mTLS&#xff08;Mutual TLS&…

Node.js net.Socket.destroy()深入解析

socket.destroy() 是 Node.js net 模塊中用于強制銷毀 TCP 套接字的方法&#xff0c;比 socket.end() 更徹底。下面我將從多個方面全面講解這個方法。 基本用法 const net require(net);const server net.createServer((socket) > {// 強制銷毀套接字socket.destroy(); })…

vmware 克隆虛擬機,報錯:克隆時出錯:指定不存在的設備。然后電腦卡死,只能強制關機再開機。

vmware 克隆虛擬機&#xff0c;報錯&#xff1a;克隆時出錯:指定不存在的設備。然后電腦卡死&#xff0c;只能強制關機再開機。1、問題描述2、問題原因3、解決方法1、問題描述 vmware 版本&#xff1a;vmware workstation pro 17.6.3 克隆虛擬機時&#xff0c;創建完整克隆&am…

如何使用Python將任意PPT變為“智能模板”(解決 python-pptx 替換元素無法保留格式的問題,陰影、填充等屬性保留!)

文章目錄 ?? 介紹 ?? ?? 演示環境 ?? ?? 深入 OpenXML:格式保留的終極武器 ?? ?? 如何打造你自己的“格式保留”PPT模板? ?? 為什么格式會丟失? ??? 方案一:圖片的“格式移植”大法 ??? 實現代碼 ?? 原理解析 ?? 方案二:文本的“外科手術”大法…

學習python中離線安裝pip及下載package的方法

正常而言&#xff0c;Python 3.4及以上版本默認自帶pip工具?&#xff0c;無需額外安裝&#xff0c;如果需要單獨離線安裝pip&#xff0c;可以先使用DeepSeek查看指定操作系統能安裝的最高pip版本&#xff0c;然后在參考文獻1中現在指定版本的pip離線安裝文件&#xff0c;通常為…

liunx運維進階腳本

一、文件與目錄操作1.快速創建目錄樹mkdir -p project/{src,doc,test/{unit,integration}}創建嵌套目錄結構&#xff0c;避免逐層創建。2查找并刪除7天前的日志文件find /var/log -name "*.log" -mtime 7 -exec rm -f {} \;結合find和exec實現定時清理。3.批量重命名…

Apache Ignite 中的 SQL 模式(Schema)管理機制

這段內容講的是 Apache Ignite 中的 SQL 模式&#xff08;Schema&#xff09;管理機制。我們可以從幾個方面來理解&#xff1a; 一、什么是 Schema&#xff08;模式&#xff09;&#xff1f; 在 SQL 中&#xff0c;Schema 是數據庫對象&#xff08;如表、視圖等&#xff09;的…

分布式光伏發電多合一(也稱為五合一或者群調群控)終端,從功能、市場前景等等方面介紹

對于當下分布式光伏從業者&#xff0c;多合一終端經常被提及到。而且很多地區也有正常使用&#xff0c;目前來看&#xff0c;使用效果也是比較好的&#xff0c;滿足當下的使用要求。并且價格也是可以接受。下面從幾個方面簡單介紹一下。功能介紹 5G通信功能 設備內置 5G通信模組…

AWE2026啟動:加碼AI科技,雙展區聯動開啟產業新格局

7月22日&#xff0c;由中國家用電器協會主辦的2026年中國家電及消費電子博覽會&#xff08;AWE2026&#xff09;啟動發布會在上海舉行。據「TMT星球」了解&#xff0c;AWE2026將以“AI科技、慧享未來”為主題&#xff0c;首次啟用“一展雙區”的新模式&#xff0c;于2026年3月1…

Django基礎(六)———數據庫

前言上篇文章給大家介紹了DTL模板結構這篇文章將講述Django框架與MySQL數據庫的綜合使用一、Django配置連接數據庫在操作數據庫之前&#xff0c;首先先要連接數據庫&#xff0c;這里我們以配置MySQL為例來講解。Diango連接數據庫&#xff0c;不需要單獨的創建一個連接對象。 只…

postgresql使用記錄 SCRAM authentication requires libpq version 10 or above

文章目錄 背景 如何用命令行連接數據庫 報錯 原因 解決方案 psql常見命令 ?? **核心數據庫操作命令** 1. **查看所有數據庫** 2. **切換數據庫** 3. **查看表及結構** 4. **執行 SQL 文件** 5. **退出 psql** ?? **高級管理命令** ? **注意事項** 背景 由于某種原因,無法…

2.0版本seata、nacos+ruoyi(微服務)配置

一、下載&#xff1a; seata下載&#xff1a;點擊這里 nacos下載&#xff1a;點擊這里 ruoyi&#xff08;微服務&#xff09;下載&#xff1a;點擊這里 Git bash下載&#xff1a;點擊這里 本文所用的版本&#xff1a; seata-2.2.0&#xff08;下圖紅色框框&#xff09;&a…

面試高頻題 力扣 LCR 130.衣柜整理 洪水灌溉(FloodFill) 深度優先遍歷(dfs) 暴力搜索 C++解題思路 每日一題

目錄零、題目描述一、為什么這道題值得一看&#xff1f;二、題目拆解&#xff1a;核心要素與約束三、算法實現&#xff1a;基于 DFS 的解決方案代碼邏輯拆解五、時間復雜度與空間復雜度時間復雜度空間復雜度六、坑點總結七、舉一反三八、洪水灌溉&#xff08;Flood Fill&#x…

Ext4文件系統全景解析

目錄Ext4文件系統全景解析&#xff1a;從inode到數據恢復實戰1. Ext文件系統的"小區規劃"&#xff1a;塊組結構詳解 &#x1f3d8;?1.1 塊組&#xff1a;文件系統的基本管理單元1.2 超級塊的"多重備份"機制 &#x1f6e1;?2. inode&#xff1a;文件的&qu…

貪心算法Day4學習心得

先來看第一道&#xff1a;860. 檸檬水找零 - 力扣&#xff08;LeetCode&#xff09; 有如下三種情況&#xff1a; 情況一&#xff1a;賬單是5&#xff0c;直接收下。情況二&#xff1a;賬單是10&#xff0c;消耗一個5&#xff0c;增加一個10情況三&#xff1a;賬單是20&#…

接口自動化測試種涉及到接口依賴怎么辦?

《接口自動化測試中接口依賴的處理方式及選擇原則》在接口自動化測試中&#xff0c;接口依賴是指某個接口的請求參數、執行條件或測試結果依賴于其他接口的輸出&#xff08;如返回數據、狀態等&#xff09;。處理接口依賴是確保測試用例準確性和穩定性的關鍵&#xff0c;常見的…