Java

Spring Bootの認証をSpring Securityで実装する方法【Spring Security 6/7対応・2026年版】

Spring Bootで認証機能を作ろうとして、参考にしたコードがコンパイルすら通らない——2026年時点でよく起きる状況だ。原因の多くは、記事で使われている WebSecurityConfigurerAdapterauthorizeRequests() が Spring Security 6 で削除されている点にある。本記事は、現行の Spring Boot 3.5/4.1(それぞれ Spring Security 6.5/7.1)で実際に動く書き方に絞り、フォームログイン・データベース認証・SPA向けのJWT認証までを実装コード付きで解説する。

まとめ:現行のSpring Bootで認証を実装する要点

  • 現行の設定は SecurityFilterChain を返すBean+ラムダDSL。WebSecurityConfigurerAdapter は Spring Security 6 で削除され、旧コードはそのままでは動かない。
  • 認可の指定は authorizeHttpRequestsrequestMatchers(旧 authorizeRequestsantMatchers は不可)。パッケージも javax.* から jakarta.* へ変わった。
  • DB上のユーザーで認証するには UserDetailsService の実装と PasswordEncoder(BCrypt)をBean登録する。パスワードのハッシュ化は必須。
  • SPA・REST APIではセッションよりトークン(JWT)認証が基本。JJWT 0.12系は verifyWithparseSignedClaims を使う(旧 setSigningKeyparseClaimsJws は非推奨)。
  • Spring Boot 4.1 は Spring Security 7.1・Java 17以上で、ラムダDSLが必須。3.5系は2026年6月末でEOL。

以下、認証の全体像から実装コード、旧バージョンからの移行時の注意までを順に見ていく。

Spring Securityによる認証の全体像

Spring Securityは、Spring Bootに依存関係を1つ追加するだけで全リクエストに認証を要求する状態を作れる。その分、どのコンポーネントが何を担当するかを把握しないと、設定をどこに書くべきか迷いやすい。基礎的な位置づけはSpring Securityとは何か?その基本と重要性を解説にまとめているので、ここでは認証を実装する上で押さえるべき登場人物に絞る。

認証(Authentication)と認可(Authorization)の違い

認証はリクエストの送信者が誰かを確かめる処理で、ユーザー名とパスワードやトークンの照合がこれにあたる。認可は、認証済みの利用者がそのリソースにアクセスしてよいかを判定する処理で、Spring Securityでは requestMatchers によるURL単位の制御や、ロール(権限)による制御で表現する。「ログインできるか」が認証、「その画面を開けるか」が認可、と分けて考えると設定の置き場所を間違えにくい。

認証処理の流れ:AuthenticationManagerからUserDetailsServiceまで

フォームログインの場合、送信された資格情報はまず認証用フィルタが Authentication オブジェクトに変換し、AuthenticationManager(標準実装は ProviderManager)へ渡す。ProviderManager は登録された AuthenticationProvider に処理を委譲し、DB認証では DaoAuthenticationProviderUserDetailsService でユーザーを取得し、PasswordEncoder でパスワードを照合する。成功すると認証済みの AuthenticationSecurityContext に格納され、後続の処理から参照できる。フィルタの連鎖そのものの仕組みはSecurityFilterChainとは何かを初心者にもわかりやすく解説で詳しく扱っている。

Spring Bootへの導入と最小構成のセキュリティ設定

依存関係の追加とデフォルトの認証動作

まず spring-boot-starter-security を追加する。MavenとGradleそれぞれの記述は次のとおり。

<!-- Maven: pom.xml -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-security</artifactId>
</dependency>
// Gradle: build.gradle
implementation 'org.springframework.boot:spring-boot-starter-security'

依存関係を足して起動すると、全エンドポイントが認証必須になり、ユーザー名 user の自動生成パスワードが起動ログに Using generated security password: として一度だけ出力される。これは動作確認用で、本番では必ず後述の独自認証に置き換える。

SecurityFilterChainによるアクセス制御(ラムダDSL)

Spring Security 6以降は、設定クラスを継承する方式が廃止され、SecurityFilterChain をBeanとして返す方式に統一された。認可ルールは authorizeHttpRequests の中にラムダで書く。/public 配下だけ公開し、残りを認証必須にする最小構成は次のとおり。

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.web.SecurityFilterChain;

@Configuration
public class SecurityConfig {

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http
            .authorizeHttpRequests(auth -> auth
                .requestMatchers("/public/**").permitAll()
                .anyRequest().authenticated());
        return http.build();
    }
}

ポイントは3つ。設定クラスに @EnableWebSecurity を付ける必要は(Spring Boot利用時は)なく、@ConfigurationSecurityFilterChain Beanだけで足りる。authorizeRequests ではなく authorizeHttpRequestsantMatchers ではなく requestMatchers を使う。そして .and() でつなぐ必要はなく、各設定をラムダで独立して書く。

フォームログインとログアウトの設定

ブラウザ向けのフォームログインとログアウトも同じくラムダで指定する。独自ログイン画面を使う場合は loginPage にパス(例 /login)を渡す。

@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
    http
        .authorizeHttpRequests(auth -> auth
            .requestMatchers("/public/**", "/login").permitAll()
            .anyRequest().authenticated())
        .formLogin(form -> form
            .loginPage("/login")
            .defaultSuccessUrl("/", true)
            .permitAll())
        .logout(logout -> logout
            .logoutUrl("/logout")
            .logoutSuccessUrl("/login?logout")
            .permitAll());
    return http.build();
}

loginPage を指定したら、その /login を返すコントローラとテンプレートは自分で用意する。指定しなければSpring Securityが標準のログイン画面を自動生成するので、まず動作を見たい段階では formLogin(withDefaults()) だけでよい。

データベース上のユーザーで認証する

実運用では、固定ユーザーではなくデータベースに保存したユーザーで認証する。必要なのは、ユーザーのエンティティとリポジトリ、そして UserDetailsService の実装、パスワード照合用の PasswordEncoder の4点だ。

ユーザーエンティティとリポジトリ

ユーザー名・ハッシュ化済みパスワード・ロールを持つエンティティを定義する。Spring Boot 3以降は永続化アノテーションが jakarta.persistence パッケージに移っている点に注意する(javax.persistence ではない)。

import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;

@Entity
public class UserAccount {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String username;
    private String password;   // BCryptでハッシュ化した値を保存する
    private String role;       // 例: ROLE_USER, ROLE_ADMIN

    // getter / setter は省略
}
import org.springframework.data.jpa.repository.JpaRepository;
import java.util.Optional;

public interface UserAccountRepository extends JpaRepository<UserAccount, Long> {
    Optional<UserAccount> findByUsername(String username);
}

UserDetailsServiceの実装とパスワードのハッシュ化

UserDetailsService は、ユーザー名からユーザー情報を取り出して UserDetails に詰め替えるだけの単純な部品だ。取得できなければ UsernameNotFoundException を投げる。User.withUsername() を使えば独自の UserDetails クラスを自作せずに済む。

import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;

@Service
public class DbUserDetailsService implements UserDetailsService {

    private final UserAccountRepository repository;

    public DbUserDetailsService(UserAccountRepository repository) {
        this.repository = repository;
    }

    @Override
    public UserDetails loadUserByUsername(String username) {
        UserAccount user = repository.findByUsername(username)
            .orElseThrow(() -> new UsernameNotFoundException("user not found: " + username));
        return User.withUsername(user.getUsername())
            .password(user.getPassword())   // 保存済みのハッシュ値
            .authorities(user.getRole())
            .build();
    }
}

パスワードは必ずハッシュ化して保存し、照合器として PasswordEncoder をBean登録する。標準はBCryptで、より強度を上げたい場合は Argon2PasswordEncoder も選べる。UserDetailsServicePasswordEncoder をBeanとして置いておけば、Spring Bootが DaoAuthenticationProvider を自動で組み立てる。

import org.springframework.context.annotation.Bean;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;

@Bean
public PasswordEncoder passwordEncoder() {
    return new BCryptPasswordEncoder();
}

// ユーザー登録時は encoder.encode(rawPassword) の戻り値を保存する

AuthenticationManagerを明示的に公開する場合

REST APIのログインエンドポイントを自作するなど、AuthenticationManager を自分で呼び出したい場面がある。Spring Security 6以降は AuthenticationConfiguration から取り出してBean公開するのが定石だ。

import org.springframework.context.annotation.Bean;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;

@Bean
public AuthenticationManager authenticationManager(AuthenticationConfiguration config) throws Exception {
    return config.getAuthenticationManager();
}

プロバイダを明示的に組みたいときは、DaoAuthenticationProviderUserDetailsServicePasswordEncoder を設定し、ProviderManager でくるんで返す方法もある。ただし前述のBean登録だけで多くのケースは足りるため、まずは自動構成を使い、独自の AuthenticationProvider が必要になったら差し替える判断でよい。

SPA・REST APIでのステートレス認証(JWT)

ReactやVueで作るSPAからSpring BootのAPIを叩く構成では、サーバー側でセッションを持たず、トークン(JWT)で認証状態を運ぶのが一般的だ。フロントエンドとの連携全体(CORSやトークンの受け渡し)は【2026年版】Spring BootとReactを連携する方法|REST API・CORS・JWT認証まで実装解説で扱っているので、ここではSpring Security側の実装に絞る。

セッション認証とトークン認証の使い分け

まず前提を整理する。両者は排他ではなく、配信構成で選ぶものだ。

観点 セッション認証 トークン認証(JWT)
状態 サーバーが保持(ステートフル) サーバーは保持しない(ステートレス)
保存先 Cookie(セッションID) クライアント(Authorizationヘッダ等)
CSRF対策 必要 ヘッダ送信なら基本不要
水平スケール 共有ストア(Redis等)が要る 不要(鍵の共有のみ)
向く構成 同一ドメイン配信のSPA・従来型Web 別ドメインAPI・モバイル併用

フロントとバックを同じドメインで配信するなら、セッション+Cookieでも安全に組める。JWTが有利になるのは、APIを別ドメインで公開したり、モバイルアプリと共通化したりする場合だ。「流行っているから」でJWTを選ぶと、失効管理やトークン漏洩対策の手間が増える点は割り切って判断したい。

JWTの発行と検証(JJWT 0.12系)

広く使われる io.jsonwebtoken(JJWT)は0.12でAPIが刷新された。古い setSigningKey(String)parseClaimsJws は非推奨で、現行は署名鍵に SecretKey を渡し、verifyWithparseSignedClaims を使う。

import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.security.Keys;
import javax.crypto.SecretKey;
import java.util.Date;

// 256bit以上の鍵を安全に管理する(ハードコードしない)
SecretKey key = Keys.hmacShaKeyFor(secretBytes);

// 発行
String jwt = Jwts.builder()
    .subject(username)
    .issuedAt(new Date())
    .expiration(new Date(System.currentTimeMillis() + 3600_000)) // 1時間
    .signWith(key)
    .compact();

// 検証・読み取り
String subject = Jwts.parser()
    .verifyWith(key)
    .build()
    .parseSignedClaims(jwt)
    .getPayload()
    .getSubject();

この検証処理を認証用フィルタ(例: OncePerRequestFilter の実装)に置き、Authorization ヘッダの Bearer トークンを取り出して SecurityContext に認証情報を設定する、という流れになる。

CSRF・CORSの設定とSPAでの注意点

ステートレスなJWT認証では、セッションを作らせない設定にしたうえでCSRFを無効化する。トークンをヘッダで送る限りCSRFの前提(Cookie自動送信)が成立しないためだ。

import org.springframework.security.config.http.SessionCreationPolicy;

http
    .csrf(csrf -> csrf.disable())
    .sessionManagement(session -> session
        .sessionCreationPolicy(SessionCreationPolicy.STATELESS))
    .cors(cors -> {});   // CorsConfigurationSource Beanで許可オリジンを定義

ただし csrf().disable() は「ステートレスAPIだから」という理由がある場合の判断だ。セッション+Cookieで認証するSPAで安易にCSRFを無効化するのは危険で、その場合は無効化ではなく CookieCsrfTokenRepository.withHttpOnlyFalse() でトークンをJSから読める形にして送り返す構成を採る。CORSは CorsConfigurationSource Beanで許可オリジン・メソッド・ヘッダを明示し、ワイルドカードの多用は避ける。

認証情報の取得と、避けるべき設定

SecurityContextHolderからログインユーザーを取得

認証済みユーザーの情報は SecurityContextHolder から取得できる。コントローラでは @AuthenticationPrincipal で直接受け取るほうが簡潔だ。

import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.web.bind.annotation.GetMapping;

@GetMapping("/me")
public String me(@AuthenticationPrincipal UserDetails user) {
    return user.getUsername();
}

// フィルタやサービス層からは SecurityContextHolder を直接参照する
// Authentication auth = SecurityContextHolder.getContext().getAuthentication();

やってはいけない設定・失敗パターン

認証まわりの事故は、動くコードをそのまま本番へ持ち込んだ結果として起きやすい。次の設定は避ける。

パスワードの平文保存。 NoOpPasswordEncoder は検証・学習用途に限る。本番でこれを使うと、DB流出時に全ユーザーのパスワードがそのまま漏れる。必ずBCryptなどでハッシュ化する。

署名鍵のハードコードと短い鍵。 JWTの署名鍵をソースに直書きすると、リポジトリ経由で漏洩する。HS256は256bit(32バイト)以上の鍵が必要で、短い文字列はJJWTが例外を投げる。鍵は環境変数やシークレットマネージャで管理する。

ブラウザ認証でのCSRF無効化。 セッション+Cookieで認証しているのにCSRFを切ると、正規利用者になりすました強制リクエストを防げない。無効化はステートレスAPIに限定する。

permitAll() の付けすぎ。 動作確認のために広く許可したまま公開すると、保護すべきエンドポイントが素通しになる。anyRequest().authenticated() を基本線にし、公開範囲は最小にする。

Spring Security 5→6→7で変わった点(古いコードが動かない理由)

ネット上のSpring Security解説は5系(Spring Boot 2系)を前提にしたものが今も多く、そのままでは現行環境でコンパイルが通らない。つまずきやすい変更点を整理しておく。

WebSecurityConfigurerAdapterの廃止とSecurityFilterChain

Spring Security 6.0で WebSecurityConfigurerAdapter は削除された。継承して configure(HttpSecurity) をオーバーライドする書き方はもう使えず、SecurityFilterChain をBeanとして返す構成に置き換える。設定を「継承」から「Beanの組み立て」へ発想を変えるのがこの移行の核心だ。

authorizeRequests/antMatchers から authorizeHttpRequests/requestMatchers へ

認可DSLも改名された。authorizeRequests()authorizeHttpRequests()、URLパターン指定の antMatchers()mvcMatchers()requestMatchers() に統一された。あわせて .and() による連結は不要になり、各設定をラムダで独立して書く「ラムダDSL」が標準になった。ラムダDSLはSpring Security 7.0では必須で、旧スタイルは使えなくなる。

javax→jakarta とバージョン対応表

Spring Boot 3でJakarta EE 9+へ移行したため、javax.servlet.*javax.persistence.*jakarta.* に変わった。フィルタやエンティティのimport文を機械的に置換する必要がある。主要バージョンの対応は次のとおり。

Spring Boot Spring Framework Spring Security Java(最小) 状態(2026-07)
4.1 7.0 7.1 17 最新(2026-06)
4.0 7.0 7.0 17 サポート中
3.5 6.2 6.5 17 2026-06-30 EOL
3.4 6.2 6.4 17 EOL済
2.7 5.3 5.8 8 OSS EOL済

これから新規に書くなら、ラムダDSLを前提にSpring Boot 3.5以上、可能なら4.1で組むのが無難だ。バージョンごとのサポート期限はSpring Bootバージョン一覧とサポート期限【2026年7月】最新4.1と3.5 EOL後の選び方、4系の変更点はSpring Boot 4とは?最新バージョン4.1の変更点・新機能とSpring Boot 3との違いを解説【2026年最新】を参照してほしい。

よくある質問

Spring Bootのデフォルトのログインパスワードはどこで確認できますか?

アプリ起動時のコンソールログに Using generated security password: の行が一度だけ出力され、そこに表示されます。ユーザー名は user です。この自動生成パスワードは動作確認用なので、本番では UserDetailsService による独自認証に置き換えます。

WebSecurityConfigurerAdapterが使えないのはなぜですか?

Spring Security 6.0で削除されたためです。SecurityFilterChain をBeanとして返す構成へ書き換えてください。継承ベースの旧コードは、Spring Boot 3以降ではコンパイルできません。

認証と認可はどう違いますか?

認証は「誰か」を確かめる処理(ログインの成否)、認可は「何を許すか」を判定する処理(画面やAPIへのアクセス可否)です。Spring Securityでは認証を UserDetailsService 側、認可を requestMatchers 側で表現します。

SPA(ReactやVue)ではセッションとJWTのどちらを使うべきですか?

フロントとAPIを同一ドメインで配信するなら、セッション+Cookie(CSRFトークン付き)で十分かつ安全です。APIを別ドメインで公開する、モバイルアプリと認証を共通化する、といった要件があるときにJWTを選びます。

パスワードは平文で保存してよいですか?

いけません。PasswordEncoder(BCryptやArgon2)でハッシュ化して保存します。NoOpPasswordEncoder は平文をそのまま扱うため、学習・検証用途に限り、本番では使わないでください。

関連記事

資料請求

RELATED POSTS 関連記事