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

SpringBoot+MyBatis實(shí)現(xiàn)數(shù)據(jù)庫(kù)字段級(jí)加密

 更新時(shí)間:2025年11月17日 08:17:57   作者:風(fēng)象南  
在數(shù)據(jù)安全越來(lái)越受重視的今天,如何保護(hù)用戶的敏感信息成為每個(gè)開(kāi)發(fā)者都要面對(duì)的問(wèn)題,本文將分享一個(gè)基于注解的自動(dòng)加解密方案,感興趣的小伙伴可以了解下

前言

在數(shù)據(jù)安全越來(lái)越受重視的今天,如何保護(hù)用戶的敏感信息成為每個(gè)開(kāi)發(fā)者都要面對(duì)的問(wèn)題。比如用戶的手機(jī)號(hào)、身份證、銀行卡這些信息,如果直接存在數(shù)據(jù)庫(kù)里,一旦數(shù)據(jù)泄露,后果很嚴(yán)重。

傳統(tǒng)的做法是在每個(gè)查詢和插入的地方手動(dòng)加解密,但這樣做代碼會(huì)變得很亂,而且容易遺漏。今天分享一個(gè)基于注解的自動(dòng)加解密方案,通過(guò) Spring Boot + MyBatis 實(shí)現(xiàn),讓敏感字段自動(dòng)加密存儲(chǔ),自動(dòng)解密使用。

遇到的實(shí)際問(wèn)題

傳統(tǒng)加密方式的問(wèn)題

代碼重復(fù)太多

// 每個(gè)查詢都要手動(dòng)處理
User user = userMapper.findById(id);
user.setPhone(decrypt(user.getPhone()));
user.setEmail(decrypt(user.getEmail()));
user.setIdCard(decrypt(user.getIdCard()));
return user;

// 插入時(shí)也要手動(dòng)加密
User newUser = new User();
newUser.setPhone(encrypt(phone));
newUser.setEmail(encrypt(email));
userMapper.insert(newUser);

修改很麻煩

  • 新增加密字段要改很多地方
  • 容易忘記某個(gè)查詢的加解密
  • 代碼到處都是加解密邏輯

維護(hù)成本高

  • 加密邏輯分散在各個(gè)方法里
  • 出了問(wèn)題很難排查
  • 新人接手要理解整套加密邏輯

我們需要什么樣的方案

理想中的方案應(yīng)該是:

  • 在需要加密的字段上加個(gè)注解就行
  • 加解密過(guò)程自動(dòng)完成
  • 業(yè)務(wù)代碼不用關(guān)心加密邏輯
  • 性能要好,不能影響正常業(yè)務(wù)

解決方案設(shè)計(jì)

核心思路

1. 注解標(biāo)記:用 @Encrypted 注解標(biāo)記要加密的字段

2. 攔截處理:MyBatis 攔截器自動(dòng)處理加解密

3. 透明操作:業(yè)務(wù)代碼感覺(jué)不到加密的存在

技術(shù)架構(gòu)

業(yè)務(wù)代碼 → MyBatis Mapper → 攔截器 → 自動(dòng)加解密 → 數(shù)據(jù)庫(kù)

簡(jiǎn)單來(lái)說(shuō),就是在業(yè)務(wù)層和數(shù)據(jù)庫(kù)之間加了一層透明的加解密處理。

核心實(shí)現(xiàn)

1. 定義注解

先定義一個(gè)簡(jiǎn)單的注解來(lái)標(biāo)記需要加密的字段:

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Encrypted {
    // 是否支持模糊查詢
    boolean supportFuzzyQuery() default false;
}

使用方式:

public class User {
    private Long id;
    private String username;

    @Encrypted  // 這個(gè)字段會(huì)自動(dòng)加密
    private String phone;

    @Encrypted  // 這個(gè)字段也會(huì)自動(dòng)加密
    private String email;
}

2. 加密工具

使用 AES-GCM 算法進(jìn)行加密:

public class CryptoUtil {
    private static final String ALGORITHM = "AES/GCM/NoPadding";
    private static final int IV_LENGTH = 12;

    public static String encrypt(String plaintext) {
        // 生成隨機(jī)IV
        byte[] iv = new byte[IV_LENGTH];
        new SecureRandom().nextBytes(iv);

        // 加密
        Cipher cipher = Cipher.getInstance(ALGORITHM);
        cipher.init(Cipher.ENCRYPT_MODE, secretKey, new GCMParameterSpec(128, iv));
        byte[] ciphertext = cipher.doFinal(plaintext.getBytes());

        // 組合IV和密文,Base64編碼
        byte[] encryptedData = new byte[iv.length + ciphertext.length];
        System.arraycopy(iv, 0, encryptedData, 0, iv.length);
        System.arraycopy(ciphertext, 0, encryptedData, iv.length, ciphertext.length);

        return Base64.getEncoder().encodeToString(encryptedData);
    }

    public static String decrypt(String encryptedText) {
        // Base64解碼
        byte[] encryptedData = Base64.getDecoder().decode(encryptedText);

        // 提取IV和密文
        byte[] iv = Arrays.copyOfRange(encryptedData, 0, IV_LENGTH);
        byte[] ciphertext = Arrays.copyOfRange(encryptedData, IV_LENGTH, encryptedData.length);

        // 解密
        Cipher cipher = Cipher.getInstance(ALGORITHM);
        cipher.init(Cipher.DECRYPT_MODE, secretKey, new GCMParameterSpec(128, iv));
        byte[] plaintext = cipher.doFinal(ciphertext);

        return new String(plaintext);
    }

    // 檢查是否已經(jīng)加密,避免重復(fù)加密
    public static boolean isEncrypted(String value) {
        return value != null && value.contains(":");
    }
}

加密后的格式:Base64(IV):Base64(密文)

3. MyBatis 攔截器

攔截器是整個(gè)方案的核心,負(fù)責(zé)自動(dòng)加解密:

@Intercepts({
    @Signature(type = Executor.class, method = "update", args = {MappedStatement.class, Object.class}),
    @Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class})
})
public class EncryptionInterceptor implements Interceptor {

    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        String methodName = invocation.getMethod().getName();

        // UPDATE/INSERT 操作:加密輸入?yún)?shù)
        if ("update".equals(methodName)) {
            Object parameter = getParameter(invocation);
            if (shouldEncrypt(parameter)) {
                encryptFields(parameter);
            }
        }

        // 執(zhí)行原始SQL
        Object result = invocation.proceed();

        // SELECT 操作:解密查詢結(jié)果
        if ("query".equals(methodName)) {
            decryptResult(result);
        }

        return result;
    }

    private void encryptFields(Object obj) {
        if (obj == null) return;

        // 只處理實(shí)體對(duì)象,不處理基本類型和Map
        if (isBasicType(obj.getClass()) || obj instanceof Map || obj instanceof Collection) {
            return;
        }

        Class<?> clazz = obj.getClass();
        Field[] fields = clazz.getDeclaredFields();

        for (Field field : fields) {
            if (field.isAnnotationPresent(Encrypted.class)) {
                try {
                    field.setAccessible(true);
                    Object value = field.get(obj);

                    if (value instanceof String && !isEncrypted((String) value)) {
                        String encrypted = CryptoUtil.encrypt((String) value);
                        field.set(obj, encrypted);
                    }
                } catch (Exception e) {
                    log.error("加密字段失敗: {}", field.getName(), e);
                }
            }
        }
    }

    private void decryptResult(Object result) {
        if (result instanceof List) {
            for (Object item : (List<?>) result) {
                decryptFields(item);
            }
        } else if (result != null) {
            decryptFields(result);
        }
    }

    private void decryptFields(Object obj) {
        // 解密邏輯和加密類似,但是反向操作
        Class<?> clazz = obj.getClass();
        Field[] fields = clazz.getDeclaredFields();

        for (Field field : fields) {
            if (field.isAnnotationPresent(Encrypted.class)) {
                try {
                    field.setAccessible(true);
                    Object value = field.get(obj);

                    if (value instanceof String && isEncrypted((String) value)) {
                        String decrypted = CryptoUtil.decrypt((String) value);
                        field.set(obj, decrypted);
                    }
                } catch (Exception e) {
                    log.error("解密字段失敗: {}", field.getName(), e);
                }
            }
        }
    }
}

4. 自動(dòng)配置

配置攔截器自動(dòng)生效:

@Configuration
@ConditionalOnProperty(name = "encryption.enabled", havingValue = "true", matchIfMissing = true)
public class EncryptionAutoConfiguration {

    @Bean
    public ConfigurationCustomizer encryptionConfigurationCustomizer() {
        return configuration -> {
            configuration.addInterceptor(new EncryptionInterceptor());
        };
    }
}

只需要在配置文件中啟用:

encryption:
  enabled: true

使用效果

數(shù)據(jù)庫(kù)存儲(chǔ)情況

原始數(shù)據(jù)

用戶信息:
姓名: 張三
手機(jī): 13812345678
郵箱: zhangsan@example.com
身份證: 110101199001011234

數(shù)據(jù)庫(kù)存儲(chǔ)(自動(dòng)加密后):

用戶信息:
姓名: 張三
手機(jī): nTuVgMWime1:hFGa9as6JHxLT2vG8dpiRmu4wtxDnkTEr/1x
郵箱: mK7pL9xQ2rS8vN3w:jKxL9mN2pQ7rS8vT3wX4yZ6aB8cD1eF2g
身份證: X1Y2Z3A4B5C6D7E8:F9G0H1I2J3K4L5M6N7O8P9Q0R1S2T3U4V

業(yè)務(wù)代碼使用

@Service
public class UserService {

    @Autowired
    private UserMapper userMapper;

    // 插入用戶:自動(dòng)加密存儲(chǔ)
    public void createUser(User user) {
        // 這些字段會(huì)被攔截器自動(dòng)加密
        user.setPhone("13812345678");
        user.setEmail("zhangsan@example.com");
        userMapper.insert(user);
    }

    // 查詢用戶:自動(dòng)解密返回
    public User getUser(Long id) {
        User user = userMapper.findById(id);
        // 這些字段已經(jīng)被攔截器自動(dòng)解密
        System.out.println(user.getPhone());  // 13812345678
        System.out.println(user.getEmail());  // zhangsan@example.com
        return user;
    }
}

可以看到,業(yè)務(wù)代碼完全不用關(guān)心加密和解密的過(guò)程,一切都在幕后自動(dòng)完成。

安全考慮

1. 密鑰管理

實(shí)際項(xiàng)目中不要把密鑰寫(xiě)死在代碼里:

@Configuration
public class EncryptionConfig {

    @Value("${encryption.key}")
    private String encryptionKey;

    @Bean
    public SecretKey getSecretKey() {
        // 可以從環(huán)境變量、配置中心、密鑰管理系統(tǒng)獲取
        byte[] keyBytes = Base64.getDecoder().decode(encryptionKey);
        return new SecretKeySpec(keyBytes, "AES");
    }
}

2. 日志安全

避免在日志中打印敏感信息:

public class SensitiveDataLogger {

    public String maskPhone(String phone) {
        if (phone == null || phone.length() != 11) return phone;
        return phone.substring(0, 3) + "****" + phone.substring(7);
    }

    public String maskEmail(String email) {
        if (email == null) return email;
        int atIndex = email.indexOf("@");
        if (atIndex <= 2) return "***" + email.substring(atIndex);
        return email.substring(0, 2) + "***" + email.substring(atIndex);
    }
}

總結(jié)

這個(gè)基于注解的字段級(jí)加密方案有以下幾個(gè)優(yōu)點(diǎn):

使用簡(jiǎn)單:只需要在字段上加個(gè)注解就行

代碼干凈:業(yè)務(wù)代碼不用關(guān)心加解密邏輯

安全可靠:使用標(biāo)準(zhǔn)的加密算法

容易維護(hù):所有加密邏輯集中管理

適用場(chǎng)景

  • 用戶管理系統(tǒng)
  • 支付系統(tǒng)
  • 醫(yī)療信息系統(tǒng)
  • 任何需要保護(hù)敏感數(shù)據(jù)的系統(tǒng)

不適用場(chǎng)景

  • 對(duì)性能要求極高的系統(tǒng)
  • 需要對(duì)加密字段進(jìn)行復(fù)雜查詢的場(chǎng)景
  • 數(shù)據(jù)量特別大的系統(tǒng)

如果你也有保護(hù)敏感數(shù)據(jù)的需求,這個(gè)方案值得考慮。代碼量不大,但效果很明顯。

到此這篇關(guān)于SpringBoot+MyBatis實(shí)現(xiàn)數(shù)據(jù)庫(kù)字段級(jí)加密的文章就介紹到這了,更多相關(guān)SpringBoot MyBatis數(shù)據(jù)庫(kù)字段加密內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • IDEA錯(cuò)誤:找不到或無(wú)法加載主類的完美解決方法

    IDEA錯(cuò)誤:找不到或無(wú)法加載主類的完美解決方法

    使用IDEA開(kāi)始就一直在搭建java環(huán)境,許久沒(méi)有使用過(guò)java,剛開(kāi)始有些生疏,先建了一個(gè)最簡(jiǎn)單的類可是運(yùn)行的時(shí)候出現(xiàn)錯(cuò)誤:找不到或無(wú)法加載主類,下面這篇文章主要給大家介紹了關(guān)于IDEA錯(cuò)誤:找不到或無(wú)法加載主類的完美解決方法,需要的朋友可以參考下
    2022-07-07
  • SpringBoot+Spring Security+JWT實(shí)現(xiàn)RESTful Api權(quán)限控制的方法

    SpringBoot+Spring Security+JWT實(shí)現(xiàn)RESTful Api權(quán)限控制的方法

    這篇文章主要介紹了SpringBoot+Spring Security+JWT實(shí)現(xiàn)RESTful Api權(quán)限控制的方法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2019-03-03
  • Java數(shù)據(jù)溢出代碼詳解

    Java數(shù)據(jù)溢出代碼詳解

    這篇文章主要介紹了Java數(shù)據(jù)溢出的相關(guān)內(nèi)容,包括具體代碼示例,分析比較詳細(xì),希望對(duì)大家有所幫助,感興趣的朋友可以參考下。
    2017-09-09
  • Java 數(shù)據(jù)結(jié)構(gòu)鏈表操作實(shí)現(xiàn)代碼

    Java 數(shù)據(jù)結(jié)構(gòu)鏈表操作實(shí)現(xiàn)代碼

    這篇文章主要介紹了Java 數(shù)據(jù)結(jié)構(gòu)鏈表操作的相關(guān)資料,并附實(shí)例代碼,需要的朋友可以參考下
    2016-10-10
  • SpringBoot+Mybatis使用Enum枚舉類型總是報(bào)錯(cuò)No enum constant XX問(wèn)題

    SpringBoot+Mybatis使用Enum枚舉類型總是報(bào)錯(cuò)No enum constant&n

    這篇文章主要介紹了SpringBoot+Mybatis使用Enum枚舉類型總是報(bào)錯(cuò)No enum constant XX問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-12-12
  • 關(guān)于springboot使用rocketmq?RocketMQMessageListener參數(shù)問(wèn)題

    關(guān)于springboot使用rocketmq?RocketMQMessageListener參數(shù)問(wèn)題

    這篇文章主要介紹了springboot使用rocketmq?RocketMQMessageListener參數(shù)問(wèn)題,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值需要的朋友可以參考下
    2022-11-11
  • 深入解析Spring Bean初始化時(shí)和銷毀時(shí)的擴(kuò)展點(diǎn)

    深入解析Spring Bean初始化時(shí)和銷毀時(shí)的擴(kuò)展點(diǎn)

    在Bean進(jìn)行初始化或者銷毀的時(shí)候,如果我們需要做一些操作,比如加載和銷毀一些資源或者執(zhí)行一些方法時(shí),那么就可以使用Spring提供的一些擴(kuò)展,今天主要分享初始化Bean時(shí)的三種方式和銷毀Bean時(shí)的三種方式,需要的朋友可以參考下
    2023-08-08
  • java基于poi-ooxml編寫(xiě)Excel導(dǎo)出的工具類

    java基于poi-ooxml編寫(xiě)Excel導(dǎo)出的工具類

    這篇文章主要為大家詳細(xì)介紹了java如何基于poi-ooxml編寫(xiě)一個(gè)Excel導(dǎo)出功能的工具類,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下
    2025-08-08
  • Java 8 新特性終極版指南詳解

    Java 8 新特性終極版指南詳解

    Java 8已經(jīng)公布有一段時(shí)間了,種種跡象表明Java 8是一個(gè)有重大改變的發(fā)行版。本文給大家介紹Java 8 新特性終極版指南詳解,需要的朋友參考下
    2016-03-03
  • Java中防止數(shù)據(jù)重復(fù)提交超簡(jiǎn)單的6種方法

    Java中防止數(shù)據(jù)重復(fù)提交超簡(jiǎn)單的6種方法

    在平時(shí)開(kāi)發(fā)中,如果網(wǎng)速比較慢的情況下,用戶提交表單后,發(fā)現(xiàn)服務(wù)器半天都沒(méi)有響應(yīng),那么用戶可能會(huì)以為是自己沒(méi)有提交表單,就會(huì)再點(diǎn)擊提交按鈕重復(fù)提交表單,這篇文章主要給大家介紹了關(guān)于Java中防止數(shù)據(jù)重復(fù)提交超簡(jiǎn)單的6種方法,需要的朋友可以參考下
    2021-11-11

最新評(píng)論

兴城市| 深圳市| 丹凤县| 通许县| 永寿县| 报价| 阳谷县| 嘉黎县| 大悟县| 大名县| 庆云县| 徐汇区| 德令哈市| 道孚县| 于都县| 永胜县| 台安县| 绍兴县| 临清市| 柯坪县| 洞头县| 凤阳县| 工布江达县| 黔江区| 老河口市| 昌都县| 秭归县| 平乐县| 山东| 绥德县| 岱山县| 聂荣县| 张家口市| 璧山县| 孟津县| 峨眉山市| 云浮市| 阜南县| 武汉市| 马边| 山阳县|