@Value讀取問題
在檔案讀取的方式上。EncryptionService 使用 Files.readAllBytes() 直接從檔案系統讀取,
而 Controller 使用 ClassPathResource 從 classpath 讀取。
Controller 的方式可以正常運作
@Slf4j
@Service
public class EncryptionService {
@Value("${key.path.private:src/main/resources/private_key.pem}")
private String privateKeyPath;
private final RSAPrivateKey privateKey;
public EncryptionService() throws Exception {
// 初始化時載入私鑰
this.privateKey = loadPrivateKey();
}
private RSAPrivateKey loadPrivateKey() throws Exception {
try {
// Resource resource = new ClassPathResource(privateKeyPath);
// String privateKey = new String(FileCopyUtils.copyToByteArray(resource.getInputStream()));
// 讀取私鑰文件
byte[] keyBytes = Files.readAllBytes(Paths.get(privateKeyPath));
String privateKeyPEM = new String(keyBytes)
.replace("-----BEGIN PRIVATE KEY-----", "")
.replace("-----END PRIVATE KEY-----", "")
.replaceAll("\\s", "");
log.info("privateKeyPEM:{}",privateKeyPEM);
// 解碼 Base64 編碼的私鑰
byte[] decoded = Base64.getDecoder().decode(privateKeyPEM);
// 轉換為 PKCS8 格式的私鑰
PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(decoded);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
return (RSAPrivateKey) keyFactory.generatePrivate(keySpec);
// return null;
} catch (Exception e) {
log.error("Failed to load private key", e);
throw new Exception("Failed to load private key: " + e.getMessage());
}
}
@RestController
@RequestMapping("/api")
public class PublicKeyController {
private static final Logger logger = LoggerFactory.getLogger(PublicKeyController.class);
@Value("${key.path.public:src/main/resources/public_key.pem}")
private String publicKeyPath;
@Value("${key.path.private:src/main/resources/private_key.pem}")
private String privateKeyPath;
@GetMapping("/private-key")
public ResponseEntity<?> getPrivateKey() {
try {
// 使用 ClassPathResource 來讀取資源檔案
Resource resource = new ClassPathResource(privateKeyPath);
String publicKey = new String(FileCopyUtils.copyToByteArray(resource.getInputStream()));
return ResponseEntity.ok(publicKey);
} catch (FileNotFoundException e) {
logger.error("Public key file not found: {}", privateKeyPath, e);
return ResponseEntity
.status(HttpStatus.NOT_FOUND)
.body("Public key file not found");
} catch (IOException e) {
logger.error("Error reading public key file: {}", privateKeyPath, e);
return ResponseEntity
.status(HttpStatus.INTERNAL_SERVER_ERROR)
.body("Error reading public key file");
} catch (Exception e) {
logger.error("Unexpected error while reading public key: {}", e.getMessage(), e);
return ResponseEntity
.status(HttpStatus.INTERNAL_SERVER_ERROR)
.body("Internal server error");
}
}
Caused by: org.springframework.beans.BeanInstantiationException: Failed to instantiate [com.feddoubt.Cry.service.EncryptionService]: Constructor threw exception
at org.springframework.beans.BeanUtils.instantiateClass(BeanUtils.java:223)
at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:88)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateBean(AbstractAutowireCapableBeanFactory.java:1310)
... 31 common frames omitted
Caused by: java.lang.Exception: Failed to load private key: null
at com.feddoubt.Cry.service.EncryptionService.loadPrivateKey(EncryptionService.java:89)
at com.feddoubt.Cry.service.EncryptionService.<init>(EncryptionService.java:31)
at java.base/jdk.internal.reflect.DirectConstructorHandleAccessor.newInstance(DirectConstructorHandleAccessor.java:62)
at java.base/java.lang.reflect.Constructor.newInstanceWithCaller(Constructor.java:502)
at java.base/java.lang.reflect.Constructor.newInstance(Constructor.java:486)
at org.springframework.beans.BeanUtils.instantiateClass(BeanUtils.java:197)
修改 EncryptionService 採用相同的讀取方式。
private RSAPrivateKey privateKey;
@PostConstruct // 改用 @PostConstruct 進行初始化
public void init() throws Exception {
this.privateKey = loadPrivateKey();
}
改用 @PostConstruct 取代建構子初始化
使用 ClassPathResource 和 FileCopyUtils 來讀取檔案,這樣可以正確從 classpath 讀取資源
將 privateKey 欄位改為不用 final,因為我們改用 @PostConstruct 初始化