基于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
相关产品推荐
相关产品推荐

