Spring Boot + Gradle + Thymeleaf
Webアプリ開発のメリット・デメリット・注意点(詳細解説)
1. 全体像
Spring Boot + Gradle + Thymeleaf は、JavaでクラシックなサーバーサイドWebアプリを素早く構築するための王道構成。
- Spring Boot:設定が少なく自動化が強力
- Gradle:高速ビルド、柔軟、マルチモジュールに強い
- Thymeleaf:HTMLを壊さないテンプレート、MVCとの統合が強力
2. メリット
2-1. Spring Boot のメリット
- 設定が少なく生産性が高い
- 組み込みTomcatでデプロイが簡単
- エコシステムが豊富(Security / Data / Validation / Actuator)
2-2. Gradle のメリット
- ビルドが高速
- 記述が短く柔軟
- マルチモジュール構成に強い
2-3. Thymeleaf のメリット
- HTMLのまま表示できるナチュラルテンプレート
- Spring MVC / Validation との親和性が高い
- フラグメント管理によるレイアウト分割が容易
- SSR(サーバーサイドレンダリング)でSEOにも強い
3. デメリット
3-1. Spring Boot のデメリット
- ブラックボックス化しやすい
- 小規模サービスには重い
- Jakarta移行でライブラリ対応に注意
3-2. Gradle のデメリット
- 学習コストが高め
- プロジェクトごとに書き方が違いがち
- キャッシュ・プラグイン相性でハマることがある
3-3. Thymeleaf のデメリット
- SPAのようなリッチUIには不向き
- テンプレートが肥大化しやすい
- エラー原因が分かりにくい
- 大量アクセス時のテンプレート性能に注意
4. 注意点
4-1. 設計の注意点
- Controller / Service / Repository / View を明確に分離
- フォームDTOとドメインモデルを必ず分ける
- Validation とエラーメッセージの設計を慎重に
- URL設計とテンプレート構成を最初に統一
4-2. セキュリティの注意点
- CSRFトークンの付与
- th:text でエスケープ(th:utext は危険)
- 画面だけで制御せずサーバー側の権限チェック必須
4-3. テンプレート・フロント構成の注意点
- レイアウト戦略(layout:decorate / th:replace)を初期に決める
- JS と Thymeleaf の役割分担を明確化
- static/css, static/js の構造を統一
- Webpack/Vite との併用なら参照パスを整理
4-4. Gradle の注意点
- Spring Boot プラグインと本体のバージョンを一致させる
- Java toolchain を明示
- マルチモジュール依存を厳格化して循環依存を防ぐ
- application-dev.yml / application-prod.yml を分ける
5. 向いているケース / 向いていないケース
向いているケース
- CRUD中心の業務系Webアプリ
- 管理画面
- 画面数は多いがUIはシンプル
- Java/Springに強いチーム
別構成が良い場合
- SPA級UI(React / Vue)
- モバイルアプリ向けRESTが中心
- 高トラフィック・低レイテンシが必須
6. Summary in English
Pros
- Spring Boot: minimal config, embedded server, rich ecosystem
- Gradle: fast builds, flexible DSL
- Thymeleaf: natural templates, strong MVC/Validation integration
Cons
- Spring Boot feels “black box” sometimes
- Gradle has learning curve
- Thymeleaf not ideal for SPA-level UI
Cautions
- Strict separation of roles
- Proper XSS/CSRF handling
- Clear layout/JS strategy
- Consistent environment setup
補足
- CSRF とは?
CSRF(Cross-Site Request Forgery) は、
ユーザー本人が意図しないリクエストを外部サイトから送信させる攻撃。
例:
被害者が既にログイン済み
悪意あるサイトにアクセス
背景で POST /account/delete のようなリクエストを勝手に送信
サイト側は「ログイン済みなので OK」と実行してしまう
なぜ成功する?
ブラウザは Cookie(セッションID)を自動送信する
攻撃者はユーザーのセッションで操作できてしまう
- CSRF対策の仕組み(CSRFトークン)
CSRFトークンとは?
毎回フォームや POST リクエストに埋め込まれる
「サーバー側が発行した一時的な秘密の値」
正しいトークンがないリクエストは拒否される
キモは:攻撃者はトークンを知らないこと
攻撃者は
被害者のブラウザが持つ Cookie(セッションID)は勝手に送れる
しかし
トークン(hidden input)やヘッダーは盗めない
→ よって正しい POST を作れない
- Spring Boot / Spring Security におけるCSRF
Spring Security は デフォルトで CSRF保護 ON。
3-1. 何が起きるか?
すべての 状態を変更する HTTPメソッド(POST / PUT / DELETE) に CSRFトークンが必要
トークンが無いと 403 Forbidden になる
- Thymeleaf での CSRF トークン埋め込み
Thymeleaf + Spring Security を使っている場合、
th:action を使えば自動でトークンが付く。
✔ 例:通常のフォーム
更新→ Spring Security が自動で hidden input を追加する:
あなたが追加する必要はない。
- 手動でトークンが必要な場合(Ajax / Fetch / axios)
JavaScriptで POST を送る場合は 自分でトークンを付与する必要がある。
5-1. Thymeleaf から埋め込む
HTML側でトークンを取得:
5-2. Fetch API で送る例
const token = document.querySelector('meta[name="_csrf"]').content;
const header = document.querySelector('meta[name="_csrf_header"]').content;
fetch('/api/update', {
method: 'POST',
headers: {
[header]: token,
'Content-Type': 'application/json'
},
body: JSON.stringify({ name: 'taro' })
});
- CSRFトークンの保存場所
Spring Security は以下のどれかに保存する:
HttpSession
Cookie
Header
デフォルトは HttpSession。
ログイン時にトークンが生成され、必要に応じて再生成される。
- CSRFを無効化するケース
ログインAPI専用サイト(SPA+REST)などで CSRFを無効にすることがある。
理由:
Cookieベースのセッションを使わず、JWT等を使う場合
公開APIでセッション管理しない場合
完全に stateless の場合
CSRF無効化設定(Spring Security 6 / Boot 3)
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.csrf(csrf -> csrf.disable());
return http.build();
}
- CSRFエラー(403 Forbidden)の典型的原因
- th:action を使わず action="xxx" と手書きした
→ hidden input が埋め込まれない
- JavaScriptでCSRFヘッダーを付け忘れた
Fetch, axios, jQuery.ajax で起こりやすい。
- PATCH / PUT / DELETE を form で送っている
ブラウザは form で POST しか送れないため、
隠しフィールド・Filter が必要。
- CSRFトークンが更新された(ログイン・再生成)
タブを長時間放置した時に起こることがある。
- 実際のプロジェクトでの注意点
✔ CSRF トークン再生成時の動作確認
ログイン時やセッション更新時にトークンは再生成されることがある。
長時間開いたページで「送信 → CSRFエラー」が起こる
再ログインを促すUIを作ることが望ましい
✔ SPA と SSR の混在の場合は特に注意
Spring Boot(サーバー側) + JavaScript(クライアント側)で
CSRF周りが分かれると難易度が上がる。
- まとめ
CSRF攻撃は「ユーザーのCookieを悪用する攻撃」
対策は「攻撃者が生成できない秘密の値(CSRFトークン)」をチェックする
Spring Security はデフォルトでCSRFを有効化
Thymeleaf の th:action は遷移フォームには自動で付与
JavaScriptによるPOSTはトークンを手動で付与する必要がある
セッション管理方式(Cookie/JWT)によってCSRF必要性が変わる
補足2
✅ 結論(まずこれだけ覚えればOK)
CSRFで“被害を受けるサイト”は悪意あるサイトではなく、あなたが普段使う「正しいサイト」です。
悪意あるサイト=攻撃者が用意した罠ページ
サイト側(=被害者サイト)=あなたがログインしている本物の正規サービス
CSRFが起きると、
悪意あるサイト →(ユーザーのブラウザを利用して)→
正規サイトに勝手にリクエストを送る
そして正規サイトは、
「ユーザーはログイン済みだから操作OKだな」
と思ってしまって、不正操作が成功する。
つまり、
騙されるのは「正規サイト」
悪いのは「攻撃者のサイト」
被害に遭うのは“正しいサイト”のユーザー
です。
🔥 シナリオを図解すると一発で理解できます
① ユーザーが正規サイトにログインする
例)銀行サイト https://bank.com
ログインすると、ブラウザに セッションID(Cookie) が保存される。
② その後、悪意あるサイト(攻撃者サイト)を見る
ユーザーは気づかないけど、背後でこんな HTML を実行:
③ ブラウザが正規サイトに POST 送信してしまう
ここが重要ポイント!
✔ ブラウザは「同じサイトへの Cookie を必ず付ける」
悪意あるサイト → bank.com に送信する時でも
ブラウザは自動で bank.com の Cookie(セッションID)を付ける。
つまり:
Cookie: JSESSIONID=あなたのログインセッション
④ 正規サイトは「ログイン済みの本人操作」と誤認する
bank.com はこう判断する:
セッションID → 「この人はログイン中」
リクエスト → 「送金処理だな」
トークンなし →(CSRF対策が無い場合)通ってしまう
結果:
⭐ 攻撃者が“正規サイトのあなたのアカウントで操作できてしまう”
❗ なぜ「正規サイト」が CSRF トークンを必要とするの?
理由はシンプル。
🔥 正規サイト(あなたが使っているサイト)が 攻撃者の偽フォームからのリクエストを拒否するため
正規サイトはこうチェックする:
「このリクエストは正規のページから送られた?」
「それとも攻撃者の偽サイトから送られた?」
これを判別するために
CSRFトークン(正規ページでしか生成されない秘密の値)
を使う。
✔ 本当の正規フォーム
✔ 攻撃者の偽フォーム
(トークンを入れられない)
攻撃者はトークンを知らないので 絶対にトークンを送れない。
だから正規サイトは判別できる。
💡 まとめ(わかりやすく)
役割 実体 CSRFの時どうなる?
正規サイト(本物) 金融サービス、SNS、ECなど “偽リクエスト”を受けてしまう被害者側
悪意あるサイト(攻撃者) 偽サイト、罠ページ ユーザーのブラウザを利用して本物サイトへ不正リクエスト
ユーザーのブラウザ Chrome, Safari など Cookie(ログイン状態)を自動送信してしまう
CSRFトークン 正規サイトだけが生成できる秘密の値 偽サイトは生成できないので防御可能
🎯 つまり……
CSRF対策が必要なのは悪意のない正規サイト
なぜなら
攻撃者は正規サイトに“偽の本人操作”を送るから
これが本質です。