Thymeleafでログイン画面を作成!Spring Boot環境構築からフォーム実装まで解説
新人
「Spring BootでWebアプリケーションを作っているのですが、ログイン画面をどうやって作ればいいか迷っています。Thymeleaf(タイムリーフ)を使うのが一般的だと聞いたのですが...。」
先輩
「そうですね。JavaのフレームワークであるSpring Bootでは、テンプレートエンジンとしてThymeleafを利用するのが非常に強力です。HTMLの見た目を保ったまま、動的な処理を簡単に書けますからね。」
新人
「難しそうですね。何から準備を始めればいいのでしょうか?入力フォームでユーザー名やパスワードを受け取る仕組みも詳しく知りたいです。」
先輩
「まずは開発環境を整えるところからスタートしましょう。Thymeleaf特有の属性の使い方を覚えれば、初心者の方でもスムーズにログイン画面が作れますよ。では、具体的な手順を解説していきますね!」
1. Thymeleafでログイン画面を作成する準備:Spring Bootの環境構築
プログラミングを始めたばかりの方にとって、「環境構築」という言葉は少し難しく感じるかもしれません。 簡単に言うと、Javaという言語を使ってWebサイトを動かすための「道具箱」をパソコンの中に用意する作業のことです。 今回は、世界中で使われている「Spring Boot(スプリングブート)」という便利なツールセットと、画面を作るための「Thymeleaf(タイムリーフ)」を使います。
必要なツールを揃えよう
まずは、以下のツールがパソコンに入っているか確認しましょう。
- JDK(Java Development Kit):Javaのプログラムを動かすための基本ソフトです。
- IDE(統合開発環境):プログラムを書くための専用メモ帳のようなものです。「STS(Spring Tool Suite)」や「IntelliJ IDEA」がおすすめです。
- Build Tool(ビルドツール):必要な部品を自動で集めてくれる仕組みです。今回は「Maven(メイヴン)」または「Gradle(グレードル)」を使います。
プロジェクトの作成手順
Spring Initializr(スプリング・イニシャライザ)という公式サイトを使うと、ボタンをクリックするだけでプロジェクトの雛形が作れます。 以下の「依存関係(Dependencies)」を追加することを忘れないでください。
| 依存関係の名前 | 役割 |
|---|---|
| Spring Web | Webアプリケーション(ブラウザで見れるサイト)を作るための中心的な部品です。 |
| Thymeleaf | HTMLの中にJavaのデータを埋め込むための「テンプレートエンジン」です。 |
| Spring Boot DevTools | プログラムを書き換えたときに、自動で反映してくれる便利な補助ツールです。 |
プロジェクトが作成できたら、次は「コントローラー」というクラスを作ります。 コントローラーは、ユーザーが「ログイン画面を見せて!」とリクエスト(お願い)したときに、どのHTMLを表示するかを判断する「司令塔」の役割を果たします。
package com.example.demo.controller;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import com.example.demo.model.LoginForm;
@Controller
public class LoginController {
@GetMapping("/login")
public String showLoginPage(Model model) {
// フォームのデータを格納するための空のオブジェクトを渡す
model.addAttribute("loginForm", new LoginForm());
// templatesフォルダの中にある login.html を表示する
return "login";
}
}
上記のコードでは、/loginというURLにアクセスしたときに、login.htmlというファイルを表示するように設定しています。
Modelというのは、Java側からHTML側にデータを運ぶための「運び屋」のようなものです。
2. ログインフォーム의 基本構造:Thymeleafのth:actionとth:object
環境が整ったら、いよいよブラウザに表示されるHTMLファイルを書いていきましょう。
普通のHTMLと違うのは、タグの中にth:から始まる特別な合言葉を書くことです。
これがThymeleafの最大の特徴です。
フォームの土台を作る
ログイン画面には、ユーザー名やパスワードを入力して送信するための「フォーム」が必要です。
ここで重要なのが、th:actionとth:objectという2つの設定です。
th:action:ボタンを押したときに、どの宛先にデータを送るかを指定します。通常のHTMLのaction属性をパワーアップさせたものです。
th:object:フォーム全体で「どのJavaオブジェクトにデータを紐付けるか」を宣言します。先ほどのコントローラーで用意した「loginForm」を指定します。
実際のHTMLコード(Thymeleaf)を見てみましょう。
ファイル名は src/main/resources/templates/login.html として作成します。
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>ログイン画面</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
</head>
<body class="bg-light">
<div class="container mt-5">
<div class="row justify-content-center">
<div class="col-md-6">
<div class="card shadow">
<div class="card-header bg-primary text-white text-center">
<h3>ログイン</h3>
</div>
<div class="card-body">
<form th:action="@{/login}" th:object="${loginForm}" method="post">
</form>
</div>
</div>
</div>
</div>
</div>
</body>
</html>
このコードのth:action="@{/login}"に注目してください。
@{}という書き方は、URLを指定するためのThymeleaf独自の書き方です。
また、th:object="${loginForm}"によって、「このフォームで入力された内容は、Java側のloginFormという箱に入れてね」という約束をしています。
3. ユーザー名とパスワードの入力を受け取る:th:fieldの役割と使い方
最後に、具体的な入力欄(インプットボックス)を作ります。
ここで登場するのがth:fieldという属性です。
これは、未経験者の方が一番最初につまずきやすいポイントですが、仕組みを理解すればとても簡単です。
th:fieldがやってくれること
th:fieldを使うと、以下の3つのことを裏側で自動的に処理してくれます。
- id属性の設定:HTML要素を識別するための名前を自動で付けます。
- name属性の設定:Java側にデータを送る際の「ラベル名」を自動で付けます。
- value属性の同期:Java側の変数に入っている値を、自動的に入力欄に表示します。
それでは、ユーザー名(username)とパスワード(password)の入力欄を追加したコードを確認しましょう。
<div class="mb-3">
<label for="username" class="form-label">ユーザー名</label>
<input type="text" th:field="*{username}" class="form-control" placeholder="ユーザー名を入力してください">
</div>
<div class="mb-3">
<label for="password" class="form-label">パスワード</label>
<input type="password" th:field="*{password}" class="form-control" placeholder="パスワードを入力してください">
</div>
<div class="d-grid gap-2">
<button type="submit" class="btn btn-primary">ログイン</button>
</div>
ここで注目すべきは、th:field="*{username}" という書き方です。
*{}(アスタリスク)は、th:objectで指定したオブジェクトの中身を直接参照することを意味します。
つまり、「loginFormの中にあるusernameという項目を使いますよ」という意味になります。
Java側の受け皿(DTO)の作成
HTML側でth:fieldを使ったら、それを受け取るためのJavaクラスも用意しなければなりません。
これを一般的に「Formクラス」や「DTO(Data Transfer Object)」と呼びます。
package com.example.demo.model;
/**
* ログイン画面の入力データを保持するためのクラス
*/
public class LoginForm {
private String username;
private String password;
// ゲッター(値を取得するメソッド)
public String getUsername() {
return username;
}
// セッター(値をセットするメソッド)
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}
このJavaクラスを作成することで、ブラウザで入力された「ユーザー名」と「パスワード」が、自動的にこのクラスの変数に詰め込まれます。
プログラミング未経験の方は、「HTMLの入力欄」と「Javaの変数」が、th:fieldという魔法のパイプで繋がっているイメージを持つと分かりやすいでしょう。
これで、Thymeleafを使ったログイン画面の基礎部分が完成しました。 Spring Bootの強力な機能を活用することで、複雑な通信処理を意識することなく、直感的に画面とデータを結びつけることができます。 最初は難しく感じるかもしれませんが、一つ一つの属性の役割を確認しながら進めてみてくださいね。
4. エラーメッセージの表示:th:ifとth:errorsによるバリデーション連携
ユーザーがログイン情報を入力する際、必須項目を空のまま送信したり、パスワードの桁数が足りなかったりすることがあります。 こうした入力ミスをユーザーに知らせる仕組みが「バリデーション(入力チェック)」と「エラーメッセージの表示」です。 Spring BootとThymeleafを組み合わせると、Java側で行ったチェック結果を、HTML側で非常にスマートに表示させることができます。
バリデーションの基本的な流れ
まず、Javaのフォームクラス(DTO)に対して、入力制限のルールを設定します。 一般的には「Jakarta Bean Validation」という機能を使用し、変数に対して注釈(アノテーション)を付与します。 例えば、空文字を許可しない場合は「NotBlank」、文字数を制限する場合は「Size」といった具合です。
package com.example.demo.model;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Size;
public class LoginForm {
@NotBlank(message = "ユーザー名は必須入力です。")
private String username;
@NotBlank(message = "パスワードは必須入力です。")
@Size(min = 8, message = "パスワードは8文字以上で入力してください。")
private String password;
// ゲッターとセッターは省略
}
Thymeleafでエラーを表示する属性
Java側で発生したエラー内容をHTMLで表示するには、主に2つの属性を組み合わせて使用します。 「th:if」と「th:errors」です。これらを使うことで、エラーがある時だけ特定のメッセージを表示するという制御が可能になります。
- th:if="${#fields.hasErrors('項目名')}":指定した項目にエラーが存在する場合のみ、そのタグを表示します。
- th:errors="*{項目名}":エラーメッセージの本文を自動的に出力します。
具体的なHTMLの記述例を見てみましょう。Bootstrapの「text-danger」クラスなどと組み合わせることで、視覚的に分かりやすい警告を表示できます。
<div class="mb-3">
<label for="username" class="form-label">ユーザー名</label>
<input type="text" th:field="*{username}" class="form-control" th:errorclass="is-invalid">
<div class="text-danger" th:if="${#fields.hasErrors('username')}" th:errors="*{username}">
エラーメッセージが表示されます
</div>
</div>
<div class="mb-3">
<label for="password" class="form-label">パスワード</label>
<input type="password" th:field="*{password}" class="form-control" th:errorclass="is-invalid">
<div class="text-danger" th:if="${#fields.hasErrors('password')}" th:errors="*{password}">
エラーメッセージが表示されます
</div>
</div>
ここで便利なのが「th:errorclass」属性です。これは、その項目にエラーがある場合にのみ、指定したCSSクラス(ここではBootstrapのis-invalid)を自動で追加してくれます。 これにより、エラー時に「入力枠を赤くする」といった処理が一行で完結します。 開発者は複雑なJavaScriptを書く必要がなく、サーバー側の判定結果をそのままデザインに反映できるのがThymeleafの大きなメリットです。
5. ログイン状態に応じたViewの切り替え:Thymeleaf Extras Spring Securityの活用
Webサイトでは、ログインしている時とそうでない時で、画面の表示内容を変えたい場面が多くあります。 例えば「ログインボタン」を「ログアウトボタン」に変えたり、管理者だけに特別なメニューを表示したりする場合です。 これを実現するために、Spring Securityと連携したThymeleafの拡張ライブラリを使用します。
認証情報の取得と表示制御
「Thymeleaf Extras Spring Security」をプロジェクトに導入すると、HTMLの中で「現在ログインしているユーザーの名前」や「持っている権限」を直接参照できるようになります。 依存関係に「thymeleaf-extras-springsecurity6」を追加することで、特別な名前空間(sec属性)が使えるようになります。
| 属性名 | 役割 |
|---|---|
| sec:authorize="isAuthenticated()" | ログインしている場合にのみ、要素を表示します。 |
| sec:authorize="isAnonymous()" | ログインしていない場合にのみ、要素を表示します。 |
| sec:authentication="name" | ログイン中のユーザー名を表示します。 |
これを利用したナビゲーションバーの例を紹介します。 未ログイン時はログイン画面へのリンクを、ログイン後はユーザー名とログアウトボタンを表示する構造です。
<nav class="navbar navbar-expand-lg navbar-dark bg-dark mb-4">
<div class="container-fluid">
<a class="navbar-brand" href="#">マイアプリ</a>
<div class="navbar-nav ms-auto">
<a class="nav-link" th:href="@{/login}" sec:authorize="isAnonymous()">
<i class="bi bi-box-arrow-in-right"></i> ログイン
</a>
<span class="nav-link text-white" sec:authorize="isAuthenticated()">
ようこそ、<span sec:authentication="name">ユーザー</span>さん
</span>
<form th:action="@{/logout}" method="post" sec:authorize="isAuthenticated()" class="d-inline">
<button type="submit" class="btn btn-outline-light btn-sm ms-2">ログアウト</button>
</form>
</div>
</div>
</nav>
このように「sec:authorize」を使うことで、条件分岐のロジックをシンプルに保つことができます。 Java側で「ログイン済みかどうか」を判定してモデルに値を詰める手間が省けるため、コードの可読性が飛躍的に向上します。 特に大規模なアプリケーションでは、権限(ロール)に応じた表示制御が必要不可欠となるため、この機能は必ずマスターしておきたいポイントです。
6. 静的リソース(CSS/JavaScript)の読み込み:th:srcとth:hrefの最適化
ログイン画面を魅力的にデザインしたり、動的な動作を加えたりするには、外部のCSSファイルやJavaScriptファイルが欠かせません。 Spring Bootでは通常、これらのファイルは「src/main/resources/static」フォルダに配置します。 Thymeleafでこれらのファイルを読み込む際には、パスの解決を自動で行ってくれる専用の属性を使用します。
リンクパスの自動解決
通常のHTMLでは、CSSを読み込む際に href="/css/style.css" のように記述します。
しかし、アプリケーションを公開(デプロイ)する環境によっては、URLのルート(コンテキストパス)が変わることがあります。
Thymeleafの th:href や th:src を使用すると、環境に合わせた正しいパスを動的に生成してくれます。
<head>
<meta charset="UTF-8">
<link rel="stylesheet" th:href="@{/css/login-style.css}">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.10.0/font/bootstrap-icons.css">
</head>
<body>
<script th:src="@{/js/validation-helper.js}"></script>
</body>
画像リソースの扱い
ロゴ画像などを表示する場合も同様です。 th:src を利用することで、画像ファイルのパスを確実に指定できます。
また、Thymeleafは「ナチュラルテンプレート」という哲学を持っており、 th:src を使いつつ通常の src 属性も併記することで、ブラウザで直接HTMLファイルを開いたときにも画像が表示されるように工夫することが可能です。
<div class="text-center mb-4">
<img th:src="@{/images/logo.png}" src="../static/images/logo.png" alt="ロゴ" class="img-fluid" style="max-width: 150px;">
</div>
このように静的リソースのパスをThymeleafで管理することで、プロジェクトの構成変更にも柔軟に対応できるようになります。 特にチーム開発では、各メンバーの環境差異によって画像やスタイルが反映されないといったトラブルが起こりがちですが、Thymeleafのリンク記法を徹底することで、そのようなリスクを大幅に軽減できます。 これで、見た目も機能も備わった本格的なログイン画面の実装準備がすべて整いました。
7. ログイン画面のユーザー体験(UX)を高めるためのヒント
ここまで技術的な実装方法を解説してきましたが、実際の運用では「使いやすさ」も重要な要素です。 Thymeleafを活用すれば、ユーザーの利便性を高める工夫も簡単に行えます。 例えば、ログインに失敗した際に「ユーザー名だけを残して、パスワードだけを空にする」といった処理です。
セッションメッセージの表示
Spring Securityによる認証エラーが発生した場合、通常はURLに「?error」というパラメータが付与されます。 これを利用して、画面に「ユーザー名またはパスワードが正しくありません」という通知を出すことができます。
<div th:if="${param.error}" class="alert alert-danger shadow-sm border-0">
<i class="bi bi-exclamation-circle-fill me-2"></i>
ログインに失敗しました。入力内容を確認してください。
</div>
<div th:if="${param.logout}" class="alert alert-success shadow-sm border-0">
<i class="bi bi-check-circle-fill me-2"></i>
ログアウトしました。
</div>
このような細かな気配りが、アプリケーションの品質を左右します。
param.error は、リクエストパラメータに「error」が含まれているかを判定するThymeleafの便利な機能です。
これを活用することで、コントローラー側で複雑なエラーハンドリングを記述することなく、直感的なUIを実現できます。
レスポンシブ対応の重要性
現代のWebアプリケーションは、スマートフォンからアクセスされることも多々あります。 Bootstrapのクラス(container, row, col-md-6など)とThymeleafを組み合わせることで、パソコンでもスマホでも崩れないログイン画面が完成します。 HTMLの骨組みをThymeleafの属性で制御し、装飾をBootstrapで行うという役割分担を意識しましょう。
Thymeleafは単なる文字の置き換えツールではなく、Javaのロジックとブラウザの表示を強力に結びつける架け橋です。 今回学んだ属性や連携機能を活用すれば、初心者の方でもプロフェッショナルなログイン機能を構築できるはずです。 一歩ずつ、コードを書きながらその便利さを体感してみてください。
8. セキュリティを高めるCSRF対策:Thymeleafが自動生成する隠しフィールド
Webアプリケーションを公開する上で、避けて通れないのがセキュリティ対策です。特にログイン機能を持つシステムにおいて、悪意のある第三者がユーザーの意図しない操作を勝手に行わせる「CSRF(クロスサイト・リクエスト・フォージェリ)」という攻撃は非常に危険です。 しかし、Spring BootとThymeleafを組み合わせて使用している場合、この対策は驚くほど簡単に、かつ確実に行うことができます。
CSRF対策の仕組みと自動生成
通常、CSRF対策としては、フォームを送信する際に「ワンタイムトークン」と呼ばれる秘密の文字列を一緒に送ります。サーバー側でこのトークンが正しいかを確認することで、正規の画面からのリクエストであることを保証します。
Thymeleafの素晴らしい点は、フォームの th:action 属性を使用し、さらにSpring Securityが有効になっている場合、このCSRF対策用の隠しフィールド(hidden input)を自動的にHTMLの中へ埋め込んでくれることです。
開発者が手動で <input type="hidden" ...> を記述する必要はありません。
Thymeleafが生成した後のHTMLソースをブラウザで確認すると、自動的にトークンがセットされていることが分かります。
実際に生成されるHTMLのイメージを確認してみましょう。
<!-- ブラウザ側でレンダリングされた後のソースコード例 -->
<form action="/login" method="post">
<!-- Thymeleafによって自動挿入されるCSRFトークン -->
<input type="hidden" name="_csrf" value="a1b2c3d4-e5f6-g7h8-i9j0-k1l2m3n4o5p6" />
<div class="mb-3">
<label for="username">ユーザー名</label>
<input type="text" id="username" name="username" class="form-control" />
</div>
<button type="submit" class="btn btn-primary">ログイン</button>
</form>
注意点:th:action を必ず使うこと
この自動生成機能は、Thymeleafの th:action 属性を使っていることが条件となります。
もし、通常のHTML属性である action 属性をそのまま使ってしまうと、Thymeleafが「これは自分が管理するフォームだ」と認識できず、トークンの自動挿入が行われません。
セキュリティを担保するためにも、必ず th:action="@{/login}" のように、Thymeleafの記法で記述するようにしましょう。
このように、Thymeleafは単なる画面表示のツールではなく、セキュリティという目に見えない重要な部分まで強力にバックアップしてくれます。 初心者の方は「なぜセキュリティが守られているのか」を完璧に理解するのは後回しでも構いませんが、「th:action を使うことで守られている」という事実を覚えておくことが大切です。
9. 実践的なUI改善:フラッシュ属性を使ったログアウト完了メッセージの表示
ログイン画面において、ユーザーが操作を行った後に「何が起きたのか」を明確に伝えることは、優れたユーザー体験(UX)を提供するために不可欠です。 特に、ログアウトが成功した際や、パスワード変更後にログイン画面へ戻ってきた際などに、一度だけ表示される通知メッセージを表示する仕組みを学びましょう。 これを実現するのが「フラッシュ属性(Flash Attributes)」です。
フラッシュ属性とは何か
フラッシュ属性は、リダイレクト(別のページへ移動)する際に、移動先の画面へ一度だけデータを引き継ぐための仕組みです。
通常の Model に値を入れると、リダイレクトした瞬間にデータが消えてしまいますが、フラッシュ属性はセッションを一時的に利用するため、移動後の画面でも値を受け取ることができます。
一度表示されると自動的に削除されるため、画面をリロードしたときにメッセージが消えるという、理想的な挙動を簡単に実装できます。
コントローラーでの実装例
まずは、Javaのコントローラー側でリダイレクト時にメッセージをセットする方法を見てみましょう。
RedirectAttributes というオブジェクトを使用します。
@PostMapping("/process-logout")
public String logout(RedirectAttributes redirectAttributes) {
// ログアウト処理の実装(実際にはSpring Securityが自動で行うことが多い)
// リダイレクト先に渡すメッセージをセット
redirectAttributes.addFlashAttribute("successMessage", "ログアウトが完了しました。ご利用ありがとうございました。");
// ログイン画面へリダイレクト
return "redirect:/login";
}
HTML側でのメッセージ表示制御
次に、受け取ったメッセージをログイン画面で表示するThymeleafの記述です。
前述の th:if を活用して、「メッセージがある時だけ」表示されるように制御します。
<!-- 成功メッセージの表示エリア -->
<div th:if="${successMessage}" class="alert alert-success alert-dismissible fade show shadow-sm" role="alert">
<i class="bi bi-check-circle-fill me-2"></i>
<span th:text="${successMessage}">ここにメッセージが入ります</span>
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
</div>
ここではBootstrapの alert-dismissible クラスを利用しており、ユーザーが右上の「×」ボタンでメッセージを閉じられるようになっています。
こうした細かな工夫により、システムからの一方的な通知ではなく、ユーザーが納得感を持って操作できるインターフェースへと進化します。
また、ログインに失敗した際のエラーメッセージ表示にも応用が可能です。
Spring Securityが標準で提供する ${param.error} を使う方法も手軽で良いですが、より詳細なエラー理由(アカウントがロックされている、期限切れである等)を表示したい場合は、このフラッシュ属性を使って独自のメッセージを渡す手法が非常に有効です。
10. Thymeleafで使い勝手の良いログイン画面を作るための要点
これまでの内容を振り返ると、Thymeleafを使ったログイン画面の実装には、いくつかの重要な柱があることが分かります。 単にHTMLを書くだけでなく、Spring Bootの機能と密接に連携させることで、高度な機能を最小限の記述で実現できるのが最大の魅力です。
開発時に意識すべき三つのポイント
現場で開発を行う際に、特に意識してほしいポイントを整理しました。
th:objectとth:fieldを使いこなし、JavaのオブジェクトとHTMLの入力項目を正確に紐付けましょう。
th:actionを適切に設定し、CSRF対策などのセキュリティ機能をフレームワークに任せることで安全性を確保します。
バリデーションエラーや成功メッセージを適切に表示し、ユーザーが迷わない親切な画面作りを目指します。
Webアプリケーションの入り口であるログイン画面は、ユーザーが最初に触れる「顔」とも言える部分です。 今回紹介したThymeleafのテクニックは、どれも実際の業務システム開発で頻繁に使われる実践的なものばかりです。 属性一つひとつの意味を理解し、なぜその記述が必要なのかを意識することで、応用力のあるスキルが身につくはずです。
最初は「おまじない」のように見えた th: から始まる属性も、使い慣れてくるとその合理性に気づくでしょう。
Java側でデータを準備し、Thymeleafがそれを美しく、そして安全に画面へ描画する。この強力な連携を武器にして、ぜひ使い勝手の良い素晴らしいアプリケーションを構築してください。
これで、Thymeleafを用いたログイン画面作成の解説を終わります。 もしエラーが出たときは、属性のスペルミスがないか、Java側のフィールド名と一致しているかをまず確認してみてくださいね。 一歩ずつ着実に進んでいきましょう!