SpringBootに用意された、高機能なセキュリティ機構「SpringSecurity」を使います。
STS3(3.9.6)+SpringBoot2.0+Tymeleaf3.0迄動作確認しています。
今回は「ログインフォームをオリジナルページにする」という部分をやります。
オリジナルログインページのHTMLをつくる
ログイン画面用のHTMLを用意します。
凝るのも面倒なので、最低限のフォームにしておきます。
イメージはこんな感じ。
login.html のHTMLソースです。
<html xmlns:th="http://www.thymeleaf.org" xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity4"> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Sample App</title> <link th:href="@{/css/styles.css}" rel="stylesheet"/> </head> <body> <div class="box"> <h1>オリジナルログインフォームです</h1> <form role="form" id="login" th:action="@{/login}" method="post"> <div th:if="${iserror}" style="color:red;"> <p>ログインできませんでした。やりなおしてください。</p> </div> <p>ユーザID</p> <input type="text" id="username" name="username" autofocus="autofocus" /> <p>パスワード</p> <input type="password" id="password" name="password" /><br/> <button type="submit">ログイン</button> </form> </div> </body> </html>
ポイントだけ補足します。
ログインエラーメッセージを表示する箇所は以下のようにしてます。
<div th:if="${iserror}" style="color:red;"> <p>ログインできませんでした。やりなおしてください。</p> </div>
コントローラクラスで、iserrorという変数のエラーでの表示(true)か否(false)かがセットされて渡ってくる前提で、エラーメッセージの表示・非表示を切り替えているだけです。
別にこうしないといけないという訳ではないですが、とりあえず、シンプルで悩むところの少ない、ひとつのやり方ではあります。
もうひとつ、ユーザー名を入力するテキストボックスのnameは「username」、パスワードを入力するテキストボックスのnameは「password」です。
これを違う名前にすると、正しくユーザIDとパスワードを入力しているのに、認証エラーになる・・みたいなことで悩むことになります。
変更する方法はあるみたいですが、あえて変更することに、手間に見合うメリットがない限りはあわせておくことをおすすめします。
参考までに、CSSは外部ファイルにして、以下の最低限の設定だけしています。
@CHARSET "UTF-8"; html { font-size:16px } body { font-family: 'メイリオ','Hiragino Kaku Gothic Pro',sans-serif; background-color:white; } .box{ wide:100%; text-align:center; } p{ margin-left:1em; margin-right:auto; margin-top:0.5em; margin-bottom:0.5em; } input[type="text"]{ margin-left:1em; margin-top:0.5em; margin-bottom:0.5em; height:2em; line-height:2em; paddhing:1em; } input[type="password"]{ margin-left:1em; margin-top:0.5em; margin-bottom:0.5em; height:2em; line-height:2em; paddhing:1em; }
JAVAコンフィグクラスにオリジナルフォームを使う設定をする
Spring Securityの設定はXMLで行う方法もありますが、自分はXMLを増やすのが嫌いなので、Javaで設定する方法をとります。
configパッケージを作って、Spring Securityの設定を行うクラスをその下に作ります。
決まった名前付けルールとかはありません。
違うパッケージの下に違うクラス名で作っても構いません。
@EnableWebSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity web)throws Exception{ web.formLogin().loginPage("/login").defaultSuccessUrl("/inpg01").failureUrl("/login-error").permitAll(); web.authorizeRequests().antMatchers("/css/**", "/images/**", "/js/**").permitAll().anyRequest().authenticated(); } }
まず、絶対の前提条件です。
- 必ず「WebSecurityConfigurerAdapter」を継承します。
- 必ず「@EnableWebSecurity」アノテーションをつけます。
- 必ず「configure」メソッドをオーバーライドします。
次に以下の部分の説明です。
web.formLogin().loginPage("/login").defaultSuccessUrl("/inpg01").failureUrl("/login-error").permitAll();
formLogin() は「フォーム認証を行う」。
loginPage("/login") は「ログインページを表示するURLは/login」
defaultSuccessUrl("/inpg01")は「認証成功時のデフォルト遷移先URLは/inpg01」
failureUrl("/login-error")は、「認証失敗時の遷移先URLは/login-error」
.permitAll(); は、「すべてのユーザに対して、ログインページへのアクセスを許す」
です。
ようするに、オリジナルログインページを認証で使うための宣言です。
認証失敗時のURLを指定しなくても、デフォルトで「login?error」みたいに、ログインページのURLに?errorのパラメータをつけて遷移してくれますが、明示的に指定しておいたほうがわかりやすいかな・・。
続けて、以下の部分です。
web.authorizeRequests().antMatchers("/css/**", "/images/**", "/js/**").permitAll().anyRequest().authenticated();
anyRequest().authenticated() は、すべてのユーザは認証されているユーザ以外にアクセスは許さないという指定です。
ただ、それだけだと、ログインページを表示する時にCSSファイルや画像ファイルにもアクセスできなくなってしまいます。
なので、その前の「antMatchers("/css/**", "/images/**", "/js/**").permitAll()」でstaticフォルダのCSS,images、jsフォルダに対してはすべてのユーザのアクセスを許すようにしています。
コントローラクラスを作成する
ここまでできたら、あとはログインページのコントローラクラスを作るだけです。
LoginController.javaという名前にします。
ログイン成功時の遷移先「/inpg01」は前回作成したページをそのまま使います。
だから、LoginControllerクラスで作成するの必要があるは、「/login」と「/login-error」の2つです。
@Controller public class LoginController { @RequestMapping(value = "/login", method = RequestMethod.GET) public String index(Model model) { model.addAttribute("iserror",false); return "login"; } @RequestMapping(value = "/login-error", method = RequestMethod.GET) public String loginError(Model model) { model.addAttribute("iserror",true); return "login"; } }
ごくシンプルに、どちらも「login.html」を呼び出してます。
違いは、「iserror」にfalseをセットしているか、true(エラーメッセージを表示)にしているか・・くらいです。
さて実行してみます。
認証時のユーザ名とパスワードと権限は、前回同様、プロパティファイルで指定するパターンのままでやってみます。
今回はUSERロールでやってみます。
spring.security.user.name=user
spring.security.user.password=demo
spring.security.user.roles=USER
これで前回同様にまず「/inpg01」にアクセスすると、まだログインしていないのでログイン画面が表示されます。
まずエラーページをみたいので、上記のように適当なユーザ名とパスワードを入力してログインボタンを押してみます。
ちゃんとエラーページに遷移してますので、今度は正しく「user」「demo」を入力してログインします。
OKですね。
オリジナルのログイン画面に変更することができました。
今回はこんなところで。