マイクロサービスアーキテクチャでは、APIゲートウェイがクライアントとバックエンドサービス間の重要な中継点として機能し、リクエストのルーティング、負荷分散、セキュリティ検証といった多岐にわたる役割を担います。数あるAPIゲートウェイフレームワークの中でも、Spring Cloud Gatewayはその強力な機能セットにより、多くのマイクロサービスプロジェクトで採用されています。本稿では、Spring Cloud Gatewayで独自のグローバルフィルターを実装する方法について、具体的なコード例を交えながら解説します。
1. グローバルフィルターとは
Spring Cloud Gatewayにおけるグローバルフィルター(Global Filter)は、ゲートウェイを通過するすべてのリクエストに対して処理を適用できる特殊なフィルターです。これらは、特定のルートに限定されることなく、全てのAPIアクセスに対して横断的な機能を提供するために利用されます。
グローバルフィルターの代表的な適用例は以下の通りです。
- アクセスログ記録:全てのリクエスト詳細情報を記録します。
- 統一認証/認可:システムへの全ての入力リクエストに対して認証チェックを行います。
- リクエストの変更/拡張:共通のヘッダーを追加したり、URLを書き換えたりします。
カスタムグローバルフィルターを実装することで、これらの機能をゲートウェイレベルで容易に実現できます。
2. 必要な依存関係
カスタムフィルターの実装を始める前に、Spring Cloud Gatewayプロジェクトに必要な依存関係が`pom.xml`ファイルに含まれていることを確認してください。
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
- `spring-cloud-starter-gateway`: Spring Cloud Gatewayのコア機能を提供します。
- `spring-boot-starter-webflux`: Spring Cloud GatewayはSpring WebFlux上に構築されているため、WebFlux関連の依存関係が必要です。
3. カスタムグローバルフィルターの実装
ここでは、ゲートウェイを通過するリクエストのヘッダー情報をログに出力し、簡易的な認証プロセスをシミュレートするカスタムグローバルフィルター`AuthVerificationFilter`を実装します。このフィルターは、特に指定の認証トークンヘッダーが存在するかどうかを確認します。
package jp.co.example.gateway.auth;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.http.HttpHeaders;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* カスタム認証検証グローバルフィルター
* このフィルターは、API Gatewayを通過するリクエストのヘッダー情報をログに出力し、
* 簡易的な認証プロセスをシミュレートします。
*/
@Component
public class AuthVerificationFilter implements GlobalFilter, Ordered {
private static final Logger log = LoggerFactory.getLogger(AuthVerificationFilter.class);
@Override
public Mono<Void> filter(ServerWebExchange currentExchange, GatewayFilterChain chain) {
ServerHttpRequest httpRequest = currentExchange.getRequest();
HttpHeaders requestHeaders = httpRequest.getHeaders();
log.info("着信リクエストパス: {}", httpRequest.getPath());
// 'X-Auth-Token' ヘッダーの存在を確認
if (requestHeaders.containsKey("X-Auth-Token")) {
String authToken = requestHeaders.getFirst("X-Auth-Token");
log.info("認証トークンが検出されました: {}", authToken != null ? authToken.substring(0, Math.min(authToken.length(), 10)) + "..." : "null");
// ここに実際の認証ロジックを記述します
} else {
log.warn("リクエストパス '{}' に 'X-Auth-Token' ヘッダーがありません。", httpRequest.getPath());
}
// デバッグレベルで全ヘッダーを出力
log.debug("全リクエストヘッダー: {}", requestHeaders);
// 次のフィルターまたはルートハンドラーにリクエストを渡す
return chain.filter(currentExchange);
}
@Override
public int getOrder() {
// フィルターの実行順序を定義。値が小さいほど優先度が高い。
return -100; // 他の一般的なフィルターより高い優先度を設定
}
}
コード解説
- パッケージとインポート: カスタムフィルターは`jp.co.example.gateway.auth`パッケージに配置され、Spring Cloud GatewayやWebFlux、ロギングに必要なクラスをインポートしています。
- `GlobalFilter`インターフェースの実装: `AuthVerificationFilter`クラスは`GlobalFilter`インターフェースを実装しており、これによりゲートウェイを通過する全てのリクエストをインターセプトする`filter`メソッドを提供します。
- `filter`メソッド: このメソッド内で、`currentExchange.getRequest()`から現在の`ServerHttpRequest`オブジェクトを取得し、その`getHeaders()`メソッドを通じてリクエストヘッダーにアクセスします。ここでは、リクエストパスをログに出力し、`X-Auth-Token`ヘッダーの存在を確認しています。トークンが存在する場合はその一部をログに出力し、存在しない場合は警告をログに出力します。実際の認証ロジックは、この部分に拡張して実装することが可能です。処理後、`chain.filter(currentExchange)`を呼び出すことで、リクエストを次のフィルターチェーンに渡します。
- `Ordered`インターフェースの実装: `Ordered`インターフェースを実装することで、`getOrder`メソッドを通じてフィルターの実行順序を制御できます。数値が小さいほどフィルターの優先度が高くなります。本例では`-100`を設定し、他の多くのフィルターよりも先に実行されるようにしています。
4. グローバルフィルターの役割と特性
Spring Cloud Gatewayのグローバルフィルターには、特定の特性があります。
- 全リクエストへの適用: グローバルフィルターは、設定された特定のルートや条件に関わらず、ゲートウェイを通過する全てのAPIリクエストに対して機能します。
- 実行順序の制御: `Ordered`インターフェースを利用することで、他のグローバルフィルターやルート固有のフィルターとの間で、フィルターの実行順序を詳細に制御できます。
- リクエスト/レスポンスの両方への介入: リクエストの段階だけでなく、バックエンドサービスからのレスポンスがクライアントに返される際にも処理を適用し、レスポンス内容の変更や追加が可能です。
5. 拡張的な利用シナリオ
実用的なプロジェクトにおいて、グローバルフィルターは以下のような場面で特に有効です。
- 全般的な認証と認可: 全てのAPIリクエストに対して統一された認証フローを適用し、認証情報をリクエストヘッダーに追加してバックエンドサービスに渡します。
- 包括的なリクエストログ: 各リクエストのパス、パラメータ、応答時間、ユーザーエージェントなどの詳細情報を記録し、監査、監視、デバッグに役立てます。
- 共通エラーハンドリング: ゲートウェイレベルで発生する全てのリクエスト処理中の例外を捕捉し、一貫性のあるエラーレスポンスをクライアントに返します。