MyBatis-Plus 優雅實現數據加密存儲

文章目錄

  • 前言
  • 一、數據庫字段加解密實現
    • 1. 定義加密類型枚舉
    • 2. 定義AES密鑰和偏移量
    • 3. 配置定義使用的加密類型
    • 4. 加密解密接口
    • 5. 解密解密異常類
    • 6. 加密解密實現類
      • 6.1 AES加密解密實現類
      • 6.2 Base64加密解密實現類
    • 7. 實現數據庫的字段保存加密與查詢解密處理類
    • 8. MybatisPlus配置類
  • 二、數據庫字段加密解密的使用
    • 1. 創建實體類
    • 2. 數據保存示例
    • 3. 數據查詢示例


前言

本文將基于Mybatis-Plus講述如何在數據的源頭存儲層保障其安全。我們都知道一些核心私密字段,比如說密碼,手機號等在數據庫層存儲就不能明文存儲,必須加密存儲保證即使數據庫泄露了也不會輕易曝光數據。


本文實現效果參考 plasticene-boot-starter-parent,更多信息在下面鏈接。

Github地址:https://github.com/plasticene/plasticene-boot-starter-parent

Gitee地址:https://gitee.com/plasticene3/plasticene-boot-starter-parent

當然也可以參考 mybatis-mate 為 mp 企業級模塊,旨在更敏捷優雅處理數據。

Gitee地址:https://gitee.com/baomidou/mybatis-mate-examples

一、數據庫字段加解密實現

1. 定義加密類型枚舉

默認提供基于base64和AES加密算法,當然也可以自定義加密算法。

public enum Algorithm {BASE64,AES
}

2. 定義AES密鑰和偏移量

@Data
@ConfigurationProperties(prefix = "ptc.encrypt")
public class EncryptProperties {/*** 加密算法 {@link Algorithm}*/private Algorithm algorithm = Algorithm.BASE64;/*** aes算法需要秘鑰key*/private String key = "8iUJAD805IHO2vog";/*** aes算法需要一個偏移量* AES算法的偏移量長度必須為16字節(128位)*/private String iv = "cUTd1U+yxk8Dl6Cg";}

AES的密鑰和偏移量的生成可訪問:AES 密鑰在線生成器

若使用RSA,密鑰對的生成可訪問:在線生成非對稱加密公鑰私鑰對

3. 配置定義使用的加密類型

這里我們使用aes加密算法:

  • application.yml
ptc:encrypt:algorithm: aes

4. 加密解密接口

public interface EncryptService {/*** 加密算法* @param content* @return*/String encrypt(String content);/*** 解密算法* @param content* @return*/String decrypt(String content);}

5. 解密解密異常類

  • BizException.java
/*** 業務異常類*/
@Data
public class BizException extends RuntimeException {private Integer code;public BizException() {super();}public BizException(String message) {super(message);}public BizException(Integer code, String message) {super(message);this.code = code;}}

6. 加密解密實現類

6.1 AES加密解密實現類

@Slf4j
public class AESEncryptService implements EncryptService {@Resourceprivate EncryptProperties encryptProperties;@Overridepublic String encrypt(String content) {try {SecretKeySpec secretKey = new SecretKeySpec(encryptProperties.getKey().getBytes(StandardCharsets.UTF_8), Constants.AES);byte[] enCodeFormat = secretKey.getEncoded();SecretKeySpec secretKeySpec = new SecretKeySpec(enCodeFormat, Constants.AES);IvParameterSpec iv = new IvParameterSpec(encryptProperties.getIv().getBytes(StandardCharsets.UTF_8));Cipher cipher = Cipher.getInstance(Constants.AES_CBC_CIPHER);cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, iv);byte[] valueByte = cipher.doFinal(content.getBytes(StandardCharsets.UTF_8));return Base64.getEncoder().encodeToString(valueByte);} catch (Exception e) {log.error("加密失敗:", e);throw new BizException("加密失敗");}}@Overridepublic String decrypt(String content) {try {byte[] originalData = Base64.getDecoder().decode(content.getBytes(StandardCharsets.UTF_8));SecretKeySpec secretKey = new SecretKeySpec(encryptProperties.getKey().getBytes(StandardCharsets.UTF_8), Constants.AES);byte[] enCodeFormat = secretKey.getEncoded();SecretKeySpec secretKeySpec = new SecretKeySpec(enCodeFormat, Constants.AES);IvParameterSpec iv = new IvParameterSpec(encryptProperties.getIv().getBytes(StandardCharsets.UTF_8));Cipher cipher = Cipher.getInstance(Constants.AES_CBC_CIPHER);cipher.init(Cipher.DECRYPT_MODE, secretKeySpec, iv);byte[] valueByte = cipher.doFinal(originalData);return new String(valueByte);} catch (Exception e) {log.error("解密失敗:", e);throw new BizException("解密失敗");}}
}

6.2 Base64加密解密實現類

public class Base64EncryptService implements EncryptService {@Overridepublic String encrypt(String content) {try {return Base64.getEncoder().encodeToString(content.getBytes(StandardCharsets.UTF_8));} catch (Exception e) {throw new RuntimeException("encrypt fail!", e);}}@Overridepublic String decrypt(String content) {try {byte[] asBytes = Base64.getDecoder().decode(content);return new String(asBytes, StandardCharsets.UTF_8);} catch (Exception e) {throw new RuntimeException("decrypt fail!", e);}}
}

7. 實現數據庫的字段保存加密與查詢解密處理類

接下來就可以基于加密算法,擴展 MyBatis 的 BaseTypeHandler 對實體字段數據進行加密解密

public class EncryptTypeHandler<T> extends BaseTypeHandler<T> {@Resourceprivate EncryptService encryptService;@Overridepublic void setNonNullParameter(PreparedStatement ps, int i, Object parameter, JdbcType jdbcType) throws SQLException {ps.setString(i, encryptService.encrypt((String)parameter));}@Overridepublic T getNullableResult(ResultSet rs, String columnName) throws SQLException {String columnValue = rs.getString(columnName);//有一些可能是空字符return StrUtil.isBlank(columnValue) ? (T)columnValue : (T)encryptService.decrypt(columnValue);}@Overridepublic T getNullableResult(ResultSet rs, int columnIndex) throws SQLException {String columnValue = rs.getString(columnIndex);return StrUtil.isBlank(columnValue) ? (T)columnValue : (T)encryptService.decrypt(columnValue);}@Overridepublic T getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {String columnValue = cs.getString(columnIndex);return StrUtil.isBlank(columnValue) ? (T)columnValue : (T)encryptService.decrypt(columnValue);}
}

8. MybatisPlus配置類

把EncryptService實現類注入到容器中

@Configuration
@MapperScan("com.chh.mapper")
@EnableConfigurationProperties({EncryptProperties.class})
public class MybatisPlusConfig {@Resourceprivate EncryptProperties encryptProperties;// 分頁插件@Beanpublic MybatisPlusInterceptor mybatisPlusInterceptor() {MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.POSTGRE_SQL));return interceptor;}@Beanpublic EncryptTypeHandler encryptTypeHandler() {return new EncryptTypeHandler();}@Bean@ConditionalOnMissingBean(EncryptService.class)public EncryptService encryptService() {Algorithm algorithm = encryptProperties.getAlgorithm();EncryptService encryptService;switch (algorithm) {case BASE64:encryptService = new Base64EncryptService();break;case AES:encryptService = new AESEncryptService();break;default:encryptService = null;}return encryptService;}}

二、數據庫字段加密解密的使用

1. 創建實體類

  • 在實體類上加上 @TableName(value = "表名", autoResultMap = true)
  • 在需要加密的屬性上加上 @TableField(value = "字段", typeHandler = EncryptTypeHandler.class)
@Data
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
@TableName(value = "app_account_login", autoResultMap = true)
public class AppAccountLogin implements Serializable {private static final long serialVersionUID = 1L;/*** 主鍵*/@TableId(value = "id", type = IdType.AUTO)private Integer id;/*** 名稱*/@TableField("name")private String name;/*** 登錄賬號*/@TableField("login_account")private String loginAccount;/*** 登錄密碼*/@TableField(value = "login_password", typeHandler = EncryptTypeHandler.class)private String loginPassword;/*** 激活狀態*/@TableField("enabled")private Boolean enabled;/*** 創建時間*/@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")@TableField(fill = FieldFill.INSERT)private Date createTime;}

2. 數據保存示例

AppAccountLogin appAccountLogin = new AppAccountLogin();
appAccountLogin.setName("test");
appAccountLogin.setLoginAccount("123456789");
appAccountLogin.setLoginPassword("abc123456");
appAccountLoginService.save(appAccountLogin);

保存結果:

請添加圖片描述

3. 數據查詢示例

System.out.println(appAccountLoginService.getById(4));

查詢結果:

AppAccountLogin(id=4, name=test, loginAccount=123456789, loginPassword=abc123456, enabled=true, createTime=Fri Feb 02 10:02:41 CST 2024)

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

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

相關文章

使用python進行量化交易

yfinance yfinance國內不能使用&#xff0c;可以使用tushare、akshare代替 import yfinance as yf# 輸入股票代碼 stock_symbol AAPL # 替換為你想要查詢的股票代碼# 獲取股票數據 data yf.download(stock_symbol)# 打印實時數據 print(data)pip install akshare import …

Selenium安裝與配置

文章目錄 一、selenium安裝1. Python環境準備&#xff1a;2. 安裝Selenium&#xff1a;3. 瀏覽器驅動安裝&#xff1a;4. 驗證安裝&#xff1a; 二、常見問題1. Selenium版本與瀏覽器驅動程序不兼容&#xff1a;2. 瀏覽器驅動程序路徑未正確設置&#xff1a; Selenium是一個用于…

2024年1月手機市場行業分析:蘋果手機份額驟降,國產高端手機成功逆襲!

小米Ultra發布。 一方面&#xff0c;我們有望看到國產手機再一次超越自己的決心&#xff0c;繼續創新追逐高端&#xff1b;另一方面&#xff0c;我們也不得不正視目前手機市場所面臨的危機狀態。 2024年1月的線上手機市場遠不如去年。根據鯨參謀數據顯示&#xff0c;今年1月京…

Qt(C++)面試題 | 精選25項常問

面試是每個求職者都必須經歷的一關,而QT面試更是需要面試者有深厚的編程基礎和豐富的實戰經驗。下面我們為大家整理了25道QT面試題,希望能夠幫助大家在求職路上獲得成功。 ?Qt 中常用的五大模塊是哪些? Qt 中常用的五大模塊包括: QtCore:提供了 Qt 的核心功能,例如基本的…

Java面試題之分布式/微服務篇

經濟依舊不景氣啊&#xff0c;如此大環境下Java還是這么卷&#xff0c;又是一年一次的金三銀四。 兄弟們&#xff0c;你準備好了嗎&#xff1f;沖沖沖&#xff01;歐里給&#xff01; 分布式/微服務相關面試題解 題一&#xff1a;CAP理論&#xff0c;BASE理論題二&#xff1a;…

深度神經網絡

包括&#xff1a;深度前饋神經網絡、深度卷積神經網絡、深度循環神經網絡 深度神經網絡全面概述&#xff1a;從基本概念到實際模型和硬件基礎-騰訊云開發者社區-騰訊云

MQL語言實現JSON協議庫

文章目錄 一、MQL語言實現JSON協議的意義二、定義JSON數據枚舉類型簡單數據類型復雜數據類型枚舉數據類型定義類變量清理與賦值方法構造與析構方法重載運算符添加與設置方法序列化與反序列方法 一、MQL語言實現JSON協議的意義 數據交互&#xff1a;JSON是一種輕量級的數據交換格…

【2024軟件測試面試必會技能】Postman(1): postman的介紹和安裝

Postman的介紹 Postman 是一款谷歌開發的接口測試工具,使API的調試與測試更加便捷。 它提供功能強大的 Web API & HTTP 請求調試。它能夠發送任何類型的HTTP 請求 (GET, HEAD, POST, PUT..)&#xff0c;附帶任何數量的參數 headers。 postman是一款支持http協議的接口調試…

【PTA|函數題|期末復習】指針

目錄 6-1 計算兩數的和與差&#xff08;5分&#xff09; 函數接口定義&#xff1a; 裁判測試程序樣例&#xff1a; 輸入樣例&#xff1a; 輸出樣例&#xff1a; 代碼 6-2 拆分實數的整數與小數部分 (5分) 函數接口定義&#xff1a; 裁判測試程序樣例&#xff1a; 輸入…

springboot整合mybatisPlus超級詳細

springboot整合mybatis-plus超級詳細 一、環境二、springboot整合myBatisPlus2.1新建2.2 添加Mybatis-plus和mysql依賴2.3 修改配置文件2.4 新建包和文件2.5 新建表2.6 創建實體類2.7 創建Mapper接口2.8 創建Service接口2.9 創建Service實現類2.10 增刪改查 MyBatis-Plus&#…

C# Onnx 使用onnxruntime部署實時視頻幀插值

目錄 介紹 效果 模型信息 項目 代碼 下載 C# Onnx 使用onnxruntime部署實時視頻幀插值 介紹 github地址&#xff1a;https://github.com/google-research/frame-interpolation FILM: Frame Interpolation for Large Motion, In ECCV 2022. The official Tensorflow 2…

四.QT5工具安裝和環境變量的配置

1.以管理員身份運行安裝包 2.登錄qt賬號&#xff0c;點擊【next】 3.選中同意 4.選擇安裝目錄&#xff0c;注意不能有中文和空格 5.勾選 64位 mingw。點擊【next】&#xff0c;等待安裝完成 6.配置環境變量

[07] 組件注冊

目錄 Vue.js 組件局部注冊全局注冊 Vue.js 組件 組件&#xff08;Component&#xff09;是 Vue.js 最強大的功能之一。 組件可以擴展 HTML 元素&#xff0c;封裝可重用的代碼。 組件系統讓我們可以用獨立可復用的小組件來構建大型應用&#xff0c;幾乎任意類型的應用的界面都…

為什么很多人選用QT開發,有哪些應用實例?

在軟件開發領域&#xff0c;Qt框架作為一種跨平臺的C應用程序開發框架&#xff0c;近年來受到越來越多開發者的青睞。這主要得益于其卓越的跨平臺性能、豐富的功能庫、開發效率以及社區支持。以下將通過詳實的分析&#xff0c;從不同角度探討為什么很多人改用QT開發&#xff0c…

低代碼開發:學校低成本數字化轉型的新引擎

隨著科技的飛速發展&#xff0c;數字化轉型已經成為教育行業的一大趨勢。然而&#xff0c;對于許多學校來說&#xff0c;高昂的數字化改造成本成為了一道難以逾越的門檻。本文將探討如何通過低代碼開發&#xff0c;以低成本實現學校數字化轉型&#xff0c;為教育行業注入新活力…

力扣熱題100_子串_560_和為 K 的子數組

文章目錄 題目鏈接解題思路解題代碼 題目鏈接 560.和為 K 的子數組 解題思路 1.定義變量count&#xff08;計算個數&#xff0c;返回值&#xff09;為0、n&#xff08;當前nums長度&#xff09;、preSums&#xff08;利用利用defaultdict的特性&#xff0c;當presum-k不存在…

list.stream().forEach()和list.forEach()的區別

list.stream().forEach() 和 list.forEach() 在 Java 中都是用于遍歷集合元素的方法&#xff0c;但它們在使用場景和功能上有所不同&#xff1a; list.forEach()&#xff1a; 是從 Java 8 開始引入到 java.util.List 接口的標準方法。直接對列表進行迭代&#xff0c;它采用內部…

力扣645. 錯誤的集合(排序,哈希表)

Problem: 645. 錯誤的集合 文章目錄 題目描述思路復雜度Code 題目描述 思路 1.排序 1.對nums數組按從小到大的順序排序; 2.遍歷數組時若判斷兩個相鄰的元素則找到重復元素&#xff1b; 3.記錄一個整形變量prev一次置換當前位置元素并與其作差&#xff0c;若差等于2著說明缺失的…

Mysql索引操作

1、索引語法 2、慢查詢日志 慢查詢日志記錄了所有執行時間超過指定參數&#xff08; long_query_time &#xff0c;單位&#xff1a;秒&#xff0c;默認 10 秒&#xff09;的所有 SQL 語句的日志。 MySQL 的慢查詢日志默認沒有開啟&#xff0c;我們可以查看一下系統變量 slo…

MySQL數據庫備份與還原批處理腳本

數據庫備份文件&#xff0c;例如保存為&#xff1a;dump_msyql.bat REM 數據庫備份 echo offREM 激活延遲環境變量擴展&#xff0c;防止變量賦值丟失 setlocal enabledelayedexpansionREM 獲取當天時間 set today%date:~8,2%REM 大前天&#xff0c;當天-3天&#xff0c;小于10…