You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于YML配置动态扩展Spring Boot加密服务的方案问询

无需新增Java代码即可扩展加密服务的Spring Boot实现

问题背景

在Spring Boot微服务中,需要基于YML配置处理加密API请求,示例配置如下:

profile-protection:
  dataClasses:
    - name: USER_INFO
      activeVersionName: version-1
      versions:
        - versionName: version-1
          algorithm: AES
          encryptKeyName: user
    - name: SAMPLE_ENCRYPTION
      activeVersionName: version-1
      versions:
        - versionName: version-1
          algorithmName: AES
          encryptKeyName: user_aes

最初采用工厂模式实现,通过dataClassName匹配对应的加密服务类,但这种方案存在明显局限性:每新增一个dataClass类型,必须手动创建对应的Spring Boot服务类,无法通过配置直接扩展。


解决方案:配置驱动的通用加密服务

核心思路是基于配置动态生成加密能力,用一个通用加密服务类处理所有dataClass的加密解密需求,无需为每个类型单独编写实现类。

1. 配置属性绑定类

先创建配置绑定类,将YML配置映射为Java对象,兼容配置中algorithm和algorithmName两种字段名:

@ConfigurationProperties(prefix = "profile-protection")
@Component
public class EncryptionProperties {
    private List<DataClassConfig> dataClasses;

    // Getters & Setters
    public static class DataClassConfig {
        private String name;
        private String activeVersionName;
        private List<VersionConfig> versions;

        // Getters & Setters
        public static class VersionConfig {
            private String versionName;
            @JsonProperty(value = {"algorithm", "algorithmName"})
            private String algorithm;
            private String encryptKeyName;

            // Getters & Setters
        }
    }
}

2. 通用加密服务实现

创建通用加密服务,根据dataClassName从配置中获取对应的算法、密钥参数,执行加密解密逻辑:

@Service
public class GenericEncryptionService implements EncryptionService {
    private final EncryptionProperties encryptionProperties;
    private final KeyManager keyManager;

    @Autowired
    public GenericEncryptionService(EncryptionProperties encryptionProperties, KeyManager keyManager) {
        this.encryptionProperties = encryptionProperties;
        this.keyManager = keyManager;
    }

    @Override
    public String encrypt(String plaintext, String dataClassName) {
        DataClassConfig config = getValidDataClassConfig(dataClassName);
        VersionConfig activeVersion = getActiveVersionConfig(config);
        
        SecretKey key = keyManager.getKey(activeVersion.getEncryptKeyName());
        return executeEncryption(plaintext, key, activeVersion.getAlgorithm());
    }

    @Override
    public String decrypt(String ciphertext, String dataClassName) {
        DataClassConfig config = getValidDataClassConfig(dataClassName);
        VersionConfig activeVersion = getActiveVersionConfig(config);
        
        SecretKey key = keyManager.getKey(activeVersion.getEncryptKeyName());
        return executeDecryption(ciphertext, key, activeVersion.getAlgorithm());
    }

    // 校验并获取指定dataClass的配置
    private DataClassConfig getValidDataClassConfig(String dataClassName) {
        return encryptionProperties.getDataClasses().stream()
                .filter(c -> c.getName().equals(dataClassName))
                .findFirst()
                .orElseThrow(() -> new IllegalArgumentException("无效的dataClass名称: " + dataClassName));
    }

    // 获取当前激活的版本配置
    private VersionConfig getActiveVersionConfig(DataClassConfig config) {
        return config.getVersions().stream()
                .filter(v -> v.getVersionName().equals(config.getActiveVersionName()))
                .findFirst()
                .orElseThrow(() -> new IllegalArgumentException("dataClass " + config.getName() + "无有效激活版本"));
    }

    // 通用加密逻辑,可扩展支持多种算法
    private String executeEncryption(String plaintext, SecretKey key, String algorithm) {
        try {
            Cipher cipher = Cipher.getInstance(algorithm);
            cipher.init(Cipher.ENCRYPT_MODE, key);
            byte[] encryptedBytes = cipher.doFinal(plaintext.getBytes(StandardCharsets.UTF_8));
            return Base64.getEncoder().encodeToString(encryptedBytes);
        } catch (Exception e) {
            throw new RuntimeException("加密失败", e);
        }
    }

    // 通用解密逻辑
    private String executeDecryption(String ciphertext, SecretKey key, String algorithm) {
        try {
            Cipher cipher = Cipher.getInstance(algorithm);
            cipher.init(Cipher.DECRYPT_MODE, key);
            byte[] decryptedBytes = cipher.doFinal(Base64.getDecoder().decode(ciphertext));
            return new String(decryptedBytes, StandardCharsets.UTF_8);
        } catch (Exception e) {
            throw new RuntimeException("解密失败", e);
        }
    }
}

3. 密钥管理服务(示例)

实现密钥管理服务,从安全渠道(如配置中心、密钥库)获取密钥,避免硬编码:

@Service
public class KeyManager {
    private final Map<String, SecretKey> keyStore = new HashMap<>();

    @PostConstruct
    public void initKeys() {
        // 实际项目建议从Spring Cloud Vault、阿里云KMS等安全系统获取密钥
        keyStore.put("user", generateAesKey("your-user-key-secret"));
        keyStore.put("user_aes", generateAesKey("your-user-aes-key-secret"));
    }

    public SecretKey getKey(String keyName) {
        SecretKey key = keyStore.get(keyName);
        if (key == null) {
            throw new IllegalArgumentException("未找到密钥: " + keyName);
        }
        return key;
    }

    private SecretKey generateAesKey(String secret) {
        byte[] keyBytes = secret.getBytes(StandardCharsets.UTF_8);
        // 调整密钥长度为AES标准的16/24/32字节
        keyBytes = Arrays.copyOf(keyBytes, 16);
        return new SecretKeySpec(keyBytes, "AES");
    }
}

4. 调整控制器与接口

修改EncryptionService接口,新增dataClassName参数,直接注入通用服务即可:

public interface EncryptionService {
    String encrypt(String plaintext, String dataClassName);
    String decrypt(String ciphertext, String dataClassName);
}

@RestController
@RequestMapping("/encryption")
public class EncryptionController {
    private final GenericEncryptionService encryptionService;

    @Autowired
    public EncryptionController(GenericEncryptionService encryptionService) {
        this.encryptionService = encryptionService;
    }

    @PostMapping("/{dataClassName}/encrypt")
    public String encrypt(@PathVariable String dataClassName, @RequestBody String plaintext) {
        return encryptionService.encrypt(plaintext, dataClassName);
    }

    @PostMapping("/{dataClassName}/decrypt")
    public String decrypt(@PathVariable String dataClassName, @RequestBody String ciphertext) {
        return encryptionService.decrypt(ciphertext, dataClassName);
    }
}

方案优势

  • 零代码扩展:新增dataClass只需在YML中添加配置条目,无需编写新服务类
  • 配置驱动:加密规则(算法、密钥、版本)完全由配置管理,便于动态调整
  • 版本兼容:支持同一个dataClass的多版本配置,实现算法/密钥的平滑过渡
  • 扩展性强:新增加密算法只需在通用服务中补充对应逻辑即可

内容的提问来源于stack exchange,提问作者Peter Penzov

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.17 18:14:51