デフォルトのログインページから独自のログインページを作ってみたいなっと思うことってあるじゃないですか。なので、やってみようかなと思いまして。
公式リファレンスではこちらの箇所にカスタムのログインページの作り方が書いてあります。
https://docs.spring.io/spring-security/site/docs/5.4.2/reference/html5/#servlet-authentication-form
SpringBootで自動構成が有効になっている状態では勝手にログインページが生成されますが、そのログインページをカスタムのログインページにするためには以下の2つのステップが必要なようです。
- Configurationでログインページのパスを示すこと。
- カスタムのログインページのHTMLを作成すること。
どちらも上記のリファレンスに参考例があるので参考にするといいと思います。
Configurationでログインページのパスを示す
以下のように configure
メソッドでフォームログインを有効化すると同時に、ログインページのパスを指定します。SpringBootの自動構成でも /login
がログインページのパスだった気がしますが、このようにして明示しないとログインページの自動構成が無効にならないのでしょう。
作ってみたコードはこんな感じになりました。
@Override protected void configure(HttpSecurity http) throws Exception { http.formLogin(form -> form .loginPage("/login").permitAll() .defaultSuccessUrl("/home", true) ); http.authorizeRequests() .mvcMatchers("/home/**").authenticated() .mvcMatchers("/manage/**").hasRole("ADMIN") .anyRequest().permitAll(); }
あと /login
パスに対応するコントローラーも必要になります。自動構成では勝手に生成されたHTMLを返してもらえていましたが、独自に作成したログインページを返すことになりますね。
@Controller public class LoginController { @GetMapping("login") public String login() { return "login"; } }
カスタムのログインページのHTMLを作成する
ログインページを作る上で知っておく必要があるのは以下の内容かなと。
- ログイン自体は
/login
にPOSTリクエストを行う - CSRFトークンが必要ならリクエストに含めること(Thymeleafを使っている場合自動的に送信してくれます。便利!)
- ユーザー名は
username
で送信 - パスワードは
password
で送信 - もしHTTPパラメーターに
error
があればユーザー名/パスワードが間違っている - もしHTTPパラメーターに
logout
があればログアウトが行われた
これらの構成はデフォルトの話なので、Configurationによって変更することはできます。ですがとりあえずデフォルトのまま使っていても問題ないかなと。
作ってみたらこんな感じになりました。
<!DOCTYPE html> <html lang="en" xmlns:th="http://www.thymeleaf.org"> <head> <meta charset="UTF-8"> <title>Home</title> <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.0/css/bootstrap.min.css" integrity="sha384-9aIt2nRpC12Uk9gS9baDl411NQApFmC26EwAOH8WgZl5MYYxFfc+NcPb1dKGj7Sk" crossorigin="anonymous"> </head> <body> <div class="container"> <div class="row mt-5"> <div class="col"> <form class="w-50 mx-auto p-3 bg-light" method="post" th:action="@{/login}"> <h3 class="mb-4 text-center">ログイン</h3> <div class="alert alert-success" role="alert" th:if="${param.logout}">ログアウトしました。</div> <div class="alert alert-danger" role="alert" th:if="${param.error}">ユーザー名またはパスワードが違います。</div> <div class="form-group"> <label for="username">ユーザー名</label> <input class="form-control" id="username" name="username" type="text"/> </div> <div class="form-group"> <label for="password">パスワード</label> <input class="form-control" id="password" name="password" type="password"/> </div> <div class="d-flex flex-row-reverse"> <button class="btn btn-primary" type="submit">ログイン</button> </div> </form> </div> </div> </div> </body> </html>
Thymeleafで param
変数にHTTPパラメーターが詰められていることを知りました。この仕組を使うことでログアウト時とエラー時の挙動を実装できます。
実際にレンダリングされるHTMLはこんな感じになって、CSRFトークンがhiddenパラメーターになっていることがわかります。
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Home</title> <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.0/css/bootstrap.min.css" integrity="sha384-9aIt2nRpC12Uk9gS9baDl411NQApFmC26EwAOH8WgZl5MYYxFfc+NcPb1dKGj7Sk" crossorigin="anonymous"> </head> <body> <div class="container"> <div class="row mt-5"> <div class="col"> <form class="w-50 mx-auto p-3 bg-light" method="post" action="/login"><input type="hidden" name="_csrf" value="7958d02e-069e-4ecc-8202-1fe79733270b"/> <h3 class="mb-4 text-center">ログイン</h3> <div class="form-group"> <label for="username">ユーザー名</label> <input class="form-control" id="username" name="username" type="text"/> </div> <div class="form-group"> <label for="password">パスワード</label> <input class="form-control" id="password" name="password" type="password"/> </div> <div class="d-flex flex-row-reverse"> <button class="btn btn-primary" type="submit">ログイン</button> </div> </form> </div> </div> </div> </body> </html>
とまあ、カスタムログインページの話でした。