Spring集成CloudFront:如何加载内存私钥文件生成Signed URL?
解决Spring中加载CloudFront私钥PEM文件的问题
你碰到的这个问题太常见了——当Spring应用打包成JAR后,classpath里的资源是嵌在JAR内部的,根本不是文件系统里的独立文件,所以resource.getFile()肯定会报错。下面给你两种靠谱的解决办法:
方法1:把资源转成临时文件(适配你当前用的API)
既然你现在用的getSignedURLWithCannedPolicy要求传File对象,我们可以把classpath里的PEM资源读成流,再写入一个临时文件,传给CloudFront就行:
// 加载classpath资源 Resource pemResource = resourceLoader.getResource("classpath:config/pk-mykey.pem"); // 创建临时文件,后缀保持.pem File tempPrivateKeyFile = File.createTempFile("cloudfront-private-", ".pem"); // 把资源流写入临时文件 try (InputStream inputStream = pemResource.getInputStream(); FileOutputStream outputStream = new FileOutputStream(tempPrivateKeyFile)) { // 自己实现流拷贝,也可以用Apache Commons IO的IOUtils.copy简化 byte[] buffer = new byte[1024]; int bytesRead; while ((bytesRead = inputStream.read(buffer)) != -1) { outputStream.write(buffer, 0, bytesRead); } } // 调用CloudFront签名API String signedUrl = CloudFrontUrlSigner.getSignedURLWithCannedPolicy( Protocol.https, distributionDomainName, tempPrivateKeyFile, "/images/img.jpg", cloudFrontKeyPairId, expirationDate ); // 让JVM退出时自动删除临时文件,避免磁盘残留 tempPrivateKeyFile.deleteOnExit();
方法2:直接加载PrivateKey(更优雅,不用临时文件)
其实CloudFront的签名API有重载方法,支持直接传PrivateKey对象,完全不用依赖物理文件。我们可以直接解析classpath里的PEM资源生成私钥,再调用对应的API:
第一步:解析PEM为PrivateKey
如果你的PEM是PKCS#8格式(开头是-----BEGIN PRIVATE KEY-----),用JDK自带API就能解析:
Resource pemResource = resourceLoader.getResource("classpath:config/pk-mykey.pem"); String pemContent; // 读取PEM内容,去掉首尾的标记行 try (InputStream inputStream = pemResource.getInputStream(); BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream))) { pemContent = reader.lines() .filter(line -> !line.startsWith("-----BEGIN") && !line.startsWith("-----END")) .collect(Collectors.joining()); } // 解码Base64并生成PrivateKey PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(Base64.getDecoder().decode(pemContent)); KeyFactory keyFactory = KeyFactory.getInstance("RSA"); PrivateKey privateKey = keyFactory.generatePrivate(keySpec);
如果你的PEM是PKCS#1格式(开头是-----BEGIN RSA PRIVATE KEY-----),需要借助BouncyCastle库解析:
// 先添加BouncyCastle依赖,再注册Provider Security.addProvider(new BouncyCastleProvider()); Resource pemResource = resourceLoader.getResource("classpath:config/pk-mykey.pem"); String pemContent; try (InputStream inputStream = pemResource.getInputStream(); BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream))) { pemContent = reader.lines().collect(Collectors.joining("\n")); } // 用BouncyCastle解析PKCS#1格式私钥 PEMParser pemParser = new PEMParser(new StringReader(pemContent)); JcaPEMKeyConverter converter = new JcaPEMKeyConverter().setProvider("BC"); PrivateKey privateKey = converter.getPrivateKey((PEMKeyPair) pemParser.readObject());
第二步:调用签名API
拿到PrivateKey后,调用对应的重载方法(注意参数顺序和File版本不同):
String signedUrl = CloudFrontUrlSigner.getSignedURLWithCannedPolicy( "/images/img.jpg", cloudFrontKeyPairId, privateKey, expirationDate, Protocol.https, distributionDomainName );
小提醒
- 打包时要确认你的PEM文件被正确包含在JAR里,Spring Boot默认会打包
src/main/resources下的所有文件,只要放在这个目录下就没问题。 - 优先用方法2,不用创建临时文件,性能更优也更干净。
内容的提问来源于stack exchange,提问作者wesleyy
相关产品推荐
相关产品推荐

