Vault リポジトリ
VaultTemplate
(Javadoc) と Java クラスにマップされたレスポンスを操作すると、読み取り、書き込み、削除などの基本的なデータ操作が可能になります。Vault リポジトリは、Vault の上に Spring Data のリポジトリコンセプトを適用します。Vault リポジトリは基本的な CRUD 機能を公開し、識別子プロパティ、ページング、並べ替えを制約する述語を使用したクエリ派生をサポートします。Vault リポジトリは、キー / 値シークレットエンジン機能を使用して、データを永続化およびクエリします。バージョン 2.4 以降、Spring、Vault は、キー / 値バージョン 2 シークレットエンジンを追加で使用できます。実際のシークレットエンジンバージョンは実行時に検出されます。
バージョン管理されたキー / 値シークレットエンジン内での削除には、DELETE 操作が使用されます。秘密は CrudRepository.delete(…) を通じて破棄されません。 |
Spring Data Commons リファレンスドキュメントの Spring Data リポジトリの詳細を参照してください。リファレンスドキュメントでは、Spring Data リポジトリの概要が説明されています。 |
使用方法
Vault に格納されているドメインエンティティにアクセスするには、リポジトリサポートを利用して、それらの実装を大幅に簡素化できます。
@Secret
class Credentials {
@Id String id;
String password;
String socialSecurityNumber;
Address address;
}
ここには非常に単純なドメインオブジェクトがあります。org.springframework.data.annotation.Id
でアノテーションが付けられた id
という名前のプロパティと、その型に @Secret
アノテーションがあることに注意してください。これら 2 つは、Vault 内でオブジェクトを JSON として永続化するために使用される実際のキーを作成するロールを果たします。
@Id でアノテーションが付けられたプロパティ、および id という名前のプロパティは、識別子プロパティと見なされます。アノテーションのあるものは他のものよりも好まれます。 |
次のステップは、ドメインオブジェクトを使用するリポジトリインターフェースを宣言することです。
Credentials
エンティティの基本リポジトリインターフェース interface CredentialsRepository extends CrudRepository<Credentials, String> {
}
リポジトリは CrudRepository
を継承するため、基本的な CRUD とクエリメソッドを提供します。Vault リポジトリには Spring Data コンポーネントが必要です。クラスパスに spring-data-commons
および spring-data-keyvalue
アーティファクトを必ず含めてください。
これを実現する最も簡単な方法は、依存関係管理を設定し、アーティファクトを pom.xml
に追加することです。
次に、次を pom.xml
の依存関係セクションに追加します。
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-bom</artifactId>
<version>2023.1.9</version>
<scope>import</scope>
<type>pom</type>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<!-- other dependency elements omitted -->
<dependency>
<groupId>org.springframework.vault</groupId>
<artifactId>spring-vault-core</artifactId>
<version>3.1.2</version>
</dependency>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-keyvalue</artifactId>
<!-- Version inherited from the BOM -->
</dependency>
</dependencies>
物事を接着するために間に必要なものは、それに応じた Spring 構成です。
@Configuration
@EnableVaultRepositories
class ApplicationConfig {
@Bean
VaultTemplate vaultTemplate() {
return new VaultTemplate(…);
}
}
上記の設定があれば、続行して CredentialsRepository
をコンポーネントに注入できます。
@Autowired CredentialsRepository repo;
void basicCrudOperations() {
Credentials creds = new Credentials("heisenberg", "327215", "AAA-GG-SSSS");
rand.setAddress(new Address("308 Negra Arroyo Lane", "Albuquerque", "New Mexico", "87104"));
repo.save(creds); (1)
repo.findOne(creds.getId()); (2)
repo.count(); (3)
repo.delete(creds); (4)
}
1 | キー値シークレットシークレットエンジンのキーパターン keyspace/id (この場合は credentials/heisenberg ) を使用して、Credentials のプロパティを Vault ハッシュ内に格納します。 |
2 | 指定された ID を使用して、keyspace/id に格納されているオブジェクトを取得します。 |
3 | Credentials で @Secret によって定義されたキースペース資格情報内で使用可能なエンティティの総数をカウントします。 |
4 | 指定されたオブジェクトのキーを Vault から削除します。 |
オブジェクトから Vault JSON へのマッピング
Vault リポジトリは、JSON を交換形式として使用して Vault にオブジェクトを格納します。JSON とエンティティ間のオブジェクトマッピングは VaultConverter
によって行われます。コンバーターは、VaultResponse
からの本体を含む SecretDocument
の読み取りと書き込みを行います。VaultResponse
は Vault から読み取られ、本体は Jackson によって String
と Object
の Map
に逆直列化されます。デフォルトの VaultConverter
実装は、ネストされた値、List
および Map
オブジェクトを含む Map
を読み取り、これらをエンティティに、またはその逆に変換します。
前のセクションの Credentials
型の場合、デフォルトのマッピングは次のようになります。
{
"_class": "org.example.Credentials", (1)
"password": "327215", (2)
"socialSecurityNumber": "AAA-GG-SSSS",
"address": { (3)
"street": "308 Negra Arroyo Lane",
"city": "Albuquerque",
"state": "New Mexico",
"zip": "87104"
}
}
1 | _class 属性は、ルートレベルだけでなく、ネストされたインターフェースまたは抽象型にも含まれています。 |
2 | 単純なプロパティ値はパスによってマップされます。 |
3 | 複合型のプロパティは、ネストされたオブジェクトとしてマップされます。 |
@Id プロパティは String にマップする必要があります。 |
タイプ | サンプル | マップされた値 |
---|---|---|
シンプル型 | String firstname=" ウォルター "; | " ファーストネーム ": 「ウォルター」 |
複合型 | Address address = new Address("308 Negra Arroyo Lane"); | " 住所 ": { " 街 ": "308 ネグラアロヨレーン " } |
リスト | List<String> nicknames = asList("walt" , "heisenberg" ); | 「ニックネーム」: [「ウォルト」、「ハイゼンベルグ」] |
地図 | Map< 文字列、整数> atts = asMap(" 年齢 ", 51) | 「アッツ」: {" 年 ": 51} |
リスト | リスト <Address> アドレス = asList(新しいアドレス ("308 … | " 住所 ": [{ " 街 ": "308 Negra Arroyo Lane" }, … ] |
VaultCustomConversions
に Converter
を登録することで、マッピングの動作をカスタマイズできます。これらのコンバーターは、LocalDate
や SecretDocument
などの型との間の変換を処理できますが、最初のコンバーターは単純なプロパティの変換に適しており、最後のコンバーターは複雑な型を JSON 表現に変換するのに適しています。2 番目のオプションでは、結果の SecretDocument
を完全に制御できます。オブジェクトを Vault
に書き込むと、コンテンツが削除され、エントリ全体が再作成されるため、マップされていないデータは失われます。
クエリとクエリメソッド
クエリメソッドを使用すると、メソッド名から単純なクエリを自動的に派生させることができます。Vault にはクエリエンジンはありませんが、HTTP コンテキストパスに直接アクセスする必要があります。Vault クエリメソッドは、Vault の API の可能性をクエリに変換します。クエリメソッドの実行は、コンテキストパスに子を一覧表示し、Id にフィルター処理を適用し、オプションでオフセット / 制限を使用して Id ストリームを制限し、結果をフェッチした後に並べ替えを適用します。
interface CredentialsRepository extends CrudRepository<Credentials, String> {
List<Credentials> findByIdStartsWith(String prefix);
}
Vault リポジトリのクエリメソッドは、@Id プロパティの述語を含むクエリのみをサポートします。 |
Vault でサポートされているキーワードの概要を次に示します。
キーワード | サンプル |
---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
並べ替えとページング
クエリメソッドは、Vault コンテキストパスから取得したサブリスト (オフセット / 制限) ID をメモリ内で選択することにより、並べ替えとページングをサポートします。クエリメソッドの述語とは異なり、並べ替えは特定のフィールドに限定されません。非ページソートは ID フィルタリングの後に適用され、結果のすべてのシークレットは Vault からフェッチされます。このようにして、クエリメソッドは、結果の一部としても返される結果のみをフェッチします。
ページングとソートを使用するには、パフォーマンスに影響を与える ID をフィルタリングする前にシークレットフェッチが必要です。並べ替えとページングは、Vault によって返される Id の自然順序が変更された場合でも、同じ結果を返すことを保証します。最初にすべての Id が Vault から取得され、次に並べ替えが適用され、その後フィルタリングとオフセット / 制限が適用されます。
interface CredentialsRepository extends PagingAndSortingRepository<Credentials, String> {
List<Credentials> findTop10ByIdStartsWithOrderBySocialSecurityNumberDesc(String prefix);
List<Credentials> findByIdStarts(String prefix, Pageable pageRequest);
}
楽観的ロック
Vaults キー / 値シークレットエンジンバージョン 2 は、バージョン付きシークレットを維持できます。Spring Vault は、@Version
のアノテーションが付けられたドメインモデルのバージョンプロパティを通じてバージョン管理をサポートします。オプティミスティックロックを使用すると、バージョンが一致するシークレットにのみ更新が適用されます。バージョンプロパティの実際の値は、cas
プロパティを通じて更新リクエストに追加されます。その間に別の操作によってシークレットが変更された場合、OptimisticLockingFailureException がスローされ、シークレットは更新されません。
バージョンプロパティは、int
や long
などの数値プロパティである必要があり、シークレットを更新するときに cas
プロパティにマップする必要があります。
@Secret
class VersionedCredentials {
@Id String id;
@Version int version;
String password;
String socialSecurityNumber;
Address address;
}
次の例は、これらの機能を示しています。
VersionedCredentialsRepository repo = …;
VersionedCredentials credentials = repo.findById("sample-credentials").get(); (1)
VersionedCredentials concurrent = repo.findById("sample-credentials").get(); (2)
credentials.setPassword("something-else");
repos.save(credentials); (3)
concurrent.setPassword("concurrent change");
repos.save(concurrent); // throws OptimisticLockingFailureException (4)
1 | ID sample-credentials によってシークレットを取得します。 |
2 | ID sample-credentials によってシークレットの 2 番目のインスタンスを取得します。 |
3 | シークレットを更新し、Vault のバージョンをインクリメントします。 |
4 | 以前のバージョンを使用する 2 番目のインスタンスを更新します。その間にバージョンが Vault でインクリメントされたため、操作は OptimisticLockingFailureException で失敗します。 |
バージョン管理されたシークレットを削除する場合、ID による削除では最新のシークレットが削除されます。エンティティごとに削除では、指定されたバージョンでシークレットが削除されます。 |
バージョン管理されたシークレットへのアクセス
Key/Value バージョン 2 シークレットエンジンは、Vault リポジトリインターフェース宣言で RevisionRepository
(Javadoc) を実装することでアクセスできるシークレットのバージョンを維持します。リビジョンリポジトリは、特定の識別子のリビジョンを取得するための検索メソッドを定義します。識別子は String
である必要があります。
RevisionRepository
の実装 interface RevisionCredentialsRepository extends CrudRepository<Credentials, String>,
RevisionRepository<Credentials, String, Integer> (1)
{
}
1 | 最初の型パラメーター (Credentials ) はエンティティの型を示し、2 番目 (String ) は id プロパティの型を示し、最後の型パラメーター (Integer ) はリビジョン番号の型を示します。Vault は、String 識別子と Integer リビジョン番号のみをサポートします。 |
使用方法
次の例に示すように、RevisionRepository
のメソッドを使用してエンティティのリビジョンをクエリできるようになりました。
RevisionRepository
を使用する RevisionCredentialsRepository repo = …;
Revisions<Integer, Credentials> revisions = repo.findRevisions("my-secret-id");
Page<Revision<Integer, Credentials>> firstPageOfRevisions = repo.findRevisions("my-secret-id", Pageable.ofSize(4));