サービスを提供する多くのWEBサイトではログイン認証が必要です。しかし、いざ実装しようとすると結構面倒なものです。
今回はSpring Bootに含まれるSpring Securityを使ったログイン認証を見てみます。
最もシンプルな例
以後の作業はEclipseをベースに作られたSpring BootのためのIDEであるSprint Tool Suiteを使用します。
使用しているSprint Tool SuiteはPleiadesにより日本語化されています。
プロジェクトの生成
ファイルメニューまたはパッケージ・エクスプローラーの空白領域で新規→Springスターター・プロジェクトを指定します。
パッケージングにWarを指定します。その他の項目は適当に入力してください。
依存関係は
- コア→セキュリティー
- コア→Lombok
- テンプレート・エンジン→Thymeleaf
- Web→Web
をチェックします。
完了ボタンをクリックしてプロジェクトを生成します。生成したプロジェクトは次のようになります。
画面の追加
画面はログイン画面とログイン完了画面の2画面を用意します。
src/main/resources/templatesに次のふたつのhtmlを配置します。
- login.html
<!DOCTYPE html> <html xmlns:th="https://www.thymeleaf.org"> <head> <meta charset="UTF-8"> <title>Login</title> </head> <body> <form name="loginForm" method="post" th:action="@{authenticate}"> <table> <tbody> <tr> <th colspan="2">ログイン</th> </tr> <tr> <th>ユーザーID</th> <td><input type="text" name="username" maxlength="128" size="32"></td> </tr> <tr> <th>パスワード</th> <td><input type="password" name="password" maxlength="128" size="32"></td> </tr> <tr> <th colspan="2"><button type="submit">ログイン</button></th> </tr> </tbody> </table> </form> </body> </html>
- loginSuccess.html
<!DOCTYPE html> <html xmlns:th="https://www.thymeleaf.org"> <head> <meta charset="UTF-8"> <title>Login Success</title> </head> <body> <h1>ログイン成功</h1> ログインユーザー名:<span th:text="${username}"></span> <a href="logout">ログアウト</a> </body> </html>コントローラーの追加
- MainController.java
package com.sios.ex.springsecurity; import java.security.Principal; import org.springframework.security.core.Authentication; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.GetMapping; @Controller public class MainController { @GetMapping("/") public String rootForm(Model model) { return "login"; } @GetMapping("/login") public String loginForm(Model model) { return "login"; } @GetMapping("/loginSuccess") public String loginSuccessForm(Principal principal, Model model) { Authentication authentication = (Authentication)principal; String username = authentication.getName(); model.addAttribute("username", username); return "loginSuccess"; } @GetMapping("/invalidSession") public String invalidSessionForm(Model model) { return "login"; } }セキュリティ設定
Spring Securityの主要部分になります。
今回はSpring Securityの簡単なサンプルを示すだけなのでログインは認証のみを行っています。ユーザーもデータベース、Active DirectoryおよびLDAPを使用せずオンメモリで保持しています。
- MainSecurityConfigurer.java
package com.sios.ex.springsecurity; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Configuration; import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.builders.WebSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; import org.springframework.security.core.userdetails.User; import org.springframework.security.web.util.matcher.AntPathRequestMatcher; @Configuration @EnableWebSecurity public class MainSecurityConfigurer extends WebSecurityConfigurerAdapter { @Override public void configure(WebSecurity web) throws Exception { // セキュリティ設定を無視するリクエスト設定 // 静的リソースに対するアクセスはセキュリティ設定を無視する web.ignoring().antMatchers("/css/**", "/img/**", "/js/**"); } @Override protected void configure(HttpSecurity https) throws Exception { https .authorizeRequests() // アクセス権限の設定 // アクセス制限の無いURL .antMatchers("/", "/login", "/error").permitAll() // その他は認証済みであること .anyRequest() .authenticated() .and() .formLogin() // ログイン画面 .loginPage("/login") // 認証処理 .loginProcessingUrl("/authenticate") // ログイン成功 .defaultSuccessUrl("/loginSuccess") // ログイン失敗 .failureUrl("/login") // usernameのパラメータ名 .usernameParameter("username") // passwordのパラメータ名 .passwordParameter("password") .and() .logout() // ログアウト処理 .logoutRequestMatcher(new AntPathRequestMatcher("/logout")) // ログアウト成功時の遷移先 .logoutSuccessUrl("/login") // ログアウト時に削除するクッキー名 .deleteCookies("JSESSIONID") // ログアウト時のセッション破棄を有効化 .invalidateHttpSession(true) .permitAll() .and() .sessionManagement() // セッションが無効な時の遷移先 .invalidSessionUrl("/invalidSession"); } @Autowired protected void config(AuthenticationManagerBuilder auth) throws Exception { auth .inMemoryAuthentication() .withUser( User.withDefaultPasswordEncoder() .username("foo").password("foopass").roles("USER").build()) .and() .inMemoryAuthentication() .withUser( User.withDefaultPasswordEncoder() .username("bar").password("barpass").roles("ADMIN", "USER").build()); } }18~22行目
セキュリティ設定から除外するパスを指定します。CSS、画像、Javascriptなどの静的なファイルはセキュリティによるアクセス制限をかけないのでそのパスを指定します。
27~33行目
アクセス制限を行わないURLとアクセス制限を行うURLを指定します。
35~47行目
ログイン画面とログインに関わる処理を設定します。
- loginPageにはログイン画面のURLを指定します。
- loginProcessingUrlで指定したURLがリクエストされるとログイン認証の処理を行います。
- defaultSuccessUrlにはログインが成功したときに遷移するURLを指定します。
- failureUrlにはログインが失敗したときに遷移するURLを指定します。
- usernameParameterにはログイン画面のユーザー名のパラメータ名を指定します。
- passwordParameterにはログイン画面のパスワードのパラメータ名を指定します。
defaultSuccessUrlはログインが成功したときに実行する処理に置き換えることができます。同様にfailureUrlもログインが失敗したときの処理に置き換えることができます。
66~76行目
ログインできるユーザー名とそのパスワードを登録しています。
Spring Bootの2.0.0.RELEASEからオンメモリのユーザー設定でも暗号化しなくてはならなくなったためここではUser.withDefaultPasswordEncoder()を用いてパスワードを暗号化しています。
このユーザーの管理をオンメモリではなくデータベース、Active DirectoryまたはLDAPにすることができます。
最後に
ここではSpring Securityの簡単なサンプルを紹介しました。実用的なWEBアプリケーションに使うためには次の箇所を目的に合わせて変えます。これらの変更もSpring Securityはサポートしています。
- ログイン成功時の処理
- ログイン失敗時の処理(エラーメッセージ等)
- ユーザー管理