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
、ローカルはグローバル値をオーバーライドします。詳細については、CorsConfiguration#combine(CorsConfiguration)
(Javadoc) を参照してください。
ソースからさらに学習するか、高度なカスタマイズを行うには、コードビハインドを確認します。
|
@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 名前空間を使用してこれを実行します。
デフォルトでは、グローバル構成により以下が有効になります。
すべての起源。
すべてのヘッダー。
GET
、HEAD
、POST
メソッド。
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)