CORS

Spring MVC では、CORS (Cross-Origin Resource Sharing) を処理できます。このセクションでは、その方法について説明します。

導入

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

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

資格証明付きリクエスト

認証情報付きリクエストで 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 プロパティの値。

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

処理

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

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

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

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

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

グローバル設定とローカル設定を組み合わせるルールは、一般的には追加的です。たとえば、すべてグローバル設定とすべてローカル設定です。allowCredentials や maxAge など、1 つの値しか受け入れられない属性の場合、ローカル設定がグローバル設定よりも優先されます。詳細については、CorsConfiguration#combine(CorsConfiguration) (Javadoc) を参照してください。

ソースからさらに学習するか、高度なカスタマイズを行うには、コードビハインドを確認します。

  • CorsConfiguration

  • CorsProcessorDefaultCorsProcessor

  • AbstractHandlerMapping

@CrossOrigin

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

  • Java

  • Kotlin

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

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

	@DeleteMapping("/{id}")
	public void remove(@PathVariable Long id) {
		// ...
	}
}
@RestController
@RequestMapping("/account")
class AccountController {

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

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

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

  • すべての起源。

  • すべてのヘッダー。

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

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

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

@CrossOrigin はクラスレベルでもサポートされており、次の例に示すように、すべてのメソッドに継承されます。

  • Java

  • Kotlin

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

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

	@DeleteMapping("/{id}")
	public void remove(@PathVariable Long id) {
		// ...
	}
}
@CrossOrigin(origins = ["https://domain2.com"], maxAge = 3600)
@RestController
@RequestMapping("/account")
class AccountController {

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

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

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

  • Java

  • Kotlin

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

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

	@DeleteMapping("/{id}")
	public void remove(@PathVariable Long id) {
		// ...
	}
}
@CrossOrigin(maxAge = 3600)
@RestController
@RequestMapping("/account")
class AccountController {

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

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

グローバル構成

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

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

  • すべての起源。

  • すべてのヘッダー。

  • GETHEADPOST メソッド。

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

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

Java 構成

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

  • Java

  • Kotlin

@Configuration
@EnableWebMvc
public class WebConfig implements WebMvcConfigurer {

	@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
@EnableWebMvc
class WebConfig : WebMvcConfigurer {

	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...
	}
}

XML 構成

XML 名前空間で CORS を有効にするには、次の例に示すように、<mvc:cors> 要素を使用できます。

<mvc:cors>

	<mvc:mapping path="/api/**"
		allowed-origins="https://domain1.com, https://domain2.com"
		allowed-methods="GET, PUT"
		allowed-headers="header1, header2, header3"
		exposed-headers="header1, header2" allow-credentials="true"
		max-age="123" />

	<mvc:mapping path="/resources/**"
		allowed-origins="https://domain1.com" />

</mvc:cors>

CORS フィルター

組み込みの CorsFilter (Javadoc) を介して CORS サポートを適用できます。

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

フィルターを構成するには、次の例に示すように、CorsConfigurationSource をコンストラクターに渡します。

  • Java

  • Kotlin

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);

CorsFilter filter = new CorsFilter(source);
val config = CorsConfiguration()

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

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

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

val filter = CorsFilter(source)