Spring Boot数据加密实战:AES算法、JPA集成与密钥管理

1. 项目概述

最近在做一个涉及用户隐私数据的后台管理系统,数据加密成了绕不开的坎。无论是用户身份证号、手机号,还是存储在数据库里的敏感配置项,明文存放的风险谁都担不起。Spring Boot作为我们Java后端开发的主流框架,虽然提供了强大的生态,但在数据加密这块,并没有一个“开箱即用”的银弹方案。这意味着我们需要自己动手,把加密能力集成到项目里。

这不仅仅是调用几个API那么简单。你得考虑用哪种加密算法(AES、RSA还是国密SM4?),密钥怎么管理才安全,是全程手动加解密,还是利用JPA或MyBatis的拦截器自动处理,又或者在传输层通过HTTPS解决。不同的场景,比如配置文件加密、数据库字段加密、接口传输加密,对应的技术选型和实现策略也完全不同。搞明白了这些,才能在Spring Boot项目里构建一套既安全又实用的数据保护体系,而不是仅仅在代码里写死一个加密函数。接下来,我就结合自己的踩坑经验,聊聊在Spring Boot里实现数据加密的几种核心思路和落地细节。

2. 加密方案的核心选型与设计思路

在Spring Boot项目里谈加密,第一步不是写代码,而是定方案。选型错了,后面可能事倍功半,甚至引入安全漏洞。

2.1 对称加密 vs. 非对称加密:场景决定选择

这是最根本的抉择。对称加密,比如AES,加密和解密用的是同一把密钥。它的优点是速度快,适合加密大量数据,比如数据库里整个字段的内容。但缺点也明显:密钥分发和管理是难题。如果把密钥硬编码在代码或配置文件中,一旦源码泄露,加密形同虚设。

非对称加密,典型如RSA,有公钥和私钥一对。公钥可以公开,用于加密;私钥自己严格保管,用于解密。这解决了密钥分发问题,常用于HTTPS握手、接口签名验证。但它的计算速度慢,不适合直接加密大段数据。

在实际的Spring Boot后台系统中,我通常采用混合模式: 使用RSA来加密传输一个临时的AES密钥(或称为会话密钥),后续大量的数据加密通信则使用这个AES密钥进行 。对于落库的数据加密,更多采用对称加密,因为加解密方都是我们自己(服务端),密钥管理压力相对可控。

2.2 密钥的生命周期管理:比算法本身更重要

再强的算法,密钥泄露了也是白搭。在Spring Boot项目中,密钥管理有几个层级:

  1. 开发环境/测试环境 :为了方便,可以将密钥放在 application.yml 或环境变量中。但务必与生产环境分离。
  2. 生产环境 :绝对禁止将密钥写在项目配置文件里提交到代码仓库。推荐做法:
    • 环境变量 :通过 SPRING_APPLICATION_JSON -D 参数注入,这是最简单的方式。
    • 配置中心 :结合Spring Cloud Config、Nacos、Apollo等,将加密后的密钥存放在配置中心,应用启动时拉取并解密。
    • 密钥管理服务(KMS) :如阿里云KMS、HashiCorp Vault。这是最专业的方式,密钥由KMS生成和管理,应用通过API动态获取,甚至能做到密钥的自动轮转。对于高安全要求的金融、政务项目,这是必选项。

我的经验是,至少要做到将密钥从代码中剥离,通过环境变量或配置中心注入。在 application.yml 中,可以这样占位:

app:
  crypto:
    aes-key: ${AES_ENCRYPTION_KEY:} # 优先从环境变量AES_ENCRYPTION_KEY读取,为空则留空(启动会报错,提醒配置)
    rsa-private-key: ${RSA_PRIVATE_KEY:}

然后在Java的 @ConfigurationProperties 类中注入这些值。

2.3 加密粒度的考量:全盘加密还是字段加密?

  • 数据库透明加密(TDE) :由数据库或存储层完成,对整个数据文件或表空间加密。对应用完全透明,无需修改代码。适合防止磁盘被盗带来的数据泄露,但无法防止拥有数据库访问权限的人查看明文。
  • 应用层字段加密 :在数据写入数据库前,由应用程序对特定字段进行加密。粒度最细,安全性高,即使DBA也看不到明文。但会牺牲部分查询能力(比如无法直接对加密字段进行 LIKE 搜索或范围查询),并且加解密性能开销需要评估。

在大多数Spring Boot业务系统中,我们讨论的“数据加密”主要指 应用层字段加密 。这是业务逻辑的一部分,需要我们在代码层面精心设计。

3. 基于AES的对称加密实战实现

AES(高级加密标准)是目前最常用的对称加密算法,在Java中通过 javax.crypto 包下的 Cipher SecretKeySpec 等类实现。下面我们构建一个可在Spring Boot中直接使用的工具类。

3.1 核心工具类 CryptoUtils 设计与实现

这个工具类需要提供稳定的加密、解密方法,并处理好异常和不同的工作模式。

import lombok.extern.slf4j.Slf4j;
import org.springframework.util.Base64Utils;
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.security.Key;

/**
 * AES对称加密工具类
 * 模式:AES/ECB/PKCS5Padding (仅用于示例,ECB模式不安全,生产环境请使用CBC或GCM)
 */
@Slf4j
public class AesCryptoUtils {

    // 算法定义
    private static final String ALGORITHM = "AES";
    // 完整的算法/模式/填充 表示。这里以CBC模式为例,更安全。
    private static final String TRANSFORMATION = "AES/CBC/PKCS5Padding";
    // 密钥长度,AES支持128, 192, 256位
    private static final int KEY_SIZE = 128;

    /**
     * 生成一个随机的AES密钥(Base64编码)
     * 实际生产中,密钥应由KMS生成或通过安全流程分发,而非程序随机生成。
     */
    public static String generateRandomKey() throws Exception {
        KeyGenerator keyGen = KeyGenerator.getInstance(ALGORITHM);
        keyGen.init(KEY_SIZE);
        SecretKey secretKey = keyGen.generateKey();
        return Base64Utils.encodeToString(secretKey.getEncoded());
    }

    /**
     * 加密
     * @param plainText 明文
     * @param base64Key Base64编码的密钥
     * @return Base64编码的密文
     */
    public static String encrypt(String plainText, String base64Key) {
        try {
            // 1. 将Base64密钥解码为字节数组,并生成SecretKeySpec
            byte[] keyBytes = Base64Utils.decodeFromString(base64Key);
            // AES密钥长度必须为16(128), 24(192)或32(256)字节。这里需要校验。
            if (keyBytes.length != 16 && keyBytes.length != 24 && keyBytes.length != 32) {
                throw new IllegalArgumentException("Invalid AES key length (must be 16, 24, or 32 bytes)");
            }
            Key key = new SecretKeySpec(keyBytes, ALGORITHM);

            // 2. 初始化Cipher为加密模式
            Cipher cipher = Cipher.getInstance(TRANSFORMATION);
            // CBC模式需要初始化向量IV,这里演示生成一个随机IV并前置到密文中
            SecureRandom secureRandom = new SecureRandom();
            byte[] iv = new byte[16]; // AES块大小是16字节
            secureRandom.nextBytes(iv);
            IvParameterSpec ivSpec = new IvParameterSpec(iv);
            cipher.init(Cipher.ENCRYPT_MODE, key, ivSpec);

            // 3. 执行加密
            byte[] encryptedBytes = cipher.doFinal(plainText.getBytes(StandardCharsets.UTF_8));

            // 4. 将IV和密文一起编码为Base64返回。格式:Base64(IV) + ":" + Base64(CipherText)
            String ivBase64 = Base64Utils.encodeToString(iv);
            String encryptedTextBase64 = Base64Utils.encodeToString(encryptedBytes);
            return ivBase64 + ":" + encryptedTextBase64;

        } catch (Exception e) {
            log.error("AES加密失败", e);
            // 生产环境应考虑抛出特定的业务异常,而非返回null
            return null;
        }
    }

    /**
     * 解密
     * @param encryptedTextWithIv Base64编码的密文(包含IV),格式为 "IV:CipherText"
     * @param base64Key Base64编码的密钥
     * @return 明文
     */
    public static String decrypt(String encryptedTextWithIv, String base64Key) {
        try {
            // 1. 拆分出IV和密文
            String[] parts = encryptedTextWithIv.split(":");
            if (parts.length != 2) {
                throw new IllegalArgumentException("Invalid encrypted text format");
            }
            byte[] iv = Base64Utils.decodeFromString(parts[0]);
            byte[] encryptedBytes = Base64Utils.decodeFromString(parts[1]);

            // 2. 准备密钥
            byte[] keyBytes = Base64Utils.decodeFromString(base64Key);
            Key key = new SecretKeySpec(keyBytes, ALGORITHM);

            // 3. 初始化Cipher为解密模式,使用相同的IV
            Cipher cipher = Cipher.getInstance(TRANSFORMATION);
            IvParameterSpec ivSpec = new IvParameterSpec(iv);
            cipher.init(Cipher.DECRYPT_MODE, key, ivSpec);

            // 4. 执行解密
            byte[] decryptedBytes = cipher.doFinal(encryptedBytes);
            return new String(decryptedBytes, StandardCharsets.UTF_8);

        } catch (Exception e) {
            log.error("AES解密失败", e);
            return null;
        }
    }
}

关键点解析

  1. 模式选择 :示例中使用了 AES/CBC/PKCS5Padding 绝对不要使用ECB模式 ,因为它是不安全的,相同的明文块会产生相同的密文块,容易受到模式分析攻击。CBC模式需要初始化向量IV,且每次加密应使用随机IV。
  2. IV处理 :IV不需要保密,但必须不可预测。通常将其与密文一起存储或传输。示例中将IV和密文用 : 连接并Base64编码。
  3. 密钥校验 :对传入的密钥长度进行了校验,确保符合AES要求。
  4. 异常处理 :加解密可能抛出多种异常(如BadPaddingException, InvalidKeyException)。在工具类中捕获并记录日志,返回 null 或抛出业务异常取决于你的错误处理策略。
  5. 字符编码 :明确指定 UTF-8 ,避免因系统默认编码不同导致加解密结果不一致。

3.2 将工具类集成到Spring Boot服务中

有了工具类,我们需要将其与Spring Boot的IoC容器结合,方便管理和注入密钥。

首先,创建配置属性类,用于从配置文件加载密钥:

import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

@Data
@Component
@ConfigurationProperties(prefix = "app.crypto.aes")
public class AesCryptoProperties {
    /**
     * AES密钥,Base64编码。
     * 必须通过环境变量 SPRING_APPLICATION_JSON 或命令行参数设置,切勿写入配置文件提交。
     */
    private String key;
}

然后,创建一个Service,封装加密解密操作:

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import javax.annotation.PostConstruct;

@Slf4j
@Service
@RequiredArgsConstructor
public class AesCryptoService {

    private final AesCryptoProperties aesCryptoProperties;

    /**
     * 验证配置的密钥是否有效
     */
    @PostConstruct
    public void init() {
        if (aesCryptoProperties.getKey() == null || aesCryptoProperties.getKey().trim().isEmpty()) {
            log.error("AES加密密钥未配置!请设置环境变量或启动参数。");
            throw new IllegalStateException("AES加密密钥未配置");
        }
        try {
            // 尝试解码,验证密钥格式是否正确
            byte[] keyBytes = Base64Utils.decodeFromString(aesCryptoProperties.getKey());
            if (keyBytes.length != 16 && keyBytes.length != 24 && keyBytes.length != 32) {
                log.error("AES密钥长度无效。应为16, 24或32字节(Base64解码后)。");
                throw new IllegalStateException("AES密钥长度无效");
            }
            log.info("AES加密服务初始化成功,密钥长度: {} 位", keyBytes.length * 8);
        } catch (Exception e) {
            log.error("AES密钥Base64解码失败", e);
            throw new IllegalStateException("AES密钥格式错误", e);
        }
    }

    public String encrypt(String plainText) {
        return AesCryptoUtils.encrypt(plainText, aesCryptoProperties.getKey());
    }

    public String decrypt(String encryptedText) {
        return AesCryptoUtils.decrypt(encryptedText, aesCryptoProperties.getKey());
    }
}

现在,在业务代码中,你可以直接注入 AesCryptoService 来加解密数据:

@RestController
@RequestMapping("/api/user")
@RequiredArgsConstructor
public class UserController {

    private final UserService userService;
    private final AesCryptoService cryptoService;

    @PostMapping
    public User createUser(@RequestBody UserCreateRequest request) {
        // 对敏感信息加密后再存储
        String encryptedIdCard = cryptoService.encrypt(request.getIdCard());
        String encryptedMobile = cryptoService.encrypt(request.getMobile());

        User user = new User();
        user.setName(request.getName());
        user.setEncryptedIdCard(encryptedIdCard); // 假设实体类有对应字段
        user.setEncryptedMobile(encryptedMobile);

        return userService.save(user);
    }

    @GetMapping("/{id}")
    public UserResponse getUser(@PathVariable Long id) {
        User user = userService.findById(id);
        UserResponse response = new UserResponse();
        response.setName(user.getName());
        // 查询时解密
        response.setIdCard(cryptoService.decrypt(user.getEncryptedIdCard()));
        response.setMobile(cryptoService.decrypt(user.getEncryptedMobile()));
        return response;
    }
}

4. 与持久层框架的深度集成:自动加解密

上面的方式需要在每个业务代码里手动调用加解密,繁琐且容易遗漏。更优雅的方式是与持久层框架(如JPA/Hibernate、MyBatis)集成,实现字段的自动加解密。

4.1 使用JPA AttributeConverter 实现字段级别转换

JPA 2.1引入了 AttributeConverter 接口,可以在实体属性与数据库列之间进行自定义转换,这是实现自动加解密的绝佳位置。

import javax.persistence.AttributeConverter;
import javax.persistence.Converter;

@Converter(autoApply = false) // autoApply = false 表示需要显式在字段上使用@Convert注解
public class CryptoConverter implements AttributeConverter<String, String> {

    // 注意:这里直接静态引用了工具类。更优解是让Converter成为Spring Bean,注入Service。
    // 但JPA的Converter默认不是Bean,需要额外配置。这里为简化,使用静态方法。
    // 生产环境建议通过ThreadLocal或自定义注册方式获取Spring Context来获取Bean。
    private static final String AES_KEY = System.getenv("AES_ENCRYPTION_KEY");

    @Override
    public String convertToDatabaseColumn(String attribute) {
        // 实体属性 -> 数据库列:加密
        if (attribute == null) {
            return null;
        }
        try {
            return AesCryptoUtils.encrypt(attribute, AES_KEY);
        } catch (Exception e) {
            throw new RuntimeException("加密失败,无法持久化", e);
        }
    }

    @Override
    public String convertToEntityAttribute(String dbData) {
        // 数据库列 -> 实体属性:解密
        if (dbData == null) {
            return null;
        }
        try {
            return AesCryptoUtils.decrypt(dbData, AES_KEY);
        } catch (Exception e) {
            throw new RuntimeException("解密失败,无法读取", e);
        }
    }
}

在实体类字段上使用这个转换器:

@Entity
@Table(name = "users")
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String name;

    @Column(name = "id_card")
    @Convert(converter = CryptoConverter.class) // 应用自定义转换器
    private String idCard;

    @Column(name = "mobile")
    @Convert(converter = CryptoConverter.class)
    private String mobile;

    // getters and setters
}

这样,当你调用 userRepository.save(user) 时, idCard mobile 字段会自动被加密后存入数据库;当你从数据库查询出 User 实体时,这两个字段会自动被解密。业务代码完全感知不到加解密过程。

注意事项与局限

  1. 模糊查询失效 :由于数据在数据库中是密文, WHERE mobile = '13800138000' 这样的精确查询会失效,因为数据库里存的是加密后的字符串。同样, LIKE 查询也无法进行。这是应用层加密的通用限制。
  2. Converter不是Spring Bean :如上所述,在 CryptoConverter 中直接获取密钥或调用Spring Bean比较麻烦。一个解决方案是使用 org.springframework.beans.factory.annotation.Autowired 的静态代理模式,或者使用 ApplicationContextAware ,但这会增加复杂度。
  3. 性能 :每次读写都会触发加解密,对性能有轻微影响,需评估。

4.2 使用MyBatis TypeHandler 实现自动类型处理

对于MyBatis(或MyBatis-Plus)项目,可以通过实现 TypeHandler 接口来达到类似效果。

import org.apache.ibatis.type.BaseTypeHandler;
import org.apache.ibatis.type.JdbcType;
import org.springframework.stereotype.Component;
import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

@Component // 注册为Spring Bean
public class CryptoTypeHandler extends BaseTypeHandler<String> {

    @Autowired
    private AesCryptoService cryptoService; // 可以方便地注入Spring Bean

    @Override
    public void setNonNullParameter(PreparedStatement ps, int i, String parameter, JdbcType jdbcType) throws SQLException {
        // 设置参数时加密
        String encrypted = cryptoService.encrypt(parameter);
        ps.setString(i, encrypted);
    }

    @Override
    public String getNullableResult(ResultSet rs, String columnName) throws SQLException {
        // 从结果集获取时解密
        String encrypted = rs.getString(columnName);
        return encrypted != null ? cryptoService.decrypt(encrypted) : null;
    }

    @Override
    public String getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
        String encrypted = rs.getString(columnIndex);
        return encrypted != null ? cryptoService.decrypt(encrypted) : null;
    }

    @Override
    public String getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
        String encrypted = cs.getString(columnIndex);
        return encrypted != null ? cryptoService.decrypt(encrypted) : null;
    }
}

在MyBatis的Mapper XML文件中,或者在实体字段上使用 @TableField(typeHandler = CryptoTypeHandler.class) (MyBatis-Plus注解),即可指定该字段使用自定义的TypeHandler进行处理。

5. 配置文件敏感信息加密(Jasypt集成)

数据库密码、第三方API密钥等写在 application.yml 里也是安全隐患。可以使用 jasypt-spring-boot-starter 这个库,轻松实现配置文件的加密。

5.1 快速集成步骤

  1. 添加依赖

    <dependency>
        <groupId>com.github.ulisesbocchio</groupId>
        <artifactId>jasypt-spring-boot-starter</artifactId>
        <version>3.0.5</version>
    </dependency>
    
  2. 生成加密后的值 : 你可以写一个简单的Java程序,或者使用Jasypt提供的命令行工具来加密你的明文密码。

    import org.jasypt.encryption.pbe.StandardPBEStringEncryptor;
    import org.jasypt.iv.RandomIvGenerator;
    
    public class JasyptEncryptor {
        public static void main(String[] args) {
            StandardPBEStringEncryptor encryptor = new StandardPBEStringEncryptor();
            encryptor.setPassword("YourSecretMasterPassword"); // 这是加解密的根密码,非常重要!
            encryptor.setAlgorithm("PBEWITHHMACSHA512ANDAES_256"); // 强算法
            encryptor.setIvGenerator(new RandomIvGenerator()); // 使用随机IV
    
            String plainText = "your_database_password";
            String encryptedText = encryptor.encrypt(plainText);
            System.out.println("Encrypted: ENC(" + encryptedText + ")");
            // 输出类似:ENC(AbCdEfGhIjKlMnOpQrStUvWxYz123456==)
        }
    }
    
  3. 修改配置文件 : 将生成的密文,用 ENC() 包裹起来,替换原来的明文。

    spring:
      datasource:
        url: jdbc:mysql://localhost:3306/mydb
        username: root
        password: ENC(AbCdEfGhIjKlMnOpQrStUvWxYz123456==) # 这里是加密后的密码
    
  4. 设置解密密码 : 启动应用时,必须让Jasypt知道解密密码。可以通过环境变量、命令行参数或系统属性设置。

    • 命令行 java -jar your-app.jar --jasypt.encryptor.password=YourSecretMasterPassword
    • 环境变量 JASYPT_ENCRYPTOR_PASSWORD=YourSecretMasterPassword
    • 系统属性 :在 application.yml 中设置 jasypt.encryptor.password=${JASYPT_PASSWORD:} ,然后通过 -D 传递。

5.2 安全最佳实践

  • 根密码管理 jasypt.encryptor.password 这个根密码是解开所有配置密文的钥匙,必须严格保密。 绝对不要 把它写在项目配置文件里提交到代码库。必须通过外部环境变量或启动参数传入。
  • 算法选择 :使用强算法,如示例中的 PBEWITHHMACSHA512ANDAES_256 ,并启用随机IV。
  • 与配置中心结合 :在微服务架构中,可以将加密后的配置统一存放在配置中心(如Nacos),应用启动时拉取并解密。

6. 常见问题、性能考量与进阶方案

在实际落地过程中,你会遇到各种预料之外的问题。

6.1 典型问题排查清单

问题现象 可能原因 排查步骤与解决方案
加解密失败,报 InvalidKeyException 1. 密钥长度不符合算法要求。
2. 密钥格式错误(如不是Base64)。
3. 密钥与加密时使用的算法不匹配(如AES-256的密钥用于AES-128解密)。
1. 检查密钥字节长度是否为16/24/32。
2. 确认密钥是正确Base64编码的字符串。
3. 确保加解密双方使用的算法( TRANSFORMATION 字符串)完全一致。
解密失败,报 BadPaddingException 1. 密文被篡改或损坏。
2. 加密和解密使用的密钥不一致。
3. IV不一致(CBC模式)。
4. 加密后的密文在传输或存储过程中编码出错(如Base64解码失败)。
1. 检查密文完整性。
2. 核对双方密钥是否完全相同。
3. 确认解密时使用的IV与加密时生成并存储的IV一致。
4. 打印并对比加密后的Base64字符串和解密前收到的Base64字符串。
使用JPA Converter后,查询条件失效 查询语句中的条件值是明文,而数据库中是密文,导致匹配不上。 1. 方案一(推荐) :查询时,对查询条件也先用相同的逻辑加密,再与数据库字段比较。例如: repository.findByIdCardEncrypted(converter.convertToDatabaseColumn(“明文身份证”))
2. 方案二 :放弃对该加密字段的精确查询,或建立额外的索引字段(如哈希值)进行查询。
Jasypt解密配置文件失败 1. 根密码未设置或设置错误。
2. 加密算法不匹配。
3. 密文格式错误,未用 ENC() 包裹或包裹符号不匹配。
1. 检查环境变量 JASYPT_ENCRYPTOR_PASSWORD 或启动参数。
2. 确认加密时使用的算法与Jasypt配置的算法一致(默认是 PBEWithMD5AndDES ,建议升级)。
3. 检查配置文件中的密文是否正确被 ENC(...) 包裹。
性能瓶颈出现在数据库读写 大量数据字段加解密,或使用了大密钥(如RSA 4096)频繁操作。 1. 性能分析定位热点。
2. 对称加密AES本身很快,瓶颈多在IO。考虑异步加密或批量处理。
3. 对于非对称加密,避免加密大量数据,仅用于加密密钥或签名。

6.2 性能优化与安全加固建议

  • 密钥轮转 :定期更换加密密钥是安全最佳实践。但这意味着历史加密数据需要用旧密钥解密后再用新密钥加密。设计系统时需要考虑密钥版本管理,在加密结果中附带密钥版本号。
  • 使用更安全的模式 :如前所述,使用 AES/GCM/NoPadding 模式。GCM模式同时提供加密和完整性认证,比CBC更安全,且性能通常更好。
  • 硬件安全模块(HSM) :对于最高安全等级的需求,考虑使用HSM来生成和存储密钥,应用程序通过API调用HSM执行加解密操作,密钥本身永远不会离开HSM。
  • 国密算法支持 :在国内一些对密码算法有明确要求的项目中,可能需要使用国密算法(如SM2、SM3、SM4)。可以使用Bouncy Castle等密码库来提供支持,其集成思路与AES类似,但需注意算法标识和参数设置。

6.3 一个更工程化的加解密服务设计

对于大型项目,可以抽象出一个统一的加解密服务门面(Facade),支持多种算法和密钥版本。

public interface CryptoService {
    /**
     * 加密
     * @param plainText 明文
     * @param algorithm 算法标识(如 "AES-256-GCM", "SM4-CBC")
     * @param keyVersion 密钥版本
     * @return 密文(可包含算法、版本等元信息)
     */
    CryptoResult encrypt(String plainText, String algorithm, String keyVersion);

    /**
     * 解密
     * @param cryptoResult 密文结果对象
     * @return 明文
     */
    String decrypt(CryptoResult cryptoResult);
}

@Data
public class CryptoResult {
    private String cipherText; // 密文
    private String algorithm; // 算法
    private String keyVersion; // 密钥版本
    private String iv; // 初始化向量(如果需要)
    // ... 其他元数据
}

这样,业务代码只需要调用 CryptoService ,而不需要关心底层用的是AES还是国密,密钥从哪里来。服务的实现内部可以去查询密钥管理服务(KMS),根据 algorithm keyVersion 选择正确的密钥进行加解密。这种设计为未来的算法升级、密钥轮转提供了极大的灵活性。

数据加密在Spring Boot中的实现,是一个从“能用”到“好用”再到“安全、可维护”的持续演进过程。起步阶段,一个简单的AES工具类足以应对;随着业务复杂度和安全要求提升,逐步引入自动持久化转换、配置加密、密钥管理服务等组件。核心始终是:理解算法原理、管好密钥生命线、并根据实际业务场景(性能、查询需求、合规)做出合适的技术选型。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值