CORS

Spring WebFlux を使用すると、CORS(クロスオリジンリソース共有)を処理できます。このセクションでは、その方法について説明します。

導入

セキュリティ上の理由から、ブラウザーは現在のオリジンの外部のリソースへの AJAX 呼び出しを禁止しています。例: 銀行口座を 1 つのタブに、evil.com を別のタブに持つことができます。evil.com のスクリプトは、アカウントからお金を引き出すなど、認証情報を使用して銀行の API に AJAX リクエストを行うことはできません!

Cross-Origin Resource Sharing(CORS)は、ほとんどのブラウザー (英語) で実装されている W3C 仕様 (英語) であり、IFRAME または JSONP に基づく安全性が低く、強力ではない回避策を使用するのではなく、認可される種類のクロスドメインリクエストを指定できます。

処理

CORS 仕様は、プリフライト、シンプル、実際のリクエストを区別します。CORS がどのように機能するかを学ぶために、他の多くの人と一緒にこの記事 [Mozilla] を読むか、詳細について仕様を参照してください。

Spring WebFlux HandlerMapping 実装は、CORS の組み込みサポートを提供します。リクエストをハンドラーに正常にマッピングした後、HandlerMapping は指定されたリクエストとハンドラーの CORS 設定をチェックし、さらにアクションを実行します。プリフライトリクエストは直接処理されますが、シンプルで実際の CORS リクエストはインターセプトされ、検証され、必要な CORS レスポンスヘッダーが設定されます。

クロスオリジンリクエストを有効にするには(つまり、Origin ヘッダーが存在し、リクエストのホストとは異なる)、明示的に宣言された CORS 構成が必要です。一致する CORS 設定が見つからない場合、プリフライトリクエストは拒否されます。CORS ヘッダーは単純および実際の CORS リクエストのレスポンスに追加されないため、ブラウザーはそれらを拒否します。

各 HandlerMapping は、URL パターンベースの CorsConfiguration マッピングで個別に構成 (Javadoc) できます。ほとんどの場合、アプリケーションは WebFlux Java 構成を使用してこのようなマッピングを宣言します。これにより、単一のグローバルマップがすべての HandlerMapping 実装に渡されます。

HandlerMapping レベルのグローバル CORS 構成と、よりきめ細かいハンドラーレベルの CORS 構成を組み合わせることができます。例: アノテーション付きコントローラーは、クラスまたはメソッドレベルの @CrossOrigin アノテーションを使用できます(他のハンドラーは CorsConfigurationSource を実装できます)。

グローバル設定とローカル設定を結合するためのルールは、一般的に付加的です。たとえば、すべてのグローバルおよびすべてのローカルオリジン。allowCredentials や maxAge など、単一の値しか受け入れられない属性の場合、ローカルはグローバル値をオーバーライドします。詳細については、CorsConfiguration#combine(CorsConfiguration) (Javadoc) を参照してください。

ソースから詳細を確認するか、高度なカスタマイズを行うには、次を参照してください。

  • CorsConfiguration

  • CorsProcessor および DefaultCorsProcessor

  • AbstractHandlerMapping

資格証明付きリクエスト

認証情報付きリクエストで CORS を使用するには、allowedCredentials を有効にする必要があります。このオプションでは、構成されたドメインとの高レベルの信頼が確立され、Cookie や CSRF トークンなどのユーザー固有の機密情報が公開されるため、Web アプリケーションの攻撃対象領域が増えることに注意してください。

資格情報を有効にすると、構成された "*" CORS ワイルドカードの処理方法にも影響します。

  • allowOrigins ではワイルドカードは認可されていませんが、代わりに allowOriginPatterns プロパティを使用して、起点の動的なセットと一致させることができます。

  • allowedHeaders または allowedMethods に設定すると、Access-Control-Allow-Headers および Access-Control-Allow-Methods レスポンスヘッダーは、CORS プリフライトリクエストで指定された関連ヘッダーとメソッドをコピーすることによって処理されます。

  • exposedHeaders に設定すると、Access-Control-Expose-Headers レスポンスヘッダーは、構成されたヘッダーのリストまたはワイルドカード文字のいずれかに設定されます。CORS 仕様では、Access-Control-Allow-Credentials が true に設定されている場合にワイルドカード文字を使用できませんが、ほとんどのブラウザーはワイルドカード文字をサポートしており、CORS 処理中にレスポンスヘッダーがすべて使用できるわけではないため、結果として、ワイルドカード文字は、指定時に使用されるヘッダー値になります。allowCredentials プロパティの値。

このようなワイルドカード構成は便利ですが、可能であれば、より高いレベルのセキュリティを提供するために、代わりに有限の値のセットを構成することをお勧めします。

@CrossOrigin

@CrossOrigin (Javadoc) アノテーションは、次の例に示すように、アノテーション付きコントローラーメソッドでクロスオリジンリクエストを有効にします。

  • Java

  • Kotlin

@RestController
@RequestMapping("/account")
public class AccountController {

	@CrossOrigin
	@GetMapping("/{id}")
	public Mono<Account> retrieve(@PathVariable Long id) {
		// ...
	}

	@DeleteMapping("/{id}")
	public Mono<Void> remove(@PathVariable Long id) {
		// ...
	}
}
@RestController
@RequestMapping("/account")
class AccountController {

	@CrossOrigin
	@GetMapping("/{id}")
	suspend fun retrieve(@PathVariable id: Long): Account {
		// ...
	}

	@DeleteMapping("/{id}")
	suspend fun remove(@PathVariable id: Long) {
		// ...
	}
}

デフォルトでは、@CrossOrigin は次を許可します。

  • すべての起源。

  • すべてのヘッダー。

  • コントローラーメソッドがマップされるすべての HTTP メソッド。

allowCredentials はデフォルトでは有効になっていません。これは、ユーザー固有の機密情報(Cookie や CSRF トークンなど)を公開する信頼レベルを確立し、適切な場合にのみ使用する必要があるためです。有効になっている場合は、allowOrigins を 1 つ以上の特定のドメインに設定する必要があります(ただし、特別な値 "*" は設定しないでください)。または、allowOriginPatterns プロパティを使用して動的な起点のセットに一致させることもできます。

maxAge は 30 分に設定されています。

@CrossOrigin はクラスレベルでもサポートされ、すべてのメソッドに継承されます。次の例では、特定のドメインを指定し、maxAge を 1 時間に設定します。

  • Java

  • Kotlin

@CrossOrigin(origins = "https://domain2.com", maxAge = 3600)
@RestController
@RequestMapping("/account")
public class AccountController {

	@GetMapping("/{id}")
	public Mono<Account> retrieve(@PathVariable Long id) {
		// ...
	}

	@DeleteMapping("/{id}")
	public Mono<Void> remove(@PathVariable Long id) {
		// ...
	}
}
@CrossOrigin("https://domain2.com", maxAge = 3600)
@RestController
@RequestMapping("/account")
class AccountController {

	@GetMapping("/{id}")
	suspend fun retrieve(@PathVariable id: Long): Account {
		// ...
	}

	@DeleteMapping("/{id}")
	suspend fun remove(@PathVariable id: Long) {
		// ...
	}
}

次の例に示すように、@CrossOrigin はクラスレベルとメソッドレベルの両方で使用できます。

  • Java

  • Kotlin

@CrossOrigin(maxAge = 3600) (1)
@RestController
@RequestMapping("/account")
public class AccountController {

	@CrossOrigin("https://domain2.com") (2)
	@GetMapping("/{id}")
	public Mono<Account> retrieve(@PathVariable Long id) {
		// ...
	}

	@DeleteMapping("/{id}")
	public Mono<Void> remove(@PathVariable Long id) {
		// ...
	}
}
1 クラスレベルで @CrossOrigin を使用します。
2 メソッドレベルで @CrossOrigin を使用します。
@CrossOrigin(maxAge = 3600) (1)
@RestController
@RequestMapping("/account")
class AccountController {

	@CrossOrigin("https://domain2.com") (2)
	@GetMapping("/{id}")
	suspend fun retrieve(@PathVariable id: Long): Account {
		// ...
	}

	@DeleteMapping("/{id}")
	suspend fun remove(@PathVariable id: Long) {
		// ...
	}
}
1 クラスレベルで @CrossOrigin を使用します。
2 メソッドレベルで @CrossOrigin を使用します。

グローバル構成

細分化されたコントローラーメソッドレベルの構成に加えて、おそらくグローバルな CORS 構成も定義する必要があります。URL ベースの CorsConfiguration マッピングは、HandlerMapping で個別に設定できます。ただし、ほとんどのアプリケーションは、WebFlux Java 構成を使用してこれを実行します。

デフォルトでは、グローバル構成により以下が有効になります。

  • すべての起源。

  • すべてのヘッダー。

  • GETHEADPOST メソッド。

allowedCredentials はデフォルトでは有効になっていません。これは、ユーザー固有の機密情報(Cookie や CSRF トークンなど)を公開する信頼レベルを確立し、適切な場合にのみ使用する必要があるためです。有効になっている場合は、allowOrigins を 1 つ以上の特定のドメインに設定する必要があります(ただし、特別な値 "*" は設定しないでください)。または、allowOriginPatterns プロパティを使用して動的な起点のセットに一致させることもできます。

maxAge は 30 分に設定されています。

WebFlux Java 構成で CORS を有効にするには、次の例に示すように、CorsRegistry コールバックを使用できます。

  • Java

  • Kotlin

@Configuration
@EnableWebFlux
public class WebConfig implements WebFluxConfigurer {

	@Override
	public void addCorsMappings(CorsRegistry registry) {

		registry.addMapping("/api/**")
			.allowedOrigins("https://domain2.com")
			.allowedMethods("PUT", "DELETE")
			.allowedHeaders("header1", "header2", "header3")
			.exposedHeaders("header1", "header2")
			.allowCredentials(true).maxAge(3600);

		// Add more mappings...
	}
}
@Configuration
@EnableWebFlux
class WebConfig : WebFluxConfigurer {

	override fun addCorsMappings(registry: CorsRegistry) {

		registry.addMapping("/api/**")
				.allowedOrigins("https://domain2.com")
				.allowedMethods("PUT", "DELETE")
				.allowedHeaders("header1", "header2", "header3")
				.exposedHeaders("header1", "header2")
				.allowCredentials(true).maxAge(3600)

		// Add more mappings...
	}
}

CORS WebFilter

組み込みの CorsWebFilter (Javadoc) を使用して CORS サポートを適用できます。これは、関数エンドポイントに適しています。

CorsFilter を Spring Security で使用しようとする場合、Spring Security には CORS のサポートが組み込まれていることに注意してください。

フィルターを構成するには、次の例に示すように、CorsWebFilter Bean を宣言し、CorsConfigurationSource をコンストラクターに渡すことができます。

  • Java

  • Kotlin

@Bean
CorsWebFilter corsFilter() {

	CorsConfiguration config = new CorsConfiguration();

	// Possibly...
	// config.applyPermitDefaultValues()

	config.setAllowCredentials(true);
	config.addAllowedOrigin("https://domain1.com");
	config.addAllowedHeader("*");
	config.addAllowedMethod("*");

	UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
	source.registerCorsConfiguration("/**", config);

	return new CorsWebFilter(source);
}
@Bean
fun corsFilter(): CorsWebFilter {

	val config = CorsConfiguration()

	// Possibly...
	// config.applyPermitDefaultValues()

	config.allowCredentials = true
	config.addAllowedOrigin("https://domain1.com")
	config.addAllowedHeader("*")
	config.addAllowedMethod("*")

	val source = UrlBasedCorsConfigurationSource().apply {
		registerCorsConfiguration("/**", config)
	}
	return CorsWebFilter(source)
}