基于SpringBoot3+Java17+Nacos的配置中心和本地配置文件加解密

一、技术架构

1.1 核心依赖

xml 复制代码
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
    <version>2023.0.3.4</version>
</dependency>
<dependency>
    <groupId>com.github.ulisesbocchio</groupId>
    <artifactId>jasypt-spring-boot-starter</artifactId>
    <version>3.0.5</version>
</dependency>

二、Nacos配置中心加解密

2.1 自定义StringEncryptor实现

java 复制代码
@Component("ymlDecryptConfig")
public class YmlDecryptConfig implements StringEncryptor {
    
    @Resource
    private Sm4EncryptUtils sm4EncryptUtils;

    @Override
    public String encrypt(String message) {
        return sm4EncryptUtils.encrypt(message);
    }

    @Override
    public String decrypt(String encryptedMessage) {
        return sm4EncryptUtils.decrypt(encryptedMessage);
    }
}

2.2 配置文件配置

yaml 复制代码
jasypt:
  encryptor:
    bean: ymlDecryptConfig

spring:
  cloud:
    nacos:
      config:
        username: ENC(加密字符串)
        password: ENC(加密字符串)

三、本地配置文件加解密实现

3.1 核心原理分析

BindHandler接口作用
java 复制代码
// Spring Boot 2.4+ 引入的配置绑定机制
public interface BindHandler {
    // 在绑定属性成功后调用,可以对结果进行处理
    Object onSuccess(ConfigurationPropertyName name, 
                     Bindable<?> target, 
                     BindContext context, 
                     Object result);
    
    // 绑定前、绑定失败等方法
    // ...
}
TextEncryptorBindHandler分析

spring-cloud-context包中:

java 复制代码
// Spring Cloud 内置的解密处理器
public class TextEncryptorBindHandler extends AbstractBindHandler {
    
    @Override
    public Object onSuccess(ConfigurationPropertyName name, 
                           Bindable<?> target, 
                           BindContext context, 
                           Object result) {
        
        if (result instanceof String) {
            String value = (String) result;
            // 检查是否有加密前缀,如"ENC(...)"
            if (value.startsWith("ENC(") && value.endsWith(")")) {
                // 调用TextEncryptor进行解密
                return textEncryptor.decrypt(extractEncryptedValue(value));
            }
        }
        return result;
    }
}

3.2 自定义BindHandler实现

java 复制代码
public class CustomBindHandler extends AbstractBindHandler {
    
    private static final String ENV_KEY = "CONFIG_ENCRYPT_KEY";
    private static final String PREFIX = "ENC(";
    private static final String SUFFIX = ")";
    
    private final YmlDecryptConfig decryptConfig;
    
    public CustomBindHandler() {
        // 从环境变量获取密钥
        String key = System.getenv(ENV_KEY);
        if (key == null) {
            key = System.getProperty(ENV_KEY.toLowerCase().replace("_", "."));
        }
        
        // 创建解密器
        this.decryptConfig = createDecryptConfig(key);
    }
    
    @Override
    public Object onSuccess(ConfigurationPropertyName name, 
                           Bindable<?> target, 
                           BindContext context, 
                           Object result) {
        
        if (result instanceof String) {
            String value = (String) result;
            // 匹配ENC(...)格式
            if (value.startsWith(PREFIX) && value.endsWith(SUFFIX)) {
                // 提取并解密
                String encrypted = value.substring(PREFIX.length(), 
                                                  value.length() - SUFFIX.length());
                return decryptConfig.decrypt(encrypted);
            }
        }
        return result;
    }
    
    private YmlDecryptConfig createDecryptConfig(String key) {
        YmlDecryptConfig config = new YmlDecryptConfig();
        Sm4EncryptUtils utils = new Sm4EncryptUtils();
        utils.setPrivateKey(key);
        config.setSm4EncryptUtils(utils);
        return config;
    }
}

3.3 注册自定义处理器

java 复制代码
@Configuration
public class BootstrapConfiguration {
    
    @Bean
    @ConditionalOnMissingBean
    public BindHandler bindHandler() {
        return new CustomBindHandler();
    }
}

META-INF/spring.factories中:

properties 复制代码
org.springframework.cloud.bootstrap.BootstrapConfiguration=\
com.example.config.BootstrapConfiguration

四、底层机制解析

4.1 配置加载流程

复制代码
1. SpringApplication启动
2. 加载bootstrap配置文件
3. PropertySourceLoader解析配置文件
4. Binder进行属性绑定
5. BindHandler链式处理
6. TextEncryptorBindHandler/CustomBindHandler解密
7. 将解密后的值注入Bean

4.2 BindHandler工作机制

java 复制代码
// Binder中的关键代码
public <T> T bindOrCreate(ConfigurationPropertyName name, Bindable<T> target,
        BindHandler handler) {
    
    // 创建绑定上下文
    BindContext context = new ApplicationBindContext();
    
    // 执行绑定
    return bind(name, target, handler, context, false);
}

private <T> T bind(ConfigurationPropertyName name, Bindable<T> target,
        BindHandler handler, BindContext context, boolean allowRecursiveBinding) {
    
    // 调用BindHandler的onStart方法
    handler.onStart(name, target, context);
    
    try {
        // 执行实际绑定
        T result = doBind(name, target, context, allowRecursiveBinding);
        
        // 调用onSuccess方法(解密在这里发生)
        result = handler.onSuccess(name, target, context, result);
        
        return result;
    } catch (Exception ex) {
        // 处理异常
        return handler.onFailure(name, target, context, ex);
    } finally {
        // 清理
        handler.onFinish(name, target, context);
    }
}

4.3 TextEncryptorBindHandler vs CustomBindHandler

特性 TextEncryptorBindHandler CustomBindHandler
解密时机 属性绑定成功后 属性绑定成功后
加密前缀 可配置 ENC(...)固定格式
加密算法 Jasypt默认 SM4国密算法
密钥来源 jasypt.encryptor.password 环境变量
集成方式 Spring Cloud内置 自定义实现

4.4 Spring Cloud 2023.0.3.4版本适配

java 复制代码
// 新版Spring Cloud中,TextEncryptorBindHandler已被重构
// 核心逻辑移动到PropertySourceBootstrapConfiguration中

public class PropertySourceBootstrapConfiguration 
        implements ApplicationContextInitializer<ConfigurableApplicationContext> {
    
    @Override
    public void initialize(ConfigurableApplicationContext context) {
        // 加载PropertySource
        List<PropertySource<?>> sources = loadPropertySources();
        
        // 应用解密处理器
        for (PropertySource<?> source : sources) {
            if (source instanceof OriginTrackedMapPropertySource) {
                // 解密处理
                decryptPropertySource((OriginTrackedMapPropertySource) source);
            }
        }
    }
}

五、完整示例配置

5.1 bootstrap.yml

yaml 复制代码
spring:
  application:
    name: demo-service
  cloud:
    nacos:
      config:
        server-addr: ${NACOS_HOST:localhost}:8848
        username: ${NACOS_USER:ENC(加密用户名)}
        password: ${NACOS_PASS:ENC(加密密码)}
        
  datasource:
    url: jdbc:mysql://localhost:3306/demo
    username: ENC(加密用户名)
    password: ENC(加密密码)
    
  redis:
    password: ENC(加密Redis密码)

5.2 启动参数

bash 复制代码
# 设置加密密钥
export CONFIG_ENCRYPT_KEY=your-32bit-sm4-key

# 启动应用
java -jar app.jar \
  --spring.profiles.active=prod \
  --CONFIG.ENCRYPT.KEY=${CONFIG_ENCRYPT_KEY}

六、关键注意事项

  1. 密钥安全:生产环境密钥必须通过环境变量或密钥管理服务传入
  2. 加密格式 :必须使用ENC(加密内容)格式包裹
  3. 处理器顺序:确保CustomBindHandler在绑定链中的正确顺序
  4. 版本兼容:Spring Cloud 2023.0.3.4与Spring Boot 3.x的兼容性已验证
  5. 性能影响:加解密操作在启动时一次性完成,不影响运行时性能

七、总结

通过自定义StringEncryptorBindHandler,实现了Nacos配置中心和本地配置文件的双重加密保护。关键点在于理解Spring Boot的配置绑定机制和Spring Cloud的解密处理流程,特别是BindHandleronSuccess方法在属性绑定后的处理时机。

相关推荐
一勺菠萝丶4 小时前
Jenkins 中如何给角色分配多个不同名称的项目(Role 权限实战)
java·运维·jenkins
han_hanker4 小时前
springboot 封装的比较好的 统一的返回类型 工具类
java·spring boot·后端
怪只怪满眼尽是人间烟火4 小时前
springboot数据上链FISCO BCOS
java·spring boot·后端
没什么本事4 小时前
Springboot CGLIB 代理对象问题
java·spring boot·spring
好好沉淀4 小时前
开发过程中动态 SQL 中where 1=1的作用是什么
java·服务器·开发语言·数据库·sql
Javatutouhouduan4 小时前
SpringBoot整合reids之JSON序列化文件夹操作
java·spring boot·spring·bootstrap·html·后端开发·java架构师
她说..4 小时前
Spring AOP场景5——异常处理(附带源码)
java·数据库·后端·spring·springboot·spring aop
微扬嘴角4 小时前
Springcloud篇9-Elasticsearch-3(数据聚合、自动补全、数据同步、集群)
elasticsearch·spring cloud
醇氧4 小时前
springAI学习 (二) 模型
java·学习·spring·ai·ai编程