運用コア — @Transactional · Spring Security
運用コア — @Transactional · Spring Security
🎯 このレッスンを読み終えたら
このレッスンを最後まで読み終えると、以下の3つを自信を持って行えるようになります。
- ▸✅ Spring Security Filter Chain の図 + JWT 認証フィルターの位置
- ▸✅ @PreAuthorize によるメソッドレベルの権限制御
- ▸✅ BCryptPasswordEncoder が標準である理由(MD5/SHA-1 は禁止)
学習目標をチェックリストとして手元に置き、すべて答えられるようになったらレッスンを閉じましょう。
@Transactional — *Spring で最も強力な一行*
核心の一行
@Transactional アノテーション一行で、そのメソッドを DB トランザクションの中で実行します。例外発生時は自動ロールバック、成功時はコミット。送金・決済のように原子性が重要な処理の標準です。
最もシンプルな使い方
DB の BEGIN・COMMIT・ROLLBACK を Spring が自動的にラップしてくれます。書き忘れる心配がありません。
プロキシベース — 外部呼び出しにのみ適用
Spring の @Transactional は AOP プロキシで動作します。メソッドが外部から呼び出されたときにのみトランザクションが適用されます。
同じクラス内のメソッド呼び出しはプロキシを迂回 → アノテーションが無視されます。
解決策:
- ▸別の Bean に分離する
- ▸または
AopContext.currentProxy()で自己プロキシを呼び出す
伝播 (Propagation) — トランザクションの中でさらにトランザクション
すでにトランザクションの中にいるのに、さらに @Transactional なメソッドを呼び出すとどうなるのでしょうか? 伝播オプションによって決まります。
最もよく使う: デフォルトの REQUIRED。ただし、ログ・監査記録のように親がロールバックされても記録は残さなければならない場合は REQUIRES_NEW を使います。
ロールバックルール — RuntimeException のみ自動
デフォルト: RuntimeException とそのサブクラス(Unchecked)のみが自動ロールバックの対象です。IOException のような Checked 例外はコミットされます。
これは Java 特有の設計 — 他の言語にはほとんどない区別です。知らないとデータ破損事故を起こしやすくなります。
解決策:
すべての例外でロールバック。通常これが安全なデフォルトです。Kotlin や最近の Spring コードではほぼ常に明示されています。
readOnly = true — 読み取り専用の最適化
参照のみを行うメソッドなら readOnly=true を設定します。JPA がダーティチェックをスキップしてパフォーマンスが向上します。Hibernate の FlushMode も自動的に NEVER になります。
クラス単位:
CQRS パターンと自然に調和します。
よくある落とし穴 5 つ
1. 内部呼び出し — 上記参照。同じクラス内のメソッド呼び出しはトランザクションを無視します。
2. Checked 例外のコミット — rollbackFor = Exception.class を明示する
3. private メソッド — プロキシは適用されません。public でなければなりません。
4. トランザクション内での外部 API 呼び出し — DB ロックが外部のレスポンス時間の分だけ保持されます。トランザクションの外で呼び出すことを推奨します。
5. トランザクションのスコープが広すぎる — メソッド全体をラップするとロックが長くなります。最小限のスコープに留めましょう。
まとめ
- ▸
@Transactional一行で自動トランザクション - ▸プロキシベース → 外部呼び出しにのみ適用
- ▸伝播オプションでネストの動作を制御
- ▸デフォルトは RuntimeException のみロールバック →
rollbackFor = Exception.classを推奨 - ▸読み取りメソッドは
readOnly = trueを使用
Spring Security — *認証と認可*
認証 vs. 認可
よく混同される2つの言葉:
- ▸認証 (Authentication) = あなたは誰ですか? 本人確認(ログイン)
- ▸認可 (Authorization) = それを行う権限がありますか? 権限確認(管理者ページへのアクセス)
Spring Security は両方を処理します。
最小構成 (Spring Security 6 / Spring Boot 3)
URL ごとの権限、セッションポリシー、JWT フィルターの登録 — すべて一つのチェーンで。
ログインフロー — 最もよく使われるパターン
1. パスワード検証 (Authentication Manager)
核心:bcrypt によるパスワードのハッシュ比較。平文は絶対 NG。
2. JWT 発行 + レスポンス
- ▸Access Token — 15 分間、API 呼び出しのたびにヘッダーに付与
- ▸Refresh Token — 7 日間、httpOnly クッキー(XSS 対策)
3. 以降のリクエスト — JwtAuthenticationFilter
リクエストごとにトークン検証 → SecurityContext にユーザー情報を保存 → 他の場所から @AuthenticationPrincipal でアクセス。
メソッドレベルの認可
URL 単位ではなくメソッド単位での権限制御。SpEL (Spring Expression Language) で複雑なルールを表現できます。
有効化:設定クラスに @EnableMethodSecurity を追加。
OAuth 2.0 — ソーシャルログイン
自前で実装せず、Spring Security OAuth2 Client を使用:
Spring がOAuth フロー全体(リダイレクト・トークン交換・ユーザー情報取得)を処理します。コールバックで自分たちの DB のユーザーとのマッピングだけ行えば OK。
よくあるセキュリティの落とし穴
1. CSRF disable の乱用 — REST API + JWT なら OK。セッションクッキーベースなら有効にする必要があります。
2. CORS が緩すぎる — * は禁止。明示的なオリジンリストを使用。
3. パスワードの平文保存 — 事故寸前。bcrypt または argon2 は必須。
4. JWT シークレットの露出 — 環境変数を使用。絶対にハードコードしない。
5. SQL インジェクション — JPA またはパラメータ化クエリを常に使用。
まとめ
- ▸Spring Security = 認証・認可の標準
- ▸JWT がモダントレンド(ステートレス・モバイルフレンドリー)
- ▸パスワードは bcrypt、JWT シークレットは環境変数、CSRF・CORS は適切に設定
- ▸メソッドレベルの権限制御は
@PreAuthorize - ▸ソーシャルログインは OAuth2 Client で一行
Spring Security フィルターチェーン · JWT フィルター実装
Security の核心 — フィルターチェーン
Spring Security は数十個のフィルターをチェーンで連結します。HTTP リクエストが来ると、順番にすべてのフィルターを通過してからコントローラーに到達します。
JWT 認証を追加するには、チェーンに自分のフィルターを挿入する必要があります。
JWT フィルター — OncePerRequestFilter を継承
OncePerRequestFilter は1 リクエストにつき 1 回だけ実行されることを保証します。Spring の forward・include による重複実行を防止します。
SecurityFilterChain の登録
.addFilterBefore(...) で任意の位置にフィルターを挿入できます。
メソッドレベルの権限制御 — @PreAuthorize
SpEL (Spring Expression Language) で複雑な権限条件を表現できます。「本人か ADMIN のみ」といったパターンが一行で書けます。
まとめ
- ▸フィルターチェーン — リクエスト処理のパイプライン
- ▸OncePerRequestFilter — JWT のようなカスタムフィルターの標準ベースクラス
- ▸@PreAuthorize — コントローラー・サービスレベルの権限制御
- ▸BCryptPasswordEncoder — パスワードは必ず bcrypt を使用
面接で「Security をどのように適用しましたか?」と聞かれたら、この 4 つを挙げられるようにしておきましょう。
🤖 AI にこう依頼してみましょう
このレッスンの概念を知っていると、AI に具体的に指示できます。漠然とした「直して」ではなく、語彙を持ったリクエスト — それがトークン節約の出発点です。
- ▸「この SecurityConfig に JWT 認証フィルター (OncePerRequestFilter) を追加して」
- ▸「このメソッドを @PreAuthorize('hasRole(ADMIN)') で保護して」
- ▸「BCryptPasswordEncoder を Bean として登録して」
なぜこれがトークンを減らすのか
概念を知らないと、AI の回答を受け取っても「それって何ですか?」と再度聞かなければなりません。その「再質問」がトークンを消費します。概念を一度身につければ、会話が一度で終わります。