# Spring Boot + Jasypt 最終整合:
## `ENC(...)` 用 legacy(MD5+DES),協力廠商通訊用 `encryptorBean`
> 目標:
> - `application.yml` 的 `ENC(...)` 由 **legacy**(`PBEWithMD5AndDES` + `NoIv` + `vu4wj/3`)自動解密。
> - 協力廠商提供的 **`@Bean("encryptorBean")`** 僅供**程式碼**手動加解密通訊資料,不參與組態自動解密。
---
## 1) application.yml(只放參數,不指定 bean 名)
```yaml
jasypt:
encryptor:
algorithm: PBEWithMD5AndDES
iv-generator-classname: org.jasypt.iv.NoIvGenerator
password: vu4wj/3
# 不要設定 jasypt.encryptor.bean
```
說明:`ulisesbocchio/jasypt-spring-boot` 會依上述參數自動解 `ENC(...)`,無需指定 Bean 名。
---
## 2) 啟用註解設定(避免指定 encryptorBean)
若專案有使用 `@EnableEncryptableProperties`(或你說的 `@enableEncrypt`),**不要**指向協力廠商的 Bean 名。
```java
// 正確:讓 starter 依 application.yml 的參數處理
@EnableEncryptableProperties
// 錯誤示例(會把組態解密交給協力廠商 encryptorBean,造成衝突):
// @EnableEncryptableProperties(encryptorBean = "encryptorBean")
```
---
## 3) 協力廠商提供的 Encryptor(顯式 Bean 名,只給程式碼用)
```java
@Configuration
public class PartnerCryptoConfig {
@Bean("encryptorBean") // 協力廠商指定的 Bean 名
public StringEncryptor vendorEncryptor() {
String p1 = System.getProperty("P_ENV_1");
String p2 = System.getProperty("P_ENV_2");
if (p1 == null || p1.isBlank() || "null".equalsIgnoreCase(p1)
|| p2 == null || p2.isBlank() || "null".equalsIgnoreCase(p2)) {
p1 = System.getenv("P_ENV_1");
p2 = System.getenv("P_ENV_2");
}
String salt = (p1 == null ? "" : p1) + (p2 == null ? "" : p2);
PooledPBEStringEncryptor enc = new PooledPBEStringEncryptor();
SimpleStringPBEConfig cfg = new SimpleStringPBEConfig();
cfg.setPassword(salt);
cfg.setAlgorithm("PBEWithHmacSHA512AndAES_256");
cfg.setIvGeneratorClassName("org.jasypt.iv.RandomIvGenerator");
cfg.setKeyObtentionIterations("1000");
cfg.setPoolSize("1");
cfg.setProviderName("SunJCE");
cfg.setSaltGeneratorClassName("org.jasypt.salt.RandomSaltGenerator");
cfg.setStringOutputType("base64");
enc.setConfig(cfg);
return enc;
}
}
```
**使用方式(務必加 `@Qualifier`)**:
```java
@Service
public class PartnerCommService {
private final StringEncryptor partner;
public PartnerCommService(@Qualifier("encryptorBean") StringEncryptor partner) {
this.partner = partner;
}
public String encryptForPartner(String plain) { return partner.encrypt(plain); }
public String decryptFromPartner(String cipher) { return partner.decrypt(cipher); }
}
```
---
## 4) 常見衝突與排除
- **衝突 1:組態解密被導向 `encryptorBean`**
檢查是否在註解或屬性把 `encryptorBean` 指定為解組態用(例如 `@EnableEncryptableProperties(encryptorBean = "encryptorBean")` 或 `jasypt.encryptor.bean=encryptorBean`)。**移除這些指定**。
- **衝突 2:注入歧義(found 2 beans of type StringEncryptor)**
避免裸 `@Autowired StringEncryptor`。凡要用協力廠商版本,一律 `@Qualifier("encryptorBean")`;需要的話也可把對外服務包成自己的 Service,別直接注入 `StringEncryptor`。
---
## 5) 可選:若也想在程式碼中取得 legacy Encryptor
一般不需要(starter 會內部用 legacy 參數解組態)。若確實需要,可加:
```java
@Configuration
class LegacyExposureConfig {
@Bean("legacyEncryptor")
public StringEncryptor legacyBean(@Value("${jasypt.encryptor.password}") String pw) {
PooledPBEStringEncryptor enc = new PooledPBEStringEncryptor();
SimpleStringPBEConfig cfg = new SimpleStringPBEConfig();
cfg.setPassword(pw);
cfg.setAlgorithm("PBEWithMD5AndDES");
cfg.setIvGeneratorClassName("org.jasypt.iv.NoIvGenerator");
cfg.setPoolSize("1");
cfg.setStringOutputType("base64");
enc.setConfig(cfg);
return enc;
}
}
```
前提:所有注入點都用 `@Qualifier("legacyEncryptor")`,避免與 `encryptorBean` 產生歧義。
---
## 6) 驗證清單
- 啟動時 **不要**有 `expected single matching bean but found 2` 的錯誤。
- `application.yml` 內 `ENC(...)` 能順利還原(印出資料源連線、或以測試程式驗證)。
- 協力廠商的收送資料可用 `@Qualifier("encryptorBean")` 成功加解密。
- `@EnableEncryptableProperties` 沒有把 `encryptorBean` 指定為解組態用。
---
## 7) 安全與後續
- legacy(MD5+DES + NoIv)僅做相容使用;對外通訊與新資料請一律走協力廠商 AES-256 版本。
- 若未來要把組態也升級為 AES-256,可逐步把密文轉換,再改用路由/新 Bean 名處理。
IyBTcHJpbmcgQm9vdCArIEphc3lwdCDmnIDntYLmlbTlkIjvvJoKCiMjIGBFTkMoLi4uKWAg55SoIGxlZ2Fjee+8iE1ENStERVPvvInvvIzljZTlipvlu6DllYbpgJroqIrnlKggYGVuY3J5cHRvckJlYW5gCgoKCj4g55uu5qiZ77yaCgo+IC0gYGFwcGxpY2F0aW9uLnltbGAg55qEIGBFTkMoLi4uKWAg55SxICoqbGVnYWN5KirvvIhgUEJFV2l0aE1ENUFuZERFU2AgKyBgTm9JdmAgKyBgdnU0d2ovM2DvvInoh6rli5Xop6Plr4bjgIIKCj4gLSDljZTlipvlu6DllYbmj5DkvpvnmoQgKipgQEJlYW4oImVuY3J5cHRvckJlYW4iKWAqKiDlg4XkvpsqKueoi+W8j+eivCoq5omL5YuV5Yqg6Kej5a+G6YCa6KiK6LOH5paZ77yM5LiN5Y+D6IiH57WE5oWL6Ieq5YuV6Kej5a+G44CCCgoKCi0tLQoKCgojIyAxKSBhcHBsaWNhdGlvbi55bWzvvIjlj6rmlL7lj4PmlbjvvIzkuI3mjIflrpogYmVhbiDlkI3vvIkKCmBgYHlhbWwKCmphc3lwdDoKCiAgZW5jcnlwdG9yOgoKICAgIGFsZ29yaXRobTogUEJFV2l0aE1ENUFuZERFUwoKICAgIGl2LWdlbmVyYXRvci1jbGFzc25hbWU6IG9yZy5qYXN5cHQuaXYuTm9JdkdlbmVyYXRvcgoKICAgIHBhc3N3b3JkOiB2dTR3ai8zCgojIOS4jeimgeioreWumiBqYXN5cHQuZW5jcnlwdG9yLmJlYW4KCmBgYAoK6Kqq5piO77yaYHVsaXNlc2JvY2NoaW8vamFzeXB0LXNwcmluZy1ib290YCDmnIPkvp3kuIrov7Dlj4Pmlbjoh6rli5Xop6MgYEVOQyguLi4pYO+8jOeEoemcgOaMh+WumiBCZWFuIOWQjeOAggoKCgotLS0KCgoKIyMgMikg5ZWf55So6Ki76Kej6Kit5a6a77yI6YG/5YWN5oyH5a6aIGVuY3J5cHRvckJlYW7vvIkKCuiLpeWwiOahiOacieS9v+eUqCBgQEVuYWJsZUVuY3J5cHRhYmxlUHJvcGVydGllc2DvvIjmiJbkvaDoqqrnmoQgYEBlbmFibGVFbmNyeXB0YO+8ie+8jCoq5LiN6KaBKirmjIflkJHljZTlipvlu6DllYbnmoQgQmVhbiDlkI3jgIIKCmBgYGphdmEKCi8vIOato+eiuu+8muiukyBzdGFydGVyIOS+nSBhcHBsaWNhdGlvbi55bWwg55qE5Y+D5pW46JmV55CGCgpARW5hYmxlRW5jcnlwdGFibGVQcm9wZXJ0aWVzCgoKCi8vIOmMr+iqpOekuuS+i++8iOacg+aKiue1hOaFi+ino+WvhuS6pOe1puWNlOWKm+W7oOWVhiBlbmNyeXB0b3JCZWFu77yM6YCg5oiQ6KGd56qB77yJ77yaCgovLyBARW5hYmxlRW5jcnlwdGFibGVQcm9wZXJ0aWVzKGVuY3J5cHRvckJlYW4gPSAiZW5jcnlwdG9yQmVhbiIpCgpgYGAKCgoKLS0tCgoKCiMjIDMpIOWNlOWKm+W7oOWVhuaPkOS+m+eahCBFbmNyeXB0b3LvvIjpoa/lvI8gQmVhbiDlkI3vvIzlj6rntabnqIvlvI/norznlKjvvIkKCmBgYGphdmEKCkBDb25maWd1cmF0aW9uCgpwdWJsaWMgY2xhc3MgUGFydG5lckNyeXB0b0NvbmZpZyB7CgoKCiAgICBAQmVhbigiZW5jcnlwdG9yQmVhbiIpIC8vIOWNlOWKm+W7oOWVhuaMh+WumueahCBCZWFuIOWQjQoKICAgIHB1YmxpYyBTdHJpbmdFbmNyeXB0b3IgdmVuZG9yRW5jcnlwdG9yKCkgewoKICAgICAgICBTdHJpbmcgcDEgPSBTeXN0ZW0uZ2V0UHJvcGVydHkoIlBfRU5WXzEiKTsKCiAgICAgICAgU3RyaW5nIHAyID0gU3lzdGVtLmdldFByb3BlcnR5KCJQX0VOVl8yIik7CgogICAgICAgIGlmIChwMSA9PSBudWxsIHx8IHAxLmlzQmxhbmsoKSB8fCAibnVsbCIuZXF1YWxzSWdub3JlQ2FzZShwMSkKCiAgICAgICAgIHx8IHAyID09IG51bGwgfHwgcDIuaXNCbGFuaygpIHx8ICJudWxsIi5lcXVhbHNJZ25vcmVDYXNlKHAyKSkgewoKICAgICAgICAgICAgcDEgPSBTeXN0ZW0uZ2V0ZW52KCJQX0VOVl8xIik7CgogICAgICAgICAgICBwMiA9IFN5c3RlbS5nZXRlbnYoIlBfRU5WXzIiKTsKCiAgICAgICAgfQoKICAgICAgICBTdHJpbmcgc2FsdCA9IChwMSA9PSBudWxsID8gIiIgOiBwMSkgKyAocDIgPT0gbnVsbCA/ICIiIDogcDIpOwoKCgogICAgICAgIFBvb2xlZFBCRVN0cmluZ0VuY3J5cHRvciBlbmMgPSBuZXcgUG9vbGVkUEJFU3RyaW5nRW5jcnlwdG9yKCk7CgogICAgICAgIFNpbXBsZVN0cmluZ1BCRUNvbmZpZyBjZmcgPSBuZXcgU2ltcGxlU3RyaW5nUEJFQ29uZmlnKCk7CgogICAgICAgIGNmZy5zZXRQYXNzd29yZChzYWx0KTsKCiAgICAgICAgY2ZnLnNldEFsZ29yaXRobSgiUEJFV2l0aEhtYWNTSEE1MTJBbmRBRVNfMjU2Iik7CgogICAgICAgIGNmZy5zZXRJdkdlbmVyYXRvckNsYXNzTmFtZSgib3JnLmphc3lwdC5pdi5SYW5kb21JdkdlbmVyYXRvciIpOwoKICAgICAgICBjZmcuc2V0S2V5T2J0ZW50aW9uSXRlcmF0aW9ucygiMTAwMCIpOwoKICAgICAgICBjZmcuc2V0UG9vbFNpemUoIjEiKTsKCiAgICAgICAgY2ZnLnNldFByb3ZpZGVyTmFtZSgiU3VuSkNFIik7CgogICAgICAgIGNmZy5zZXRTYWx0R2VuZXJhdG9yQ2xhc3NOYW1lKCJvcmcuamFzeXB0LnNhbHQuUmFuZG9tU2FsdEdlbmVyYXRvciIpOwoKICAgICAgICBjZmcuc2V0U3RyaW5nT3V0cHV0VHlwZSgiYmFzZTY0Iik7CgogICAgICAgIGVuYy5zZXRDb25maWcoY2ZnKTsKCiAgICAgICAgcmV0dXJuIGVuYzsKCiAgICB9Cgp9CgpgYGAKCioq5L2/55So5pa55byP77yI5YuZ5b+F5YqgIGBAUXVhbGlmaWVyYO+8iSoq77yaCgpgYGBqYXZhCgpAU2VydmljZQoKcHVibGljIGNsYXNzIFBhcnRuZXJDb21tU2VydmljZSB7CgogICAgcHJpdmF0ZSBmaW5hbCBTdHJpbmdFbmNyeXB0b3IgcGFydG5lcjsKCgoKICAgIHB1YmxpYyBQYXJ0bmVyQ29tbVNlcnZpY2UoQFF1YWxpZmllcigiZW5jcnlwdG9yQmVhbiIpIFN0cmluZ0VuY3J5cHRvciBwYXJ0bmVyKSB7CgogICAgICAgIHRoaXMucGFydG5lciA9IHBhcnRuZXI7CgogICAgfQoKCgogICAgcHVibGljIFN0cmluZyBlbmNyeXB0Rm9yUGFydG5lcihTdHJpbmcgcGxhaW4pIHsgcmV0dXJuIHBhcnRuZXIuZW5jcnlwdChwbGFpbik7IH0KCiAgICBwdWJsaWMgU3RyaW5nIGRlY3J5cHRGcm9tUGFydG5lcihTdHJpbmcgY2lwaGVyKSB7IHJldHVybiBwYXJ0bmVyLmRlY3J5cHQoY2lwaGVyKTsgfQoKfQoKYGBgCgoKCi0tLQoKCgojIyA0KSDluLjopovooZ3nqoHoiIfmjpLpmaQKCi0gKirooZ3nqoEgMe+8mue1hOaFi+ino+Wvhuiiq+WwjuWQkSBgZW5jcnlwdG9yQmVhbmAqKiAgCgogIOaqouafpeaYr+WQpuWcqOiou+ino+aIluWxrOaAp+aKiiBgZW5jcnlwdG9yQmVhbmAg5oyH5a6a54K66Kej57WE5oWL55So77yI5L6L5aaCIGBARW5hYmxlRW5jcnlwdGFibGVQcm9wZXJ0aWVzKGVuY3J5cHRvckJlYW4gPSAiZW5jcnlwdG9yQmVhbiIpYCDmiJYgYGphc3lwdC5lbmNyeXB0b3IuYmVhbj1lbmNyeXB0b3JCZWFuYO+8ieOAgioq56e76Zmk6YCZ5Lqb5oyH5a6aKirjgIIKCi0gKirooZ3nqoEgMu+8muazqOWFpeatp+e+qe+8iGZvdW5kIDIgYmVhbnMgb2YgdHlwZSBTdHJpbmdFbmNyeXB0b3LvvIkqKiAgCgogIOmBv+WFjeijuCBgQEF1dG93aXJlZCBTdHJpbmdFbmNyeXB0b3Jg44CC5Yeh6KaB55So5Y2U5Yqb5bug5ZWG54mI5pys77yM5LiA5b6LIGBAUXVhbGlmaWVyKCJlbmNyeXB0b3JCZWFuIilg77yb6ZyA6KaB55qE6Kmx5Lmf5Y+v5oqK5bCN5aSW5pyN5YuZ5YyF5oiQ6Ieq5bex55qEIFNlcnZpY2XvvIzliKXnm7TmjqXms6jlhaUgYFN0cmluZ0VuY3J5cHRvcmDjgIIKCgoKLS0tCgoKCiMjIDUpIOWPr+mBuO+8muiLpeS5n+aDs+WcqOeoi+W8j+eivOS4reWPluW+lyBsZWdhY3kgRW5jcnlwdG9yCgrkuIDoiKzkuI3pnIDopoHvvIhzdGFydGVyIOacg+WFp+mDqOeUqCBsZWdhY3kg5Y+D5pW46Kej57WE5oWL77yJ44CC6Iul56K65a+m6ZyA6KaB77yM5Y+v5Yqg77yaCgpgYGBqYXZhCgpAQ29uZmlndXJhdGlvbgoKY2xhc3MgTGVnYWN5RXhwb3N1cmVDb25maWcgewoKICAgIEBCZWFuKCJsZWdhY3lFbmNyeXB0b3IiKQoKICAgIHB1YmxpYyBTdHJpbmdFbmNyeXB0b3IgbGVnYWN5QmVhbihAVmFsdWUoIiR7amFzeXB0LmVuY3J5cHRvci5wYXNzd29yZH0iKSBTdHJpbmcgcHcpIHsKCiAgICAgICAgUG9vbGVkUEJFU3RyaW5nRW5jcnlwdG9yIGVuYyA9IG5ldyBQb29sZWRQQkVTdHJpbmdFbmNyeXB0b3IoKTsKCiAgICAgICAgU2ltcGxlU3RyaW5nUEJFQ29uZmlnIGNmZyA9IG5ldyBTaW1wbGVTdHJpbmdQQkVDb25maWcoKTsKCiAgICAgICAgY2ZnLnNldFBhc3N3b3JkKHB3KTsKCiAgICAgICAgY2ZnLnNldEFsZ29yaXRobSgiUEJFV2l0aE1ENUFuZERFUyIpOwoKICAgICAgICBjZmcuc2V0SXZHZW5lcmF0b3JDbGFzc05hbWUoIm9yZy5qYXN5cHQuaXYuTm9JdkdlbmVyYXRvciIpOwoKICAgICAgICBjZmcuc2V0UG9vbFNpemUoIjEiKTsKCiAgICAgICAgY2ZnLnNldFN0cmluZ091dHB1dFR5cGUoImJhc2U2NCIpOwoKICAgICAgICBlbmMuc2V0Q29uZmlnKGNmZyk7CgogICAgICAgIHJldHVybiBlbmM7CgogICAgfQoKfQoKYGBgCgrliY3mj5DvvJrmiYDmnInms6jlhaXpu57pg73nlKggYEBRdWFsaWZpZXIoImxlZ2FjeUVuY3J5cHRvciIpYO+8jOmBv+WFjeiIhyBgZW5jcnlwdG9yQmVhbmAg55Si55Sf5q2n576p44CCCgoKCi0tLQoKCgojIyA2KSDpqZforYnmuIXllq4KCi0g5ZWf5YuV5pmCICoq5LiN6KaBKirmnIkgYGV4cGVjdGVkIHNpbmdsZSBtYXRjaGluZyBiZWFuIGJ1dCBmb3VuZCAyYCDnmoTpjK/oqqTjgIIKCi0gYGFwcGxpY2F0aW9uLnltbGAg5YWnIGBFTkMoLi4uKWAg6IO96aCG5Yip6YKE5Y6f77yI5Y2w5Ye66LOH5paZ5rqQ6YCj57ea44CB5oiW5Lul5ris6Kmm56iL5byP6amX6K2J77yJ44CCCgotIOWNlOWKm+W7oOWVhueahOaUtumAgeizh+aWmeWPr+eUqCBgQFF1YWxpZmllcigiZW5jcnlwdG9yQmVhbiIpYCDmiJDlip/liqDop6Plr4bjgIIKCi0gYEBFbmFibGVFbmNyeXB0YWJsZVByb3BlcnRpZXNgIOaykuacieaKiiBgZW5jcnlwdG9yQmVhbmAg5oyH5a6a54K66Kej57WE5oWL55So44CCCgoKCi0tLQoKCgojIyA3KSDlronlhajoiIflvoznuowKCi0gbGVnYWN577yITUQ1K0RFUyArIE5vSXbvvInlg4XlgZrnm7jlrrnkvb/nlKjvvJvlsI3lpJbpgJroqIroiIfmlrDos4fmlpnoq4vkuIDlvovotbDljZTlipvlu6DllYYgQUVTLTI1NiDniYjmnKzjgIIKCi0g6Iul5pyq5L6G6KaB5oqK57WE5oWL5Lmf5Y2H57Sa54K6IEFFUy0yNTbvvIzlj6/pgJDmraXmiorlr4bmlofovYnmj5vvvIzlho3mlLnnlKjot6/nlLEv5pawIEJlYW4g5ZCN6JmV55CG44CCCg==