CORSプリフライトリクエストの処理を理解しよう!初心者向けにSpring SecurityとOPTIONSメソッドを解説
新人
「先輩、CORSって最近よく聞くんですが、セキュリティに関係してるんですか?」
先輩
「その通り。CORSは『クロスオリジンリソースシェアリング』の略で、異なるドメイン間でリソースをやり取りするときのセキュリティルールなんだ。」
新人
「なるほど…でも、なぜそんな制限が必要なんですか?」
先輩
「たとえば悪意あるサイトが、あなたのログイン済み情報を使って勝手にAPIを叩いたら大変でしょ?そうならないようにCORS設定があるんだ。」
新人
「じゃあ、プリフライトリクエストっていうのもCORSと関係あるんですね?」
先輩
「その通り。OPTIONSメソッドを使った通信がそれなんだ。詳しく説明しよう!」
1. CORSとは何か?セキュリティ対策としての意味
CORS(コース、Cross-Origin Resource Sharing)は、異なるオリジン間での通信を制限・制御する仕組みです。たとえば、https://example.comからhttps://api.example.jpのAPIにアクセスするとき、ブラウザはセキュリティ上の理由から制限をかけます。
これにより、悪意あるJavaScriptがユーザーの認証情報を使って勝手に別ドメインへアクセスすることを防止できます。CORS設定を明示的にサーバー側で行わないと、リクエストがブロックされてしまいます。
Spring Securityでは、セキュリティ機構が厳格に設定されており、CORS設定を無視するとAPIへのアクセスが拒否されることがあります。開発環境がpleiades + Gradleの場合でも、適切な@Configurationを用いたCORS設定が求められます。
たとえば、以下のようにCORS設定をJavaで記述できます。
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOrigins("https://frontend.example.com")
.allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS");
}
}
このようにして、Spring Security環境でもクロスオリジン通信を許可できます。
2. プリフライトリクエストとは?OPTIONSメソッドの役割
「プリフライトリクエスト」とは、CORSが有効な状況で行われる事前確認リクエストのことです。主にPUTやDELETEなどの特殊なHTTPメソッドや、Content-Type: application/jsonなどの特殊ヘッダーを使用する際に発生します。
このとき、ブラウザはまずOPTIONSメソッドでリクエストを送信し、そのリクエストが安全に行えるかどうかを確認します。これが「プリフライトリクエスト」です。
Spring SecurityでこのOPTIONSメソッドを正しく処理しないと、プリフライトリクエストが失敗し、以降の通信がブロックされてしまいます。
たとえば、Spring Securityの設定で以下のように記述する必要があります。
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.cors().and()
.csrf().disable()
.authorizeHttpRequests(auth -> auth
.requestMatchers(HttpMethod.OPTIONS, "/**").permitAll()
.anyRequest().authenticated()
);
return http.build();
}
}
HttpMethod.OPTIONSを許可している点が、プリフライトリクエストを処理する上で非常に重要です。
この設定がなければ、プリフライトリクエストが403エラーで拒否され、本来のPOSTやPUTリクエストも通らなくなります。
また、@Controllerを使った通常のSpringアプリケーションでも、OPTIONSメソッドへの対応はWebMvcConfigurerで設定することで実現可能です。
@Controller
public class TestController {
@PostMapping("/api/test")
@ResponseBody
public String test() {
return "OK";
}
}
この@PostMappingが呼び出される前に、OPTIONSメソッドによるプリフライトリクエストが通過している必要があることを忘れないでください。
3. Springでプリフライトリクエストを処理する仕組み
Springでは、プリフライトリクエスト(OPTIONSメソッド)を自動的に処理できる仕組みが用意されています。Spring MVCやSpring Securityの設定を正しく行えば、CORSのプリフライト対応は比較的簡単に実現できます。
ポイントは、OPTIONSメソッドを認可の対象から除外し、適切にレスポンスを返せるようにすることです。前のセクションで紹介したように、requestMatchers(HttpMethod.OPTIONS, "/**").permitAll()の設定が重要になります。
この設定を行うことで、ブラウザからのプリフライトリクエストをサーバー側で拒否せず、必要なCORSヘッダー付きのレスポンスを返せるようになります。
Spring SecurityではCORSフィルタが先に動くように制御されているため、CORS設定を行っていればOPTIONSメソッドを個別にコントローラでハンドリングする必要は基本的にありません。
ただし、Controller CORSの動作を確認する際には、フィルタの順番や設定漏れに注意する必要があります。CORSに関するエラーの多くは、プリフライトリクエストが正しく処理されていないことが原因です。
4. @CrossOriginでのプリフライト対応(実装例)
Springでは、特定のコントローラやメソッドに対して簡単にCORS設定を行うためのアノテーション@CrossOriginが用意されています。
このアノテーションを使うことで、プリフライトリクエストにも自動的に対応できるようになります。
以下は、@Controllerを使った実装例です。開発環境はpleiades + Gradleが前提です。
@Controller
@CrossOrigin(
origins = "https://frontend.example.com",
methods = {RequestMethod.GET, RequestMethod.POST, RequestMethod.PUT, RequestMethod.DELETE, RequestMethod.OPTIONS}
)
public class ApiController {
@PostMapping("/api/data")
@ResponseBody
public String postData() {
return "データを受け取りました";
}
}
@CrossOriginをクラスレベルに指定することで、そのクラス内のすべてのエンドポイントに対してCORS設定が適用されます。
また、必要に応じてメソッド単位での指定も可能です。プリフライト対応としてRequestMethod.OPTIONSを含めておくことで、OPTIONSメソッドにも対応できます。
このように、@CrossOriginは手軽にController CORS設定を行いたい場合に非常に便利な方法です。ただし、Spring Securityの設定が優先されるため、Security側でもcors()を有効にする必要があります。
5. プリフライトに対するレスポンスヘッダの設定と意味
プリフライトリクエストでは、サーバーから返されるレスポンスヘッダがとても重要です。これらのヘッダが適切に設定されていないと、ブラウザは本来のリクエストを送ることを許可しません。
主なレスポンスヘッダとその意味は以下の通りです:
Access-Control-Allow-Origin:許可されるオリジン(ドメイン)Access-Control-Allow-Methods:許可されるHTTPメソッド(例:GET, POST, PUT, DELETE, OPTIONS)Access-Control-Allow-Headers:許可されるリクエストヘッダ(例:Content-Type, Authorization)Access-Control-Max-Age:プリフライト結果のキャッシュ時間(秒)
Springでは、これらのヘッダをCorsRegistryや@CrossOriginで簡単に設定できます。例として、全体の設定を以下のようにまとめることができます。
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOrigins("https://frontend.example.com")
.allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS")
.allowedHeaders("Content-Type", "Authorization")
.maxAge(3600);
}
}
allowedHeadersを指定することで、Content-TypeやAuthorizationなどのカスタムヘッダが使用可能になります。これは特に認証付きのAPIで重要です。
また、maxAgeを設定することで、ブラウザがプリフライト結果を一定時間キャッシュできるため、同じリクエストが何度もOPTIONSメソッドで確認されるのを防ぐことができます。
これらのレスポンスヘッダが正しく返されるようにController CORSやSpring Security CORS設定を組み合わせて構成することが、クロスオリジン通信の成功に不可欠です。
6. Spring Securityを導入しているときの注意点(SecurityFilterとの関係)
Spring Securityを使っているプロジェクトでは、CORS設定が少し複雑になります。SecurityFilterChainがCORSよりも先に動作する場合、正しく設定されていないとプリフライトリクエストがブロックされてしまうことがあります。
たとえば、CORSの許可設定をWebMvcConfigurerで書いたとしても、Spring Security側でcors()が有効になっていないと意味がありません。そのため、Securityの設定で必ず以下のようにcors()を有効化しておく必要があります。
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.cors().and()
.csrf().disable()
.authorizeHttpRequests(auth -> auth
.requestMatchers(HttpMethod.OPTIONS, "/**").permitAll()
.anyRequest().authenticated()
);
return http.build();
}
@Bean
public CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration config = new CorsConfiguration();
config.setAllowedOrigins(List.of("https://frontend.example.com"));
config.setAllowedMethods(List.of("GET", "POST", "PUT", "DELETE", "OPTIONS"));
config.setAllowedHeaders(List.of("Content-Type", "Authorization"));
config.setMaxAge(3600L);
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", config);
return source;
}
}
このように、Spring SecurityでCORS設定を明示することで、SecurityFilterChainより先にCORSフィルターが適用されるようになります。これがうまく動かないと、「プリフライトリクエスト エラー」や403エラーが発生する原因になります。
初心者の方は、Controller側だけでCORS設定をすればよいと思いがちですが、Spring SecurityとCORSの関係を理解して、フィルター順序の影響を意識しておくことが大切です。
7. プリフライトリクエストが失敗するときの原因と対処法
クロスオリジン通信で最も多いトラブルが、プリフライトリクエストの失敗です。初心者がつまずきやすいポイントをいくつか紹介し、それぞれの対処方法を解説します。
① Access-Control-Allow-Originヘッダが不足している
これは基本中の基本ですが、プリフライトリクエストのレスポンスにAccess-Control-Allow-Originが設定されていないと、ブラウザは通信を拒否します。
対処法: @CrossOriginやWebMvcConfigurer、もしくはcorsConfigurationSource()で正しくオリジンを指定しましょう。
② OPTIONSメソッドが許可されていない
Spring Securityの設定で、OPTIONSメソッドが認可されていない場合、403 Forbiddenエラーが発生します。
対処法: Security設定でrequestMatchers(HttpMethod.OPTIONS, "/**").permitAll()を追加して対応します。
③ リクエストヘッダが制限されている
クライアントから送信されるヘッダ(例:Content-Type: application/jsonやAuthorization)が、Access-Control-Allow-Headersに含まれていない場合、プリフライトが拒否されます。
対処法: 必要なヘッダをallowedHeadersで明示的に許可してください。
④ Access-Control-Allow-MethodsにHTTPメソッドが含まれていない
リクエストしようとしているメソッド(例:PUTやDELETE)がサーバー側で許可されていないと、リクエストはブロックされます。
対処法: allowedMethodsで、必要なHTTPメソッドをすべて指定してください。
8. ブラウザでの確認方法(開発者ツールを使ったCORSチェック)
CORSに関するエラーやプリフライトの動作確認は、ブラウザの開発者ツールを使うのが基本です。ここではGoogle Chromeを例に、確認手順を説明します。
① 開発者ツールの開き方
ブラウザでページを開いた状態で、右クリック →「検証」またはF12キーを押します。「開発者ツール」が表示されます。
② Networkタブで通信内容を確認
「Network」タブを選択してからページを再読み込みし、該当するリクエスト(例:/api/data)をクリックします。
リクエスト詳細の中に「Headers」タブがあり、Request HeadersとResponse Headersを確認できます。
③ プリフライトリクエストの確認
OPTIONSメソッドによるプリフライトリクエストは、通常のリクエストの前に表示されます。以下のような点を確認しましょう:
- ステータスコードが
200であること Access-Control-Allow-Originが正しく設定されていることAccess-Control-Allow-Methodsに目的のメソッドが含まれていること
これらを確認することで、Controller CORS設定が正しく反映されているかをチェックできます。
プリフライトリクエストの挙動は一見見えづらいですが、開発者ツールを使えば通信の裏側を視覚的に把握することができます。
まとめ
今回の記事では、CORS(クロスオリジンリソースシェアリング)とプリフライトリクエスト、Spring SecurityにおけるOPTIONSメソッドの扱いについて、初心者向けに詳しく解説しました。CORSは、異なるオリジン間でのリソースアクセスを制御する仕組みであり、ブラウザのセキュリティ機能として重要です。特に、PUTやDELETEなど特殊なHTTPメソッドを使う場合や、Content-Typeにapplication/jsonなどのカスタムヘッダーを含むリクエストでは、プリフライトリクエストが発生します。この事前確認リクエストを正しく処理しないと、ブラウザは本来のリクエストをブロックしてしまい、403エラーや通信失敗の原因になります。
Spring BootやSpring MVC環境でプリフライトリクエストを正しく処理するには、まず@CrossOriginやWebMvcConfigurerでCORS設定を行うことが基本です。そして、Spring Securityを導入している場合は、SecurityFilterChainにcors()を有効化し、requestMatchers(HttpMethod.OPTIONS, "/**").permitAll()を追加することで、OPTIONSメソッドによるプリフライトリクエストを許可する必要があります。これにより、ControllerレベルでのAPI処理前にプリフライトがスムーズに通過し、クロスオリジン通信が安全かつ確実に行われます。
また、レスポンスヘッダの設定も非常に重要です。Access-Control-Allow-OriginやAccess-Control-Allow-Methods、Access-Control-Allow-Headers、Access-Control-Max-Ageを適切に設定することで、ブラウザがリクエストを許可し、同じプリフライトリクエストが繰り返されるのを防ぐことができます。SpringのCorsRegistryや@CrossOriginを活用すると、これらの設定を簡単に管理できます。
サンプルコードまとめ
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.cors().and()
.csrf().disable()
.authorizeHttpRequests(auth -> auth
.requestMatchers(HttpMethod.OPTIONS, "/**").permitAll()
.anyRequest().authenticated()
);
return http.build();
}
@Bean
public CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration config = new CorsConfiguration();
config.setAllowedOrigins(List.of("https://frontend.example.com"));
config.setAllowedMethods(List.of("GET", "POST", "PUT", "DELETE", "OPTIONS"));
config.setAllowedHeaders(List.of("Content-Type", "Authorization"));
config.setMaxAge(3600L);
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", config);
return source;
}
}
@Controller
@CrossOrigin(
origins = "https://frontend.example.com",
methods = {RequestMethod.GET, RequestMethod.POST, RequestMethod.PUT, RequestMethod.DELETE, RequestMethod.OPTIONS}
)
public class ApiController {
@PostMapping("/api/data")
@ResponseBody
public String postData() {
return "データを受け取りました";
}
}
これらの設定により、プリフライトリクエストが正しく処理され、クロスオリジン通信が安全に実行できるようになります。特に、Spring Security環境下では、CORS設定がSecurityFilterChainよりも先に適用されるように意識することが重要です。開発者ツールを活用して、リクエストとレスポンスのヘッダを確認しながら設定を進めると、初心者でも安全なAPI通信を実現できます。
新人
「プリフライトリクエストが何で必要なのか、やっと理解できました。ブラウザが勝手にOPTIONSで確認しているんですね。」
先輩
「そうだね。それを許可しないと、POSTやPUTのリクエストが全てブロックされてしまうから、SecurityFilterとCORS設定の順序が重要なんだ。」
新人
「なるほど、Controllerで@CrossOriginだけ設定しても、SecurityFilterChainが先に動くと意味がないんですね。」
先輩
「その通り。だからSpring Security側でcors()を有効にして、OPTIONSメソッドを許可する設定が必要なんだ。これでプリフライトがスムーズに通る。」
新人
「レスポンスヘッダもきちんと設定しないといけないんですね。Access-Control-Allow-OriginやAccess-Control-Allow-Methodsなどですね。」
先輩
「そう。ヘッダを正しく設定することで、ブラウザが安全にリクエストを送れるようになる。開発者ツールで確認しながら設定すると安心だよ。」
新人
「これで、CORSとプリフライトリクエスト、Spring Securityの関係がはっきり理解できました!」
先輩
「よく理解できたね。今回学んだことを踏まえて、実務でも安全にクロスオリジン通信を扱えるように練習してみよう。」