Spring Boot × React構成におけるCORS設定の解説:別ディレクトリ構成時の重要ポイント
はじめに
近年、フロントエンドとバックエンドを完全に分離した構成が一般化している。例えば、フロントエンドをReactで実装し、バックエンドをSpring Bootで実装する構成だ。この場合、それぞれが異なる開発環境(ディレクトリ・ポート)で動作することが多く、デフォルトの状態では通信が遮断される。これは「同一生成元ポリシー(Same-Origin Policy)」によるブラウザのセキュリティ機構によるものであり、この制約を越えて通信を可能にするのが「CORS(Cross-Origin Resource Sharing)」である。
本稿では、Spring BootとReactを別々のディレクトリで構築する際に必要となるCORSの設定方法、およびその背景にある原理とベストプラクティスを詳細に解説する。
—
同一生成元ポリシーとCORSの基本
同一生成元ポリシーとは、ブラウザがセキュリティ上の理由から、異なる「スキーム(http/https)・ホスト・ポート」の間でのリソース共有を原則として禁止する仕組みである。
例えば以下のような構成を考える:
Reactフロントエンド: http://localhost:3000
Spring Bootバックエンド: http://localhost:8080
この構成では、ブラウザからReactのアプリがhttp://localhost:8080/api/**へアクセスしようとすると、クロスオリジン通信と見なされる。これがブロックされるのを防ぐために、Spring側でCORSの許可設定を行う必要がある。
—
Spring BootにおけるCORS設定方法
Spring BootにはCORSを設定する方法が複数ある。以下では、代表的な3つの方法を紹介する。
方法①: @CrossOrigin アノテーションの使用(簡易)
もっとも手軽な方法は、コントローラメソッドやクラスに@CrossOriginを付与する方法である。
@RestController
@RequestMapping(“/api”)
@CrossOrigin(origins = “http://localhost:3000”)
public class SampleController {
@GetMapping(“/hello”)
public String hello() {
return “Hello from Spring Boot!”;
}
}
この方法は開発初期には有用だが、コントローラが増えた場合に各所へ設定を繰り返す必要があるため、可搬性に欠ける。
—
方法②: グローバルCORS設定(推奨)
本番環境や大規模プロジェクトでは、CORSをグローバルに一元管理する方が望ましい。以下のようにWebMvcConfigurerを実装することで設定可能だ。
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping(“/api/**”)
.allowedOrigins(“http://localhost:3000”)
.allowedMethods(“GET”, “POST”, “PUT”, “DELETE”, “OPTIONS”)
.allowedHeaders(“*”)
.allowCredentials(true);
}
}
ここで注目すべきは以下の点である:
addMapping: APIパスのパターン。/**では全体が対象になる。
allowedOrigins: フロントエンドのオリジン(Reactが配信されているドメインやポート)。
allowedMethods: 許可するHTTPメソッド。プリフライトリクエスト(OPTIONS)も明示的に追加。
allowCredentials(true): 認証情報(CookieやAuthorizationヘッダー)をやりとりする場合に必須。
—
方法③: セキュリティ設定と統合(Spring Security使用時)
Spring Securityを使っている場合、SecurityFilterChainレベルでもCORS設定を明示的に許可する必要がある。そうしないとWebMvcConfigurer側の設定が無効化される場合がある。
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.cors(Customizer.withDefaults())
.csrf(csrf -> csrf.disable())
.authorizeHttpRequests(authz -> authz
.requestMatchers(“/api/**”).permitAll()
.anyRequest().authenticated()
);
return http.build();
}
@Bean
public CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration config = new CorsConfiguration();
config.setAllowedOrigins(List.of(“http://localhost:3000”));
config.setAllowedMethods(List.of(“GET”, “POST”, “PUT”, “DELETE”, “OPTIONS”));
config.setAllowedHeaders(List.of(“*”));
config.setAllowCredentials(true);
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration(“/api/**”, config);
return source;
}
Securityを利用しているプロジェクトでは、この方法が事実上必須になる。
—
フロントエンド側の注意点
React側では、バックエンドAPIに対してFetchやAxiosでリクエストを送る際に、credentialsやheadersの設定も重要となる。
fetch(“http://localhost:8080/api/hello”, {
method: “GET”,
credentials: “include” // Cookieなどを送信する場合に必要
})
.then(response => response.text())
.then(data => console.log(data));
Axiosの場合:
axios.get(“http://localhost:8080/api/hello”, { withCredentials: true });
このwithCredentialsを使用する場合、Spring側でallowCredentials(true)が設定されている必要がある。
—
CORS設定時の落とし穴とベストプラクティス
ワイルドカードと認証の非両立
allowedOrigins(“*”)とallowCredentials(true)は併用できない。これはセキュリティ上の制約であり、認証情報を扱う場合はオリジンを明示する必要がある。
プリフライト(OPTIONS)リクエスト
Content-Type: application/jsonなど特定のヘッダーが含まれる場合、ブラウザは事前にOPTIONSリクエストを送信する。サーバーがこれを適切に処理しないと、本リクエストが実行されない。
本番環境でのCORS制限強化
本番ではオリジンをホワイトリスト化する、またはAPI GatewayなどでCORSを制御する設計も推奨される。開発時のlocalhost前提の緩い設定は残さないこと。
—
おわりに
ReactとSpring Bootを組み合わせたフロント/バックエンド分離型アーキテクチャにおいて、CORSはセキュリティと通信の両立に不可欠な要素である。開発環境と本番環境では必要な設定が異なるため、原理と挙動を理解した上で適切な実装が求められる。
プロジェクトの初期段階からグローバルCORS設定を導入し、必要に応じてSpring Securityと統合することが、中長期的な保守性と安全性の観点から推奨される構成である。