Spring Securityを入れた瞬間、なぜ全部が認証必須になるのか?
spring-boot-starter-securityを依存に入れると、Spring Bootはデフォルトのセキュリティ設定を自動適用します
- 既定で ユーザー名はuser
- パスワードは起動時にランダム生成され、ログに出ます(開発用)
- そして基本的に すべてのエンドポイントが認証必須になりがちで、「何もしてないのに401」が発生します(=正常動作)
つまり最初にやることは、「何をどこまで守るか」を自分のアプリの要件に合わせて明示することです
まず押さえる:Spring Security 6(Boot 3)では SecurityFilterChain が基本
昔の定番 WebSecurityConfigurerAdapterは廃止(削除)され、現行では SecurityFilterChain をBean定義して構成します
さらに、よく見る設定メソッドも変化しています:
authorizeRequests()→authorizeHttpRequests()が主流(/ 6以降は移行が前提)
以下Springページを参考ください
【コピペOK】最小の SecurityFilterChain(まず動く認可設定)
「とりあえず GET /health とSwaggerは誰でもOK、その他は認証必須」みたいな最小例です
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests(auth -> auth
.requestMatchers("/actuator/health", "/swagger-ui/**", "/v3/api-docs/**").permitAll()
.anyRequest().authenticated()
)
.httpBasic(Customizer.withDefaults()); // まずは動作確認しやすい Basic 認証
return http.build();
}
}ポイントは2つだけ:
authorizeHttpRequestsで permitAll と authenticated の境界を決める- まずは
httpBasicなど単純な方式で “認証の配線”が動くことを確認する
401/403で詰まる人が多い原因トップ3と即対処
原因1:permitAllのつもりがマッチしてない(パス指定ミス)
/api/**のつもりが/apiだけになってるrequestMatchersの順序や条件が意図と違う
対処:まずはanyRequest().authenticated()を最後に置く、permitAllは具体→抽象で書く
原因2:POSTしたら403(だいたいCSRF)
ブラウザフォームを想定した構成だと、Spring SecurityはCSRF対策を有効にしがちで、トークンなしPOSTは403になります
対処の考え方は「あなたのアプリがどちらか」で決まります
- セッション+画面(フォーム送信):CSRFは基本ONのまま → トークンを正しく扱う
- REST API(JWT等でステートレス):CSRFをOFFにする設計が多い(ただし条件付き)
原因3:ブラウザからだけ失敗(だいたいCORS or preflight)
フロント(例:localhost:3000)→ API(例:localhost:8080)だとCORSで落ちます
特に OPTIONS(preflight)が弾かれるのが定番です
対処:CORSを“セキュリティ設定の中”で許可する(後述テンプレあり)
【実務テンプレ】REST API(JWT)構成の最小例(CORS/CSRF込み)
やることは大きく3つ:
- セッションを使わない(ステートレス)
- JWTを検証する(Resource Server)
- CORSを通す(ブラウザから呼ぶなら必須)
Spring SecurityのResource Server(JWT)の基本は公式リファレンスが土台になります
application.yml(issuer-uri の例)
spring:
security:
oauth2:
resourceserver:
jwt:
issuer-uri: https://idp.example.comSecurityFilterChain(JWT + CORS + CSRF)
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.csrf(csrf -> csrf.disable()) // ステートレスAPIならOFFが多い(要件次第)
.cors(Customizer.withDefaults())
.sessionManagement(sm -> sm.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
.authorizeHttpRequests(auth -> auth
.requestMatchers("/actuator/health", "/swagger-ui/**", "/v3/api-docs/**").permitAll()
.anyRequest().authenticated()
)
.oauth2ResourceServer(oauth2 -> oauth2.jwt(Customizer.withDefaults()));
return http.build();
}
@Bean
CorsConfigurationSource corsConfigurationSource() {
var 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);
var source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", config);
return source;
}
}「ローカルだけ認証なし」にしたい人向け(開発効率系の検索が多い)
開発中だけ「全部permitAll」 にしたい場合はProfileで切り替えるのがおすすめ
@Bean
@Profile("local")
SecurityFilterChain localSecurity(HttpSecurity http) throws Exception {
http
.csrf(csrf -> csrf.disable())
.authorizeHttpRequests(auth -> auth.anyRequest().permitAll());
return http.build();
}ぜひ参考ください!
是非フォローしてください
最新の情報をお伝えします
