返回列表 上一筆 下一筆

📄 資料內容

# Spring Boot + Jasypt:同時使用兩組加解密設定(舊/新)實作指南



> 語言:繁體中文

> 目的:同一個 Spring Boot 專案中,同時支援 **舊**(`PBEWithMD5AndDES`、無 IV)與 **新**(`PBEWithHmacSHA512AndAES_256`、隨機 IV)兩套 Jasypt 設定。舊組態僅做相容;新資料一律用 AES-256。



---



## 目標

1. 程式碼中可以選擇使用「舊」或「新」加解密。

2. `application.yml` 等組態可同時支援兩種加密字串(便於平滑換代)。



---



## 實作總覽(兩層)

- **A. 程式碼手動加解密**:宣告兩個 `StringEncryptor` Bean,使用 `@Qualifier` 精準注入。

- **B. 組態自動解密**:提供「路由型」解密(支援 `ENC_NEW(...)`、`ENC_OLD(...)` 與相容用 `ENC(...)`)。



---



## A. 程式碼中手動加解密(兩個 Encryptor Bean)



`JasyptEncryptorsConfig.java`:



```java

import org.jasypt.encryption.StringEncryptor;

import org.jasypt.encryption.pbe.PooledPBEStringEncryptor;

import org.jasypt.encryption.pbe.config.SimpleStringPBEConfig;

import org.springframework.beans.factory.annotation.Value;

import org.springframework.context.annotation.Bean;

import org.springframework.context.annotation.Configuration;



@Configuration

public class JasyptEncryptorsConfig {



    // 舊:僅為相容(弱加密,無 IV)

    @Bean("legacyEncryptor")

    public StringEncryptor legacyEncryptor(

            @Value("${jasypt.encryptor.password}") String legacyPassword) {

        PooledPBEStringEncryptor enc = new PooledPBEStringEncryptor();

        SimpleStringPBEConfig cfg = new SimpleStringPBEConfig();

        cfg.setPassword(legacyPassword);

        cfg.setAlgorithm("PBEWithMD5AndDES");

        cfg.setIvGeneratorClassName("org.jasypt.iv.NoIvGenerator");

        cfg.setPoolSize("1");

        cfg.setStringOutputType("base64");

        enc.setConfig(cfg);

        return enc;

    }



    // 新:協力商 AES-256 版本(安全組態)

    @Bean("encryptorBean")

    public StringEncryptor encryptorBean() {

        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;

    }

}

```



`CryptoService.java`:



```java

import org.jasypt.encryption.StringEncryptor;

import org.springframework.beans.factory.annotation.Qualifier;

import org.springframework.stereotype.Service;



@Service

public class CryptoService {



    private final StringEncryptor legacy;

    private final StringEncryptor modern;



    public CryptoService(@Qualifier("legacyEncryptor") StringEncryptor legacy,

                         @Qualifier("encryptorBean") StringEncryptor modern) {

        this.legacy = legacy;

        this.modern = modern;

    }



    public String encryptNew(String plain) { return modern.encrypt(plain); }

    public String decryptNew(String cipher) { return modern.decrypt(cipher); }



    public String decryptLegacy(String cipher) { return legacy.decrypt(cipher); }



    // 簡易自動判斷:先試新,不行再回退舊

    public String smartDecrypt(String cipher) {

        try { return modern.decrypt(cipher); }

        catch (Exception ignore) { /* fall through */ }

        return legacy.decrypt(cipher);

    }

}

```



---



## B. 組態層的自動解密(Routing + 多前綴)

**為何需要?** `ulisesbocchio/jasypt-spring-boot` 預設一次只會用一個 `jasypt.encryptor.bean` 去解 `ENC(...)`。若要同時支援兩種加密格式,建議引入自訂「偵測器/解析器」或「路由 Encryptor」,用**前綴**切換:



- `ENC_NEW(...)` → 用 **新** encryptor(AES-256)。

- `ENC_OLD(...)` → 用 **舊** encryptor(MD5+DES)。

- `ENC(...)` → 相容保留:先試新、再回退舊。



`JasyptRoutingConfig.java`:



```java

import com.ulisesbocchio.jasyptspringboot.detector.EncryptablePropertyDetector;

import com.ulisesbocchio.jasyptspringboot.resolver.EncryptablePropertyResolver;

import org.jasypt.encryption.StringEncryptor;

import org.springframework.beans.factory.annotation.Qualifier;

import org.springframework.context.annotation.Bean;

import org.springframework.context.annotation.Configuration;



@Configuration

public class JasyptRoutingConfig {



    @Bean("routingEncryptor")

    public StringEncryptor routingEncryptor(

            @Qualifier("legacyEncryptor") StringEncryptor legacy,

            @Qualifier("encryptorBean") StringEncryptor modern) {

        // 供需要時直接注入;Resolver 也會自行分流

        return new StringEncryptor() {

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

            @Override public String decrypt(String encryptedMessage) {

                try { return modern.decrypt(encryptedMessage); }

                catch (Exception ignore) {}

                return legacy.decrypt(encryptedMessage);

            }

        };

    }



    @Bean

    public EncryptablePropertyDetector encryptablePropertyDetector() {

        return value -> value != null &&

                (value.startsWith("ENC_NEW(") || value.startsWith("ENC_OLD(") || value.startsWith("ENC("));

    }



    @Bean

    public EncryptablePropertyResolver encryptablePropertyResolver(

            @Qualifier("legacyEncryptor") StringEncryptor legacy,

            @Qualifier("encryptorBean") StringEncryptor modern) {



        return value -> {

            if (value == null) return null;



            if (value.startsWith("ENC_NEW(") && value.endsWith(")")) {

                String body = value.substring("ENC_NEW(".length(), value.length() - 1);

                return modern.decrypt(body);

            }

            if (value.startsWith("ENC_OLD(") && value.endsWith(")")) {

                String body = value.substring("ENC_OLD(".length(), value.length() - 1);

                return legacy.decrypt(body);

            }

            if (value.startsWith("ENC(") && value.endsWith(")")) {

                String body = value.substring("ENC(".length(), value.length() - 1);

                try { return modern.decrypt(body); }

                catch (Exception ignore) { return legacy.decrypt(body); }

            }

            return value;

        };

    }

}

```



`application.yml`:



```yaml

jasypt:

  encryptor:

    bean: routingEncryptor   # 讓 Resolver/Detector 生效,並提供 routing 能力



spring:

  datasource:

    password: "ENC_NEW(.....)"   # 新格式(AES-256)



legacy:

  token: "ENC_OLD(.....)"        # 舊格式(MD5+DES)



other:

  secret: "ENC(.....)"           # 歷史相容:未標示新舊者

```



---



## C. 產出與轉換策略(實務)

- **新資料一律用 AES-256(`encryptorBean`)產生**;避免再寫入 MD5+DES。

- 舊資料保留解密能力一段時間(`legacyEncryptor`),逐步轉檔或在讀取時即時以新格式重寫。

- 在組態中改用 `ENC_NEW(...)`;只在必要時保留 `ENC_OLD(...)`。



---



## D. 常見落坑

- 原始片段常見誤植(`sait` → `salt`、`setAlgorithm` 拼寫、全形符號),範例已修正。

- `PBEWithMD5AndDES` + `NoIvGenerator` 安全性很弱;僅限相容期使用。

- `P_ENV_1` / `P_ENV_2` 取自 **JVM 系統屬性** 或 **環境變數** 時,請處理空字串與 `"null"`。

- 舊 JDK 需注意 AES-256 的政策檔;現代 JDK 已預設開啟,但仍建議在所有環境驗證。



---



## E. 最小可用配置清單

1. 宣告 `@Bean("legacyEncryptor")` 與 `@Bean("encryptorBean")`。

2. 若組態要同時解兩種格式:加上自訂 `EncryptablePropertyDetector` / `EncryptablePropertyResolver`,支援 `ENC_NEW(...)`、`ENC_OLD(...)`、`ENC(...)`。

3. 程式碼層以 `@Qualifier` 指定使用哪一組 Encryptor。



---



## F. 簡易測試(產生/驗證密文)



`JasyptQuickTest.java`:



```java

public class JasyptQuickTest {



    public static void main(String[] args) {

        // 假裝已從 Spring 取出兩個 Bean:legacy 與 modern

        var legacy = new org.jasypt.encryption.pbe.StandardPBEStringEncryptor();

        legacy.setPassword("vu4wj/3"); // 舊密碼:來自 application 設定

        legacy.setAlgorithm("PBEWithMD5AndDES");



        var modern = new org.jasypt.encryption.pbe.StandardPBEStringEncryptor();

        modern.setPassword(System.getenv("P_ENV_1") + System.getenv("P_ENV_2"));

        modern.setAlgorithm("PBEWithHmacSHA512AndAES_256");



        String plain = "s3cr3t-P@ss!";



        String oldCipher = legacy.encrypt(plain);

        String newCipher = modern.encrypt(plain);



        System.out.println("ENC_OLD(" + oldCipher + ")");

        System.out.println("ENC_NEW(" + newCipher + ")");



        // 驗證

        System.out.println("legacy -> " + legacy.decrypt(oldCipher));

        System.out.println("modern -> " + modern.decrypt(newCipher));

    }

}

```



> 提示:正式專案請以 Spring 取得 Bean 進行測試;此處為最小可行示意。



---



## 版控建議

- 先導入「路由」與雙 Encryptor,確保服務可同時解兩種格式。

- 撰寫資料轉換腳本(批次或讀取即轉),逐步消除 `ENC_OLD(...)`。

- 清查完畢後,**移除舊 Encryptor** 與 `ENC_OLD(...)` 支援,降低攻擊面。



---



## 授權與安全

- 本指南僅示範 Jasypt 用法,不包含第三方授權檔。

- 實務上請搭配祕密管理(如 KMS、Vault、Kubernetes Secret)取代硬編碼密鑰。

🔐 Base64 編碼內容

IyBTcHJpbmcgQm9vdCArIEphc3lwdO+8muWQjOaZguS9v+eUqOWFqee1hOWKoOino+WvhuioreWumu+8iOiIiu+8j+aWsO+8ieWvpuS9nOaMh+WNlwoKCgo+IOiqnuiogO+8mue5gemrlOS4reaWhyAgCgo+IOebrueahO+8muWQjOS4gOWAiyBTcHJpbmcgQm9vdCDlsIjmoYjkuK3vvIzlkIzmmYLmlK/mj7QgKiroiIoqKu+8iGBQQkVXaXRoTUQ1QW5kREVTYOOAgeeEoSBJVu+8ieiIhyAqKuaWsCoq77yIYFBCRVdpdGhIbWFjU0hBNTEyQW5kQUVTXzI1NmDjgIHpmqjmqZ8gSVbvvInlhanlpZcgSmFzeXB0IOioreWumuOAguiIiue1hOaFi+WDheWBmuebuOWuue+8m+aWsOizh+aWmeS4gOW+i+eUqCBBRVMtMjU244CCCgoKCi0tLQoKCgojIyDnm67mqJkKCjEuIOeoi+W8j+eivOS4reWPr+S7pemBuOaTh+S9v+eUqOOAjOiIiuOAjeaIluOAjOaWsOOAjeWKoOino+WvhuOAgiAgCgoyLiBgYXBwbGljYXRpb24ueW1sYCDnrYnntYTmhYvlj6/lkIzmmYLmlK/mj7TlhannqK7liqDlr4blrZfkuLLvvIjkvr/mlrzlubPmu5Hmj5vku6PvvInjgIIKCgoKLS0tCgoKCiMjIOWvpuS9nOe4veimve+8iOWFqeWxpO+8iQoKLSAqKkEuIOeoi+W8j+eivOaJi+WLleWKoOino+Wvhioq77ya5a6j5ZGK5YWp5YCLIGBTdHJpbmdFbmNyeXB0b3JgIEJlYW7vvIzkvb/nlKggYEBRdWFsaWZpZXJgIOeyvua6luazqOWFpeOAgiAgCgotICoqQi4g57WE5oWL6Ieq5YuV6Kej5a+GKirvvJrmj5DkvpvjgIzot6/nlLHlnovjgI3op6Plr4bvvIjmlK/mj7QgYEVOQ19ORVcoLi4uKWDjgIFgRU5DX09MRCguLi4pYCDoiIfnm7jlrrnnlKggYEVOQyguLi4pYO+8ieOAggoKCgotLS0KCgoKIyMgQS4g56iL5byP56K85Lit5omL5YuV5Yqg6Kej5a+G77yI5YWp5YCLIEVuY3J5cHRvciBCZWFu77yJCgoKCmBKYXN5cHRFbmNyeXB0b3JzQ29uZmlnLmphdmFg77yaCgoKCmBgYGphdmEKCmltcG9ydCBvcmcuamFzeXB0LmVuY3J5cHRpb24uU3RyaW5nRW5jcnlwdG9yOwoKaW1wb3J0IG9yZy5qYXN5cHQuZW5jcnlwdGlvbi5wYmUuUG9vbGVkUEJFU3RyaW5nRW5jcnlwdG9yOwoKaW1wb3J0IG9yZy5qYXN5cHQuZW5jcnlwdGlvbi5wYmUuY29uZmlnLlNpbXBsZVN0cmluZ1BCRUNvbmZpZzsKCmltcG9ydCBvcmcuc3ByaW5nZnJhbWV3b3JrLmJlYW5zLmZhY3RvcnkuYW5ub3RhdGlvbi5WYWx1ZTsKCmltcG9ydCBvcmcuc3ByaW5nZnJhbWV3b3JrLmNvbnRleHQuYW5ub3RhdGlvbi5CZWFuOwoKaW1wb3J0IG9yZy5zcHJpbmdmcmFtZXdvcmsuY29udGV4dC5hbm5vdGF0aW9uLkNvbmZpZ3VyYXRpb247CgoKCkBDb25maWd1cmF0aW9uCgpwdWJsaWMgY2xhc3MgSmFzeXB0RW5jcnlwdG9yc0NvbmZpZyB7CgoKCiAgICAvLyDoiIrvvJrlg4Xngrrnm7jlrrnvvIjlvLHliqDlr4bvvIznhKEgSVbvvIkKCiAgICBAQmVhbigibGVnYWN5RW5jcnlwdG9yIikKCiAgICBwdWJsaWMgU3RyaW5nRW5jcnlwdG9yIGxlZ2FjeUVuY3J5cHRvcigKCiAgICAgICAgICAgIEBWYWx1ZSgiJHtqYXN5cHQuZW5jcnlwdG9yLnBhc3N3b3JkfSIpIFN0cmluZyBsZWdhY3lQYXNzd29yZCkgewoKICAgICAgICBQb29sZWRQQkVTdHJpbmdFbmNyeXB0b3IgZW5jID0gbmV3IFBvb2xlZFBCRVN0cmluZ0VuY3J5cHRvcigpOwoKICAgICAgICBTaW1wbGVTdHJpbmdQQkVDb25maWcgY2ZnID0gbmV3IFNpbXBsZVN0cmluZ1BCRUNvbmZpZygpOwoKICAgICAgICBjZmcuc2V0UGFzc3dvcmQobGVnYWN5UGFzc3dvcmQpOwoKICAgICAgICBjZmcuc2V0QWxnb3JpdGhtKCJQQkVXaXRoTUQ1QW5kREVTIik7CgogICAgICAgIGNmZy5zZXRJdkdlbmVyYXRvckNsYXNzTmFtZSgib3JnLmphc3lwdC5pdi5Ob0l2R2VuZXJhdG9yIik7CgogICAgICAgIGNmZy5zZXRQb29sU2l6ZSgiMSIpOwoKICAgICAgICBjZmcuc2V0U3RyaW5nT3V0cHV0VHlwZSgiYmFzZTY0Iik7CgogICAgICAgIGVuYy5zZXRDb25maWcoY2ZnKTsKCiAgICAgICAgcmV0dXJuIGVuYzsKCiAgICB9CgoKCiAgICAvLyDmlrDvvJrljZTlipvllYYgQUVTLTI1NiDniYjmnKzvvIjlronlhajntYTmhYvvvIkKCiAgICBAQmVhbigiZW5jcnlwdG9yQmVhbiIpCgogICAgcHVibGljIFN0cmluZ0VuY3J5cHRvciBlbmNyeXB0b3JCZWFuKCkgewoKICAgICAgICBTdHJpbmcgcDEgPSBTeXN0ZW0uZ2V0UHJvcGVydHkoIlBfRU5WXzEiKTsKCiAgICAgICAgU3RyaW5nIHAyID0gU3lzdGVtLmdldFByb3BlcnR5KCJQX0VOVl8yIik7CgogICAgICAgIGlmIChwMSA9PSBudWxsIHx8IHAxLmlzQmxhbmsoKSB8fCAibnVsbCIuZXF1YWxzSWdub3JlQ2FzZShwMSkKCiAgICAgICAgIHx8IHAyID09IG51bGwgfHwgcDIuaXNCbGFuaygpIHx8ICJudWxsIi5lcXVhbHNJZ25vcmVDYXNlKHAyKSkgewoKICAgICAgICAgICAgcDEgPSBTeXN0ZW0uZ2V0ZW52KCJQX0VOVl8xIik7CgogICAgICAgICAgICBwMiA9IFN5c3RlbS5nZXRlbnYoIlBfRU5WXzIiKTsKCiAgICAgICAgfQoKICAgICAgICBTdHJpbmcgc2FsdCA9IChwMSA9PSBudWxsID8gIiIgOiBwMSkgKyAocDIgPT0gbnVsbCA/ICIiIDogcDIpOwoKCgogICAgICAgIFBvb2xlZFBCRVN0cmluZ0VuY3J5cHRvciBlbmMgPSBuZXcgUG9vbGVkUEJFU3RyaW5nRW5jcnlwdG9yKCk7CgogICAgICAgIFNpbXBsZVN0cmluZ1BCRUNvbmZpZyBjZmcgPSBuZXcgU2ltcGxlU3RyaW5nUEJFQ29uZmlnKCk7CgogICAgICAgIGNmZy5zZXRQYXNzd29yZChzYWx0KTsKCiAgICAgICAgY2ZnLnNldEFsZ29yaXRobSgiUEJFV2l0aEhtYWNTSEE1MTJBbmRBRVNfMjU2Iik7CgogICAgICAgIGNmZy5zZXRJdkdlbmVyYXRvckNsYXNzTmFtZSgib3JnLmphc3lwdC5pdi5SYW5kb21JdkdlbmVyYXRvciIpOwoKICAgICAgICBjZmcuc2V0S2V5T2J0ZW50aW9uSXRlcmF0aW9ucygiMTAwMCIpOwoKICAgICAgICBjZmcuc2V0UG9vbFNpemUoIjEiKTsKCiAgICAgICAgY2ZnLnNldFByb3ZpZGVyTmFtZSgiU3VuSkNFIik7CgogICAgICAgIGNmZy5zZXRTYWx0R2VuZXJhdG9yQ2xhc3NOYW1lKCJvcmcuamFzeXB0LnNhbHQuUmFuZG9tU2FsdEdlbmVyYXRvciIpOwoKICAgICAgICBjZmcuc2V0U3RyaW5nT3V0cHV0VHlwZSgiYmFzZTY0Iik7CgogICAgICAgIGVuYy5zZXRDb25maWcoY2ZnKTsKCiAgICAgICAgcmV0dXJuIGVuYzsKCiAgICB9Cgp9CgpgYGAKCgoKYENyeXB0b1NlcnZpY2UuamF2YWDvvJoKCgoKYGBgamF2YQoKaW1wb3J0IG9yZy5qYXN5cHQuZW5jcnlwdGlvbi5TdHJpbmdFbmNyeXB0b3I7CgppbXBvcnQgb3JnLnNwcmluZ2ZyYW1ld29yay5iZWFucy5mYWN0b3J5LmFubm90YXRpb24uUXVhbGlmaWVyOwoKaW1wb3J0IG9yZy5zcHJpbmdmcmFtZXdvcmsuc3RlcmVvdHlwZS5TZXJ2aWNlOwoKCgpAU2VydmljZQoKcHVibGljIGNsYXNzIENyeXB0b1NlcnZpY2UgewoKCgogICAgcHJpdmF0ZSBmaW5hbCBTdHJpbmdFbmNyeXB0b3IgbGVnYWN5OwoKICAgIHByaXZhdGUgZmluYWwgU3RyaW5nRW5jcnlwdG9yIG1vZGVybjsKCgoKICAgIHB1YmxpYyBDcnlwdG9TZXJ2aWNlKEBRdWFsaWZpZXIoImxlZ2FjeUVuY3J5cHRvciIpIFN0cmluZ0VuY3J5cHRvciBsZWdhY3ksCgogICAgICAgICAgICAgICAgICAgICAgICAgQFF1YWxpZmllcigiZW5jcnlwdG9yQmVhbiIpIFN0cmluZ0VuY3J5cHRvciBtb2Rlcm4pIHsKCiAgICAgICAgdGhpcy5sZWdhY3kgPSBsZWdhY3k7CgogICAgICAgIHRoaXMubW9kZXJuID0gbW9kZXJuOwoKICAgIH0KCgoKICAgIHB1YmxpYyBTdHJpbmcgZW5jcnlwdE5ldyhTdHJpbmcgcGxhaW4pIHsgcmV0dXJuIG1vZGVybi5lbmNyeXB0KHBsYWluKTsgfQoKICAgIHB1YmxpYyBTdHJpbmcgZGVjcnlwdE5ldyhTdHJpbmcgY2lwaGVyKSB7IHJldHVybiBtb2Rlcm4uZGVjcnlwdChjaXBoZXIpOyB9CgoKCiAgICBwdWJsaWMgU3RyaW5nIGRlY3J5cHRMZWdhY3koU3RyaW5nIGNpcGhlcikgeyByZXR1cm4gbGVnYWN5LmRlY3J5cHQoY2lwaGVyKTsgfQoKCgogICAgLy8g57Ch5piT6Ieq5YuV5Yik5pa377ya5YWI6Kmm5paw77yM5LiN6KGM5YaN5Zue6YCA6IiKCgogICAgcHVibGljIFN0cmluZyBzbWFydERlY3J5cHQoU3RyaW5nIGNpcGhlcikgewoKICAgICAgICB0cnkgeyByZXR1cm4gbW9kZXJuLmRlY3J5cHQoY2lwaGVyKTsgfQoKICAgICAgICBjYXRjaCAoRXhjZXB0aW9uIGlnbm9yZSkgeyAvKiBmYWxsIHRocm91Z2ggKi8gfQoKICAgICAgICByZXR1cm4gbGVnYWN5LmRlY3J5cHQoY2lwaGVyKTsKCiAgICB9Cgp9CgpgYGAKCgoKLS0tCgoKCiMjIEIuIOe1hOaFi+WxpOeahOiHquWLleino+Wvhu+8iFJvdXRpbmcgKyDlpJrliY3ntrTvvIkKCioq54K65L2V6ZyA6KaB77yfKiogYHVsaXNlc2JvY2NoaW8vamFzeXB0LXNwcmluZy1ib290YCDpoJDoqK3kuIDmrKHlj6rmnIPnlKjkuIDlgIsgYGphc3lwdC5lbmNyeXB0b3IuYmVhbmAg5Y676KejIGBFTkMoLi4uKWDjgILoi6XopoHlkIzmmYLmlK/mj7TlhannqK7liqDlr4bmoLzlvI/vvIzlu7rorbDlvJXlhaXoh6roqILjgIzlgbXmuKzlmagv6Kej5p6Q5Zmo44CN5oiW44CM6Lev55SxIEVuY3J5cHRvcuOAje+8jOeUqCoq5YmN57a0KirliIfmj5vvvJoKCgoKLSBgRU5DX05FVyguLi4pYCDihpIg55SoICoq5pawKiogZW5jcnlwdG9y77yIQUVTLTI1Nu+8ieOAggoKLSBgRU5DX09MRCguLi4pYCDihpIg55SoICoq6IiKKiogZW5jcnlwdG9y77yITUQ1K0RFU++8ieOAggoKLSBgRU5DKC4uLilgIOKGkiDnm7jlrrnkv53nlZnvvJrlhYjoqabmlrDjgIHlho3lm57pgIDoiIrjgIIKCgoKYEphc3lwdFJvdXRpbmdDb25maWcuamF2YWDvvJoKCgoKYGBgamF2YQoKaW1wb3J0IGNvbS51bGlzZXNib2NjaGlvLmphc3lwdHNwcmluZ2Jvb3QuZGV0ZWN0b3IuRW5jcnlwdGFibGVQcm9wZXJ0eURldGVjdG9yOwoKaW1wb3J0IGNvbS51bGlzZXNib2NjaGlvLmphc3lwdHNwcmluZ2Jvb3QucmVzb2x2ZXIuRW5jcnlwdGFibGVQcm9wZXJ0eVJlc29sdmVyOwoKaW1wb3J0IG9yZy5qYXN5cHQuZW5jcnlwdGlvbi5TdHJpbmdFbmNyeXB0b3I7CgppbXBvcnQgb3JnLnNwcmluZ2ZyYW1ld29yay5iZWFucy5mYWN0b3J5LmFubm90YXRpb24uUXVhbGlmaWVyOwoKaW1wb3J0IG9yZy5zcHJpbmdmcmFtZXdvcmsuY29udGV4dC5hbm5vdGF0aW9uLkJlYW47CgppbXBvcnQgb3JnLnNwcmluZ2ZyYW1ld29yay5jb250ZXh0LmFubm90YXRpb24uQ29uZmlndXJhdGlvbjsKCgoKQENvbmZpZ3VyYXRpb24KCnB1YmxpYyBjbGFzcyBKYXN5cHRSb3V0aW5nQ29uZmlnIHsKCgoKICAgIEBCZWFuKCJyb3V0aW5nRW5jcnlwdG9yIikKCiAgICBwdWJsaWMgU3RyaW5nRW5jcnlwdG9yIHJvdXRpbmdFbmNyeXB0b3IoCgogICAgICAgICAgICBAUXVhbGlmaWVyKCJsZWdhY3lFbmNyeXB0b3IiKSBTdHJpbmdFbmNyeXB0b3IgbGVnYWN5LAoKICAgICAgICAgICAgQFF1YWxpZmllcigiZW5jcnlwdG9yQmVhbiIpIFN0cmluZ0VuY3J5cHRvciBtb2Rlcm4pIHsKCiAgICAgICAgLy8g5L6b6ZyA6KaB5pmC55u05o6l5rOo5YWl77ybUmVzb2x2ZXIg5Lmf5pyD6Ieq6KGM5YiG5rWBCgogICAgICAgIHJldHVybiBuZXcgU3RyaW5nRW5jcnlwdG9yKCkgewoKICAgICAgICAgICAgQE92ZXJyaWRlIHB1YmxpYyBTdHJpbmcgZW5jcnlwdChTdHJpbmcgbWVzc2FnZSkgeyByZXR1cm4gbW9kZXJuLmVuY3J5cHQobWVzc2FnZSk7IH0KCiAgICAgICAgICAgIEBPdmVycmlkZSBwdWJsaWMgU3RyaW5nIGRlY3J5cHQoU3RyaW5nIGVuY3J5cHRlZE1lc3NhZ2UpIHsKCiAgICAgICAgICAgICAgICB0cnkgeyByZXR1cm4gbW9kZXJuLmRlY3J5cHQoZW5jcnlwdGVkTWVzc2FnZSk7IH0KCiAgICAgICAgICAgICAgICBjYXRjaCAoRXhjZXB0aW9uIGlnbm9yZSkge30KCiAgICAgICAgICAgICAgICByZXR1cm4gbGVnYWN5LmRlY3J5cHQoZW5jcnlwdGVkTWVzc2FnZSk7CgogICAgICAgICAgICB9CgogICAgICAgIH07CgogICAgfQoKCgogICAgQEJlYW4KCiAgICBwdWJsaWMgRW5jcnlwdGFibGVQcm9wZXJ0eURldGVjdG9yIGVuY3J5cHRhYmxlUHJvcGVydHlEZXRlY3RvcigpIHsKCiAgICAgICAgcmV0dXJuIHZhbHVlIC0+IHZhbHVlICE9IG51bGwgJiYKCiAgICAgICAgICAgICAgICAodmFsdWUuc3RhcnRzV2l0aCgiRU5DX05FVygiKSB8fCB2YWx1ZS5zdGFydHNXaXRoKCJFTkNfT0xEKCIpIHx8IHZhbHVlLnN0YXJ0c1dpdGgoIkVOQygiKSk7CgogICAgfQoKCgogICAgQEJlYW4KCiAgICBwdWJsaWMgRW5jcnlwdGFibGVQcm9wZXJ0eVJlc29sdmVyIGVuY3J5cHRhYmxlUHJvcGVydHlSZXNvbHZlcigKCiAgICAgICAgICAgIEBRdWFsaWZpZXIoImxlZ2FjeUVuY3J5cHRvciIpIFN0cmluZ0VuY3J5cHRvciBsZWdhY3ksCgogICAgICAgICAgICBAUXVhbGlmaWVyKCJlbmNyeXB0b3JCZWFuIikgU3RyaW5nRW5jcnlwdG9yIG1vZGVybikgewoKCgogICAgICAgIHJldHVybiB2YWx1ZSAtPiB7CgogICAgICAgICAgICBpZiAodmFsdWUgPT0gbnVsbCkgcmV0dXJuIG51bGw7CgoKCiAgICAgICAgICAgIGlmICh2YWx1ZS5zdGFydHNXaXRoKCJFTkNfTkVXKCIpICYmIHZhbHVlLmVuZHNXaXRoKCIpIikpIHsKCiAgICAgICAgICAgICAgICBTdHJpbmcgYm9keSA9IHZhbHVlLnN1YnN0cmluZygiRU5DX05FVygiLmxlbmd0aCgpLCB2YWx1ZS5sZW5ndGgoKSAtIDEpOwoKICAgICAgICAgICAgICAgIHJldHVybiBtb2Rlcm4uZGVjcnlwdChib2R5KTsKCiAgICAgICAgICAgIH0KCiAgICAgICAgICAgIGlmICh2YWx1ZS5zdGFydHNXaXRoKCJFTkNfT0xEKCIpICYmIHZhbHVlLmVuZHNXaXRoKCIpIikpIHsKCiAgICAgICAgICAgICAgICBTdHJpbmcgYm9keSA9IHZhbHVlLnN1YnN0cmluZygiRU5DX09MRCgiLmxlbmd0aCgpLCB2YWx1ZS5sZW5ndGgoKSAtIDEpOwoKICAgICAgICAgICAgICAgIHJldHVybiBsZWdhY3kuZGVjcnlwdChib2R5KTsKCiAgICAgICAgICAgIH0KCiAgICAgICAgICAgIGlmICh2YWx1ZS5zdGFydHNXaXRoKCJFTkMoIikgJiYgdmFsdWUuZW5kc1dpdGgoIikiKSkgewoKICAgICAgICAgICAgICAgIFN0cmluZyBib2R5ID0gdmFsdWUuc3Vic3RyaW5nKCJFTkMoIi5sZW5ndGgoKSwgdmFsdWUubGVuZ3RoKCkgLSAxKTsKCiAgICAgICAgICAgICAgICB0cnkgeyByZXR1cm4gbW9kZXJuLmRlY3J5cHQoYm9keSk7IH0KCiAgICAgICAgICAgICAgICBjYXRjaCAoRXhjZXB0aW9uIGlnbm9yZSkgeyByZXR1cm4gbGVnYWN5LmRlY3J5cHQoYm9keSk7IH0KCiAgICAgICAgICAgIH0KCiAgICAgICAgICAgIHJldHVybiB2YWx1ZTsKCiAgICAgICAgfTsKCiAgICB9Cgp9CgpgYGAKCgoKYGFwcGxpY2F0aW9uLnltbGDvvJoKCgoKYGBgeWFtbAoKamFzeXB0OgoKICBlbmNyeXB0b3I6CgogICAgYmVhbjogcm91dGluZ0VuY3J5cHRvciAgICMg6K6TIFJlc29sdmVyL0RldGVjdG9yIOeUn+aViO+8jOS4puaPkOS+myByb3V0aW5nIOiDveWKmwoKCgpzcHJpbmc6CgogIGRhdGFzb3VyY2U6CgogICAgcGFzc3dvcmQ6ICJFTkNfTkVXKC4uLi4uKSIgICAjIOaWsOagvOW8j++8iEFFUy0yNTbvvIkKCgoKbGVnYWN5OgoKICB0b2tlbjogIkVOQ19PTEQoLi4uLi4pIiAgICAgICAgIyDoiIrmoLzlvI/vvIhNRDUrREVT77yJCgoKCm90aGVyOgoKICBzZWNyZXQ6ICJFTkMoLi4uLi4pIiAgICAgICAgICAgIyDmrbflj7Lnm7jlrrnvvJrmnKrmqJnnpLrmlrDoiIrogIUKCmBgYAoKCgotLS0KCgoKIyMgQy4g55Si5Ye66IiH6L2J5o+b562W55Wl77yI5a+m5YuZ77yJCgotICoq5paw6LOH5paZ5LiA5b6L55SoIEFFUy0yNTbvvIhgZW5jcnlwdG9yQmVhbmDvvInnlKLnlJ8qKu+8m+mBv+WFjeWGjeWvq+WFpSBNRDUrREVT44CCICAKCi0g6IiK6LOH5paZ5L+d55WZ6Kej5a+G6IO95Yqb5LiA5q615pmC6ZaT77yIYGxlZ2FjeUVuY3J5cHRvcmDvvInvvIzpgJDmraXovYnmqpTmiJblnKjoroDlj5bmmYLljbPmmYLku6XmlrDmoLzlvI/ph43lr6vjgIIgIAoKLSDlnKjntYTmhYvkuK3mlLnnlKggYEVOQ19ORVcoLi4uKWDvvJvlj6rlnKjlv4XopoHmmYLkv53nlZkgYEVOQ19PTEQoLi4uKWDjgIIKCgoKLS0tCgoKCiMjIEQuIOW4uOimi+iQveWdkQoKLSDljp/lp4vniYfmrrXluLjopovoqqTmpI3vvIhgc2FpdGAg4oaSIGBzYWx0YOOAgWBzZXRBbGdvcml0aG1gIOaLvOWvq+OAgeWFqOW9ouespuiZn++8ie+8jOevhOS+i+W3suS/ruato+OAgiAgCgotIGBQQkVXaXRoTUQ1QW5kREVTYCArIGBOb0l2R2VuZXJhdG9yYCDlronlhajmgKflvojlvLHvvJvlg4XpmZDnm7jlrrnmnJ/kvb/nlKjjgIIgIAoKLSBgUF9FTlZfMWAgLyBgUF9FTlZfMmAg5Y+W6IeqICoqSlZNIOezu+e1seWxrOaApyoqIOaIliAqKueSsOWig+iuiuaVuCoqIOaZgu+8jOiri+iZleeQhuepuuWtl+S4suiIhyBgIm51bGwiYOOAgiAgCgotIOiIiiBKREsg6ZyA5rOo5oSPIEFFUy0yNTYg55qE5pS/562W5qqU77yb54++5LujIEpESyDlt7LpoJDoqK3plovllZ/vvIzkvYbku43lu7rorbDlnKjmiYDmnInnkrDlooPpqZforYnjgIIKCgoKLS0tCgoKCiMjIEUuIOacgOWwj+WPr+eUqOmFjee9rua4heWWrgoKMS4g5a6j5ZGKIGBAQmVhbigibGVnYWN5RW5jcnlwdG9yIilgIOiIhyBgQEJlYW4oImVuY3J5cHRvckJlYW4iKWDjgIIgIAoKMi4g6Iul57WE5oWL6KaB5ZCM5pmC6Kej5YWp56iu5qC85byP77ya5Yqg5LiK6Ieq6KiCIGBFbmNyeXB0YWJsZVByb3BlcnR5RGV0ZWN0b3JgIC8gYEVuY3J5cHRhYmxlUHJvcGVydHlSZXNvbHZlcmDvvIzmlK/mj7QgYEVOQ19ORVcoLi4uKWDjgIFgRU5DX09MRCguLi4pYOOAgWBFTkMoLi4uKWDjgIIgIAoKMy4g56iL5byP56K85bGk5LulIGBAUXVhbGlmaWVyYCDmjIflrprkvb/nlKjlk6rkuIDntYQgRW5jcnlwdG9y44CCCgoKCi0tLQoKCgojIyBGLiDnsKHmmJPmuKzoqabvvIjnlKLnlJ/vvI/pqZforYnlr4bmlofvvIkKCgoKYEphc3lwdFF1aWNrVGVzdC5qYXZhYO+8mgoKCgpgYGBqYXZhCgpwdWJsaWMgY2xhc3MgSmFzeXB0UXVpY2tUZXN0IHsKCgoKICAgIHB1YmxpYyBzdGF0aWMgdm9pZCBtYWluKFN0cmluZ1tdIGFyZ3MpIHsKCiAgICAgICAgLy8g5YGH6KOd5bey5b6eIFNwcmluZyDlj5blh7rlhanlgIsgQmVhbu+8mmxlZ2FjeSDoiIcgbW9kZXJuCgogICAgICAgIHZhciBsZWdhY3kgPSBuZXcgb3JnLmphc3lwdC5lbmNyeXB0aW9uLnBiZS5TdGFuZGFyZFBCRVN0cmluZ0VuY3J5cHRvcigpOwoKICAgICAgICBsZWdhY3kuc2V0UGFzc3dvcmQoInZ1NHdqLzMiKTsgLy8g6IiK5a+G56K877ya5L6G6IeqIGFwcGxpY2F0aW9uIOioreWumgoKICAgICAgICBsZWdhY3kuc2V0QWxnb3JpdGhtKCJQQkVXaXRoTUQ1QW5kREVTIik7CgoKCiAgICAgICAgdmFyIG1vZGVybiA9IG5ldyBvcmcuamFzeXB0LmVuY3J5cHRpb24ucGJlLlN0YW5kYXJkUEJFU3RyaW5nRW5jcnlwdG9yKCk7CgogICAgICAgIG1vZGVybi5zZXRQYXNzd29yZChTeXN0ZW0uZ2V0ZW52KCJQX0VOVl8xIikgKyBTeXN0ZW0uZ2V0ZW52KCJQX0VOVl8yIikpOwoKICAgICAgICBtb2Rlcm4uc2V0QWxnb3JpdGhtKCJQQkVXaXRoSG1hY1NIQTUxMkFuZEFFU18yNTYiKTsKCgoKICAgICAgICBTdHJpbmcgcGxhaW4gPSAiczNjcjN0LVBAc3MhIjsKCgoKICAgICAgICBTdHJpbmcgb2xkQ2lwaGVyID0gbGVnYWN5LmVuY3J5cHQocGxhaW4pOwoKICAgICAgICBTdHJpbmcgbmV3Q2lwaGVyID0gbW9kZXJuLmVuY3J5cHQocGxhaW4pOwoKCgogICAgICAgIFN5c3RlbS5vdXQucHJpbnRsbigiRU5DX09MRCgiICsgb2xkQ2lwaGVyICsgIikiKTsKCiAgICAgICAgU3lzdGVtLm91dC5wcmludGxuKCJFTkNfTkVXKCIgKyBuZXdDaXBoZXIgKyAiKSIpOwoKCgogICAgICAgIC8vIOmpl+itiQoKICAgICAgICBTeXN0ZW0ub3V0LnByaW50bG4oImxlZ2FjeSAtPiAiICsgbGVnYWN5LmRlY3J5cHQob2xkQ2lwaGVyKSk7CgogICAgICAgIFN5c3RlbS5vdXQucHJpbnRsbigibW9kZXJuIC0+ICIgKyBtb2Rlcm4uZGVjcnlwdChuZXdDaXBoZXIpKTsKCiAgICB9Cgp9CgpgYGAKCgoKPiDmj5DnpLrvvJrmraPlvI/lsIjmoYjoq4vku6UgU3ByaW5nIOWPluW+lyBCZWFuIOmAsuihjOa4rOippu+8m+atpOiZleeCuuacgOWwj+WPr+ihjOekuuaEj+OAggoKCgotLS0KCgoKIyMg54mI5o6n5bu66K2wCgotIOWFiOWwjuWFpeOAjOi3r+eUseOAjeiIh+mbmSBFbmNyeXB0b3LvvIznorrkv53mnI3li5nlj6/lkIzmmYLop6PlhannqK7moLzlvI/jgIIgIAoKLSDmkrDlr6vos4fmlpnovYnmj5vohbPmnKzvvIjmibnmrKHmiJboroDlj5bljbPovYnvvInvvIzpgJDmraXmtojpmaQgYEVOQ19PTEQoLi4uKWDjgIIgIAoKLSDmuIXmn6XlroznlaLlvozvvIwqKuenu+mZpOiIiiBFbmNyeXB0b3IqKiDoiIcgYEVOQ19PTEQoLi4uKWAg5pSv5o+077yM6ZmN5L2O5pS75pOK6Z2i44CCCgoKCi0tLQoKCgojIyDmjojmrIroiIflronlhagKCi0g5pys5oyH5Y2X5YOF56S656+EIEphc3lwdCDnlKjms5XvvIzkuI3ljIXlkKvnrKzkuInmlrnmjojmrIrmqpTjgIIgIAoKLSDlr6bli5nkuIroq4vmkK3phY3npZXlr4bnrqHnkIbvvIjlpoIgS01T44CBVmF1bHTjgIFLdWJlcm5ldGVzIFNlY3JldO+8ieWPluS7o+ehrOe3qOeivOWvhumRsOOAggo=
返回列表 上一筆 下一筆