【Spring Boot 3対応】Spring Securityの最小構成:SecurityFilterChainで「まず動く」認証・認可と、401/403の潰し方 (テンプレあり)


Spring Securityを入れた瞬間、なぜ全部が認証必須になるのか?

spring-boot-starter-securityを依存に入れると、Spring Bootはデフォルトのセキュリティ設定を自動適用します

  • 既定で ユーザー名はuser
  • パスワードは起動時にランダム生成され、ログに出ます(開発用)
  • そして基本的に すべてのエンドポイントが認証必須になりがちで、「何もしてないのに401」が発生します(=正常動作)

つまり最初にやることは、「何をどこまで守るか」を自分のアプリの要件に合わせて明示することです


まず押さえる:Spring Security 6(Boot 3)では SecurityFilterChain が基本

昔の定番 WebSecurityConfigurerAdapterは廃止(削除)され、現行では SecurityFilterChain をBean定義して構成します

さらに、よく見る設定メソッドも変化しています:

  • authorizeRequests()authorizeHttpRequests() が主流(/ 6以降は移行が前提)
    以下Springページを参考ください

https://docs.spring.io/spring-security/reference/servlet/authorization/authorize-http-requests.html?utm_source=chatgpt.com


【コピペ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つだけ:

  1. authorizeHttpRequestspermitAll と authenticated の境界を決める
  2. まずは 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.com

SecurityFilterChain(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();
}

ぜひ参考ください!

是非フォローしてください

最新の情報をお伝えします