如何在 Spring Boot 中正确读取配置目录下的 PEM 文件

spring boot 的 `-dspring.config.additional-location` 仅支持加载配置类文件(如 application.yml、properties),无法自动识别或注入普通资源文件(如 test.pem)。需通过配置属性指定路径,再结合 `resourceloader` 或 `files.readallbytes()` 安全读取。

在 Spring Boot 应用中,-Dspring.config.additional-location 是专为外部化配置设计的机制,它仅解析符合 Spring 配置格式的文件(如 .yml、.properties、.xml 等),并将其键值对注入到 Spring Environment 中。而 test.pem 是纯文本证书文件,不属于配置资源,因此 不会被自动扫描、注册或暴露为配置项 —— 这正是直接使用 Paths.get("test.pem") 报 NoSuchFileException 的根本原因:JVM 当前工作目录(user.dir)通常为项目根目录或 IDE 启动路径,并非你指定的 rdk-factory-data-config/ 目录,且该路径未加入类路径(classpath)或资源加载器搜索范围。

✅ 正确做法:将 PEM 文件路径作为配置项声明,再通过 Spring 资源抽象安全读取

步骤 1:在配置文件中声明 PEM 路径

在 application-local.yml(或你激活的 profile 配置文件)中添加自定义属性:

# rdk-factory-data-config/application-local.yml
app:
  cert:
    pem-path: file:/Users/bharatsuthar/HGW/codebaseDevelopmentRepo1/data-generation-tool/rdk-factory-data-config/test.pem
✅ 优势:路径集中管理、支持 profile 切换、便于测试与部署隔离。

步骤 2:使用 ResourceLoader 读取 PEM 内容(推荐)

ResourceLoader 是 Spring 提供的统一资源访问接口,天然支持 file:、classpath:、http: 等协议,且能正确解析 file:/... 路径:

import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.nio.charset.StandardCharsets;

@Service
public class PemCertificateService {

    private final ResourceLoader resourceLoader;

    public PemCertificateService(ResourceLoader resourceLoader) {
        this.resourceLoader = resourceLoader;
    }

    // 从配置中获取路径并读取 PEM 内容
    public String loadPemCertificate(String pemPath) throws IOException {
        Resource resource = resourceLoader.getResource(pemPath);
        if (!resource.exists()) {
            throw new IllegalArgumentException("PEM file not found: " + pemPath);
        }
        return new String(resource.getInputStream().readAllBytes(), StandardCharsets.UTF_8);
    }
}

调用示例:

@Component
public class CertInitializer {

    private final PemCertificateService pemService;

    public CertInitializer(PemCertificateService pemService) {
        this.pemService = pemService;
    }

    @PostConstruct
    public void init() throws IOException {
        String pemContent = pemService.loadPemCertificate("${app.cert.pem-path}");
        System.out.println("Loaded PEM:\n" + pemContent.substring(0, 100) + "...");
    }
}

⚠️ 注意事项与最佳实践

  • 避免硬编码绝对路径:开发时可用 file: 协议,但生产环境建议改用 classpath:(将 test.pem 放入 src/main/resources/config/)或挂载为容器卷后统一映射路径。
  • 不要用 Paths.get("test.pem"):该方式依赖 JVM 当前工作目录,不可靠且无法跨环境迁移。
  • 证书内容需校验完整性:读取后建议验证 PEM 头尾(-----BEGIN CERTIFICATE----- / -----END CERTIFICATE-----),防止文件损坏或格式错误。
  • 敏感信息防护:若 PEM 包含私钥,请确保文件权限严格(如 chmod 600),并在日志中禁止打印完整内容。

✅ 总结

Spring Boot 不会自动将 -Dspring.config.additional-location 指定目录下的非配置文件(如 .pem)纳入资源加载体系。正确路径是:通过配置属性声明路径 → 使用 ResourceLoader 加载 → 安全读取为字符串。这种方式既符合 Spring 的资源抽象规范,又具备环境可移植性与配置可维护性,是企业级应用的标准实践。