Spring Boot で書籍の貸出状況確認・貸出申請する Web アプリケーションを作る ( その8 )( ログイン画面の作成2 )
概要
Spring Boot で書籍の貸出状況確認・貸出申請する Web アプリケーションを作る ( その7 )( ログイン画面の作成 ) の続きです。
- 今回の手順で確認できるのは以下の内容です。
- ログイン機能 ( 基本的な部分 ) の作成
参照したサイト・書籍
Spring Security 3.1
- 作者: Robert Winch
- 出版社/メーカー: Packt Publishing
- 発売日: 2012/12/26
- メディア: ペーパーバック
- この商品を含むブログを見る
- 「3 Custom Authentication」を参照しました。
Spring Boot Security Application
http://kielczewski.eu/2014/12/spring-boot-security-application/Spring Security 3.1 リファレンス - テクニカル・サマリ (1)
http://m12i.hatenablog.com/entry/2013/03/28/004152- GrantedAuthority を使う部分を参考にしました。
Thymeleaf + Spring Security integration basics
http://www.thymeleaf.org/doc/articles/springsecurity.html
目次
- はじめに
- 1.0.x-make-login-basic ブランチの作成
- LoginController クラスの作成
- ログイン画面の HTML ファイルから Thymeleaf テンプレートファイルの作成
- 動作確認
- JRebel 使用のための設定
- 暗号化したパスワード文字列生成用 URL の作成
- テストデータを user_info テーブルに登録する
- UserDetails インターフェース実装クラスの作成
- UserInfoDao インターフェースの変更
- UserRoleDao インターフェースの変更
- UserDetailsService インターフェース実装クラスの作成
- テスト用ログイン成功ページの作成
- WebSecurityConfig クラスの変更
- 動作確認2
- commit、Push、Pull Request、マージ
手順
はじめに
今回は基本的な部分として、以下の実装を行います。
- ユーザ情報を格納するテーブル user_info には username, password, mail_address, enabled が格納されています。
- ログイン画面の ID には username ではなく mail_address を使用します。
- UserDetails インターフェースを実装した独自クラスを作成し、username も mail_address も認証後に参照できるようにします。
ユーザ情報を拡張したり、認証処理を変更する場合の手順は以下のようになります。
- UserDetails インターフェースを実装したクラスを作成し、独自定義した情報が格納できるようにします。
- UserDetailsService インターフェースを実装したクラスを作成し、UserDetails インターフェース実装クラスを返すようにします。
- WebSecurityConfigurerAdapter を継承した Java Configuration のクラスの configAuthentication メソッド内で、auth.userDetailsService メソッドに UserDetailsService インターフェース実装クラスを渡します。
1.0.x-make-login-basic ブランチの作成
LoginController クラスの作成
src/main/java/ksbysample/webapp/lending の下に web パッケージを作成します。
src/main/java/ksbysample/webapp/lending/web の下に LoginController.java を作成します。作成後、リンク先のその1の内容 に変更します。
ログイン画面の HTML ファイルから Thymeleaf テンプレートファイルの作成
- src/main/resources/static/html/login.html を src/main/resources/templates の下へコピーします。コピー後、リンク先の内容 に変更します。
動作確認
ログイン画面が表示されるか確認します。
Gradle projects View から bootRun タスクを実行して Tomcat を起動します。
ブラウザを起動して http://localhost:8080/ にアクセスし、ログイン画面が表示されることを確認します。
Ctrl+F2 を押して Tomcat を停止します。
JRebel を使用するための設定
Tomcat を起動したままソースコードの修正・確認を繰り返したいので、JRebel を使用するために必要な設定をします。
Project View から src/main/java/ksbysample/webapp/lending の下の Application を選択した後、コンテキストメニューを表示して「Run with JRebel」->「Application」を選択します。
JRebel で Application が起動しようとしますが、spring.profiles.active に値が指定されていないと起動しないようにしているので、この時点では起動しません。
メインメニューから「Run」-「Edit Configurations...」を選択します。
「Run/Debug Configurations」ダイアログが表示されます。画面左側のツリーから「Spring Boot」-「Application」を選択します。選択後、画面右側の「VM options」に "-Dspring.profiles.active=develop -Dfile.encoding=UTF-8" を入力して「OK」ボタンをクリックします。
メイン画面に戻ると「Select Run/Debug Configuration」で「Application」が選択されており、画面右上の JRebel のボタンが押せるようになっています。「Run with JRebel 'Application'」ボタンをクリックして Tomcat を起動します。
暗号化したパスワード文字列生成用 URL の作成
DB に暗号化したパスワードを保存できるようにするために、暗号化パスワード生成用の URL http://localhost:8080/encode?password=... を作成します。
src/main/java/ksbysample/webapp/lending/web の下の LoginController.java を リンク先のその2の内容 に変更します。
http://localhost:8080/encode?password=... を認証対象外にします。src/main/java/ksbysample/webapp/lending/config の下の WebSecurityConfig.java を リンク先のその1の内容 に変更します。
今回の変更は build し直しただけでは JRebel でも反映されませんでした。Ctrl+F5 を押して Tomcat を再起動します。
動作確認します。ブラウザで http://localhost:8080/encode?password=taro にアクセスすると暗号化された文字列が表示されます。
テストデータを user_info テーブルに登録する
ログインの動作確認に使用するデータを user_info テーブルに登録します。
パスワードを暗号化すると文字列が長くなることを考慮できていませんでした。user_info テーブルを作り直します。また mail_address をログインID に使用したいので mail_address に index を作成します。/sql の下の create_table.sql を リンク先の内容 に変更します。
コマンドプロンプトから以下のコマンドを実行してテーブルを作成し直します。
C:\project-springboot\ksbysample-webapp-lending>psql -U ksbylending_user ksbylen ding ユーザ ksbylending_user のパスワード: psql (9.4.1) "help" でヘルプを表示します. ksbylending=> drop table user_role; DROP TABLE ksbylending=> drop table user_info; DROP TABLE ksbylending=> create table user_info ( ksbylending(> user_id bigserial primary key ksbylending(> , username varchar(32) not null ksbylending(> , password varchar(256) not null ksbylending(> , mail_address varchar(256) not null ksbylending(> , enabled smallint not null default 1 ksbylending(> ); CREATE TABLE ksbylending=> create index user_info_idx_01 on user_info(mail_address); CREATE INDEX ksbylending=> create table user_role ( ksbylending(> role_id bigserial primary key ksbylending(> , user_id bigint not null references user_info(u ser_id) on delete cascade ksbylending(> , role varchar(32) not null ksbylending(> ); CREATE TABLE ksbylending=> \d リレーションの一覧 スキーマ | 名前 | 型 | 所有者 ----------+----------------------------------+------------+------------------ public | lending_app | テーブル | ksbylending_user public | lending_app_lending_app_id_seq | シーケンス | ksbylending_user public | lending_book | テーブル | ksbylending_user public | lending_book_lending_book_id_seq | シーケンス | ksbylending_user public | user_info | テーブル | ksbylending_user public | user_info_user_id_seq | シーケンス | ksbylending_user public | user_role | テーブル | ksbylending_user public | user_role_role_id_seq | シーケンス | ksbylending_user (8 行) ksbylending=> \q C:\project-springboot\ksbysample-webapp-lending>
IntelliJ IDEA の Database Tools の「Shynchronize」ボタンをクリックして変更内容を反映します。
user_info, user_role テーブルに以下の画像のデータを登録します。
- password カラムに設定している文字列
$2a$10$LKKepbcPCiT82NxSIdzJr.9ph.786Mxvr.VoXFl4hNcaaAn9u7jje
はtaro
を暗号化したものです。
UserDetails インターフェース実装クラスの作成
src/main/java/ksbysample/webapp/lending の下に security パッケージを作成します。
src/main/java/ksbysample/webapp/lending/security の下に UserInfoUserDetails.java を作成します。作成後、リンク先の内容 に変更します。
UserInfoDao インターフェースの変更
UserInfoDao インターフェースに user_info テーブルを mail_address カラムで検索するメソッドを追加します。
src/main/java/ksbysample/webapp/lending/dao の下の UserInfoDao インターフェースを リンク先の内容 に変更します。
src/main/resources/META-INF/ksbysample/webapp/lending/dao/UserInfoDao の下に selectByUsername.sql を作成します。作成後、リンク先の内容 に変更します。
UserRoleDao インターフェースの変更
UserRoleDao インターフェースに user_role テーブルを user_id で検索するメソッドを追加します。
src/main/java/ksbysample/webapp/lending/dao の下の UserRoleDao インターフェースを リンク先の内容 に変更します。
src/main/resources/META-INF/ksbysample/webapp/lending/dao/UserRoleDao の下に selectByUserId.sql を作成します。作成後、リンク先の内容 に変更します。
UserDetailsService インターフェース実装クラスの作成
- src/main/java/ksbysample/webapp/lending/security の下に UserInfoUserDetailsService.java を作成します。作成後、リンク先の内容 に変更します。
テスト用ログイン成功ページの作成
ログインが成功した時に表示するテスト用ページを作成します。テスト用ページでは認証されたユーザの情報を表示してみます。
build.gradle を リンク先の内容 に変更します。
Gradle projects View の左上にある「Refresh all Gradle projects」ボタンをクリックして更新します。
src/main/java/ksbysample/webapp/lending/web の下の LoginController.java を リンク先のその3の内容 に変更します。
src/main/resources/templates の下に loginsuccess.html を作成します。作成後、リンク先の内容 に変更します。
WebSecurityConfig クラスの変更
- src/main/java/ksbysample/webapp/lending/config の下の WebSecurityConfig.java を リンク先のその2の内容 に変更します。
動作確認2
Ctrl+F5 を押して Tomcat を再起動します。
ブラウザで http://localhost:8080/ にアクセスし、ログイン画面を表示します。ID に "tanaka.taro@sample.com"、Password に "taro" を入力して「ログイン」ボタンをクリックします。
認証が成功してテスト用ログイン成功ページが表示され、以下の画像のようにユーザ名、メールアドレス、登録されている Role 一覧が表示されます。
Ctrl+F2 を押して Tomcat を停止します。
commit、Push、Pull Request、マージ
commit します。「Code Analysis」ダイアログが出ますが、無視して「Commit」ボタンをクリックします。
GitHub へ Push、1.0.x-make-login-basic -> 1.0.x へ Pull Request、1.0.x でマージ、1.0.x-make-login-basic ブランチを削除、をします。
ソースコード
LoginController.java
■その1
package ksbysample.webapp.lending.web; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; @Controller @RequestMapping("/") public class LoginController { @RequestMapping public String index() { return "login"; } }
■その2
package ksbysample.webapp.lending.web; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseBody; @Controller @RequestMapping("/") public class LoginController { @RequestMapping public String index() { return "login"; } @RequestMapping("/encode") @ResponseBody public String encode(@RequestParam String password) { return new BCryptPasswordEncoder().encode(password); } }
- encode メソッドを追加します。
■その3
package ksbysample.webapp.lending.web; import ksbysample.webapp.lending.security.UserInfoUserDetailsService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.core.userdetails.User; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseBody; @Controller @RequestMapping("/") public class LoginController { @RequestMapping public String index() { return "login"; } @RequestMapping("/encode") @ResponseBody public String encode(@RequestParam String password) { return new BCryptPasswordEncoder().encode(password); } @RequestMapping("/loginsuccess") public String loginsuccess() { return "loginsuccess"; } }
- loginsuccess メソッドを追加します。
login.html
<!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org"> <head> <meta charset="UTF-8"/> <meta http-equiv="X-UA-Compatible" content="IE=edge"/> <title>ログイン画面</title> <!-- Tell the browser to be responsive to screen width --> <meta content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no" name="viewport"/> <!-- Bootstrap 3.3.4 --> <link href="/css/bootstrap.min.css" rel="stylesheet" type="text/css"/> <!-- Font Awesome Icons --> <link href="/css/font-awesome.min.css" rel="stylesheet" type="text/css"/> <!-- Ionicons --> <link href="/css/ionicons.min.css" rel="stylesheet" type="text/css"/> <!-- Theme style --> <link href="/css/AdminLTE.min.css" rel="stylesheet" type="text/css"/> <!-- AdminLTE Skins. Choose a skin from the css/skins folder instead of downloading all of them to reduce the load. --> <link href="/css/skins/_all-skins.min.css" rel="stylesheet" type="text/css"/> <!-- HTML5 Shim and Respond.js IE8 support of HTML5 elements and media queries --> <!-- WARNING: Respond.js doesn't work if you view the page via file:// --> <!--[if lt IE 9]> <script src="/js/html5shiv.min.js"></script> <script src="/js/respond.min.js"></script> <![endif]--> <style> <!-- .content-wrapper { background-color: #fffafa; padding-top: 50px; } .form-group { margin-bottom: 5px; } --> </style> </head> <!-- ADD THE CLASS layout-top-nav TO REMOVE THE SIDEBAR. --> <body class="skin-blue layout-top-nav"> <div class="wrapper"> <!-- Full Width Column --> <div class="content-wrapper"> <div class="container"> <!-- Main content --> <section class="content"> <div class="row"> <div class="col-xs-12 col-md-push-3 col-md-6"> <div class="form-wrap"> <div class="text-center"><h1>ksbysample-lending</h1></div> <br/> <form role="form" action="#" th:action="@{/login}" method="post" id="login-form" autocomplete="off"> <div class="form-group" th:if="${session['SPRING_SECURITY_LAST_EXCEPTION']} != null"> <p class="form-control-static text-danger" th:text="${session['SPRING_SECURITY_LAST_EXCEPTION'].message}"></p> </div> <div class="form-group"> <label for="id" class="sr-only">ID</label> <input type="text" name="id" id="id" class="form-control" placeholder="ID を入力して下さい" th:value="${session['SPRING_SECURITY_LAST_EXCEPTION']} != null ? ${session['SPRING_SECURITY_LAST_EXCEPTION'].authentication.principal} : ''"/> </div> <div class="form-group"> <label for="password" class="sr-only">Password</label> <input type="password" name="password" id="password" class="form-control" placeholder="Password を入力して下さい"/> </div> <br/> <button id="btn-login" class="btn btn-primary btn-block">ログイン</button> </form> </div> </div> </div> </section> <!-- /.content --> </div> <!-- /.container --> </div> </div> <!-- ./wrapper --> <!-- jQuery 2.1.4 --> <script src="/js/jQuery-2.1.4.min.js" type="text/javascript"></script> <!-- Bootstrap 3.3.2 JS --> <script src="/js/bootstrap.min.js" type="text/javascript"></script> <!-- AdminLTE App --> <script src="/js/app.min.js" type="text/javascript"></script> <script type="text/javascript"> <!-- $(document).ready(function() { $('#id').focus().select(); }); --> </script> </body> </html>
- 先頭に
<!DOCTYPE html>
が抜けていたので追加します。
WebSecurityConfig.java
■その1
@Override protected void configure(HttpSecurity http) throws Exception { http.authorizeRequests() // 認証の対象外にしたいURLがある場合には、以下のような記述を追加します // 複数URLがある場合はantMatchersメソッドにカンマ区切りで対象URLを複数列挙します // .antMatchers("/country/**").permitAll() .antMatchers("/fonts/**").permitAll() .antMatchers("/html/**").permitAll() .antMatchers("/encode").permitAll() .anyRequest().authenticated();
.antMatchers("/encode").permitAll()
を追加します。
■その2
package ksbysample.webapp.lending.config; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.security.SecurityProperties; import org.springframework.context.annotation.Configuration; import org.springframework.core.annotation.Order; 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.configuration.WebSecurityConfigurerAdapter; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.security.web.util.matcher.AntPathRequestMatcher; @Configuration @Order(SecurityProperties.ACCESS_OVERRIDE_ORDER) public class WebSecurityConfig extends WebSecurityConfigurerAdapter { @SuppressWarnings("SpringJavaAutowiringInspection") @Autowired private UserDetailsService userDetailsService; @Override protected void configure(HttpSecurity http) throws Exception { http.authorizeRequests() // 認証の対象外にしたいURLがある場合には、以下のような記述を追加します // 複数URLがある場合はantMatchersメソッドにカンマ区切りで対象URLを複数列挙します // .antMatchers("/country/**").permitAll() .antMatchers("/fonts/**").permitAll() .antMatchers("/html/**").permitAll() .antMatchers("/encode").permitAll() .anyRequest().authenticated(); http.formLogin() .loginPage("/") .loginProcessingUrl("/login") .defaultSuccessUrl("/loginsuccess") .failureUrl("/") .usernameParameter("id") .passwordParameter("password") .permitAll() .and() .logout() .logoutRequestMatcher(new AntPathRequestMatcher("/logout")) .logoutSuccessUrl("/") .deleteCookies("JSESSIONID") .invalidateHttpSession(true) .permitAll(); } @Autowired public void configAuthentication(AuthenticationManagerBuilder auth) throws Exception { auth.userDetailsService(userDetailsService) .passwordEncoder(new BCryptPasswordEncoder()); } }
private UserDetailsService userDetailsService;
を追加します。- 最初
private UserInfoUserDetailsService userInfoUserDetailsService;
と書いたのですが、Tomcat 起動時にCaused by: java.lang.IllegalArgumentException: Can not set ksbysample.webapp.lending.security.UserInfoUserDetailsService field ksbysample.webapp.lending.config.WebSecurityConfig.userInfoUserDetailsService to com.sun.proxy.$Proxy64
というエラーが出て起動しませんでした。 - いろいろ試した結果、実装クラス名ではなくインターフェース名で書いておけば UserInfoUserDetailsService のインスタンスをインジェクションしてくれました。仕組みがよく分かっていないのですが、一旦これで進めます。
@SuppressWarnings("SpringJavaAutowiringInspection")
は、これがないと赤い波線が表示されるので付けています。
- 最初
.defaultSuccessUrl("/loginsuccess")
に変更します。- configAuthentication メソッド内に
auth.userDetailsService(userDetailsService)
を追加し、今回作成したUserDetailsService インターフェース実装クラス UserInfoUserDetailsService の loadUserByUsername メソッドが認証時に呼ばれるようにします。
create_table.sql
create table user_info ( user_id bigserial primary key , username varchar(32) not null , password varchar(256) not null , mail_address varchar(256) not null , enabled smallint not null default 1 ); create index user_info_idx_01 on user_info(mail_address);
- password を
varchar(32)
→varchar(256)
に変更します。
UserInfoUserDetails.java
package ksbysample.webapp.lending.security; import ksbysample.webapp.lending.entity.UserInfo; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.userdetails.User; import org.springframework.security.core.userdetails.UserDetails; import java.util.Collection; import java.util.Set; public class UserInfoUserDetails implements UserDetails { private UserInfo userInfo; private final Set<GrantedAuthority> authorities; private final boolean accountNonExpired; private final boolean accountNonLocked; private final boolean credentialsNonExpired; private final boolean enabled; public UserInfoUserDetails(UserInfo userInfo , Set<GrantedAuthority> authorities , boolean accountNonExpired , boolean accountNonLocked , boolean credentialsNonExpired , boolean enabled) { this.userInfo = userInfo; this.authorities = authorities; this.accountNonExpired = accountNonExpired; this.accountNonLocked = accountNonLocked; this.credentialsNonExpired = credentialsNonExpired; this.enabled = (userInfo.getEnabled() == 1); } @Override public Collection<? extends GrantedAuthority> getAuthorities() { return authorities; } @Override public String getPassword() { return userInfo.getPassword(); } @Override public String getUsername() { return userInfo.getUsername(); } public String getMailAddress() { return userInfo.getMailAddress(); } @Override public boolean isAccountNonExpired() { return accountNonExpired; } @Override public boolean isAccountNonLocked() { return accountNonLocked; } @Override public boolean isCredentialsNonExpired() { return credentialsNonExpired; } @Override public boolean isEnabled() { return enabled; } }
UserInfoDao.java
package ksbysample.webapp.lending.dao; import ksbysample.webapp.lending.entity.UserInfo; import ksbysample.webapp.lending.util.doma.ComponentAndAutowiredDomaConfig; import org.seasar.doma.Dao; import org.seasar.doma.Delete; import org.seasar.doma.Insert; import org.seasar.doma.Select; import org.seasar.doma.Update; /** */ @Dao @ComponentAndAutowiredDomaConfig public interface UserInfoDao { /** * @param userId * @return the UserInfo entity */ @Select UserInfo selectById(Long userId); @Select UserInfo selectByMailAddress(String mailAddress); /** * @param entity * @return affected rows */ @Insert int insert(UserInfo entity); /** * @param entity * @return affected rows */ @Update int update(UserInfo entity); /** * @param entity * @return affected rows */ @Delete int delete(UserInfo entity); }
UserInfo selectByMailAddress(String mailAddress);
を追加します。
selectByUsername.sql
select /*%expand*/* from user_info where mail_address = /* mailAddress */'tanaka.taro@sample.com'
UserRoleDao.java
package ksbysample.webapp.lending.dao; import ksbysample.webapp.lending.entity.UserRole; import ksbysample.webapp.lending.util.doma.ComponentAndAutowiredDomaConfig; import org.seasar.doma.Dao; import org.seasar.doma.Delete; import org.seasar.doma.Insert; import org.seasar.doma.Select; import org.seasar.doma.Update; import java.util.List; /** */ @Dao @ComponentAndAutowiredDomaConfig public interface UserRoleDao { /** * @param roleId * @return the UserRole entity */ @Select UserRole selectById(Long roleId); @Select List<UserRole> selectByUserId(Long userId); /** * @param entity * @return affected rows */ @Insert int insert(UserRole entity); /** * @param entity * @return affected rows */ @Update int update(UserRole entity); /** * @param entity * @return affected rows */ @Delete int delete(UserRole entity); }
List<UserRole> selectByUserId(Long userId);
を追加します。
selectByUserId.sql
select /*%expand*/* from user_role where user_id = /* userId */1
UserInfoUserDetailsService.java
package ksbysample.webapp.lending.security; import ksbysample.webapp.lending.entity.UserInfo; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.userdetails.UserDetails; import java.util.Collection; import java.util.Set; public class UserInfoUserDetails implements UserDetails { private UserInfo userInfo; private final Set<? extends GrantedAuthority> authorities; private final boolean accountNonExpired; private final boolean accountNonLocked; private final boolean credentialsNonExpired; private final boolean enabled; public UserInfoUserDetails(UserInfo userInfo , Set<? extends GrantedAuthority> authorities , boolean accountNonExpired , boolean accountNonLocked , boolean credentialsNonExpired) { this.userInfo = userInfo; this.authorities = authorities; this.accountNonExpired = accountNonExpired; this.accountNonLocked = accountNonLocked; this.credentialsNonExpired = credentialsNonExpired; this.enabled = (userInfo.getEnabled() == 1); } @Override public Collection<? extends GrantedAuthority> getAuthorities() { return authorities; } @Override public String getPassword() { return userInfo.getPassword(); } @Override public String getUsername() { return userInfo.getUsername(); } public String getMailAddress() { return userInfo.getMailAddress(); } @Override public boolean isAccountNonExpired() { return accountNonExpired; } @Override public boolean isAccountNonLocked() { return accountNonLocked; } @Override public boolean isCredentialsNonExpired() { return credentialsNonExpired; } @Override public boolean isEnabled() { return enabled; } }
build.gradle
dependencies { def jdbcDriver = "org.postgresql:postgresql:9.4-1201-jdbc41" // spring-boot-gradle-plugin によりバージョン番号が自動で設定されるもの // Appendix E. Dependency versions ( http://docs.spring.io/spring-boot/docs/current/reference/html/appendix-dependency-versions.html ) 参照 compile("org.springframework.boot:spring-boot-starter-web") compile("org.springframework.boot:spring-boot-starter-thymeleaf") compile("org.thymeleaf.extras:thymeleaf-extras-springsecurity3") compile("org.springframework.boot:spring-boot-starter-data-jpa") compile("org.springframework.boot:spring-boot-starter-velocity") compile("org.springframework.boot:spring-boot-starter-mail") compile("org.springframework.boot:spring-boot-starter-security") compile("org.codehaus.janino:janino") testCompile("org.springframework.boot:spring-boot-starter-test") testCompile("org.springframework.security:spring-security-test:4.0.1.RELEASE") testCompile("org.yaml:snakeyaml") // spring-boot-gradle-plugin によりバージョン番号が自動で設定されないもの compile("${jdbcDriver}") compile("org.seasar.doma:doma:2.3.1") compile("org.bgee.log4jdbc-log4j2:log4jdbc-log4j2-jdbc4.1:1.16") compile("org.apache.commons:commons-lang3:3.4") compile("org.projectlombok:lombok:1.16.4") compile("com.google.guava:guava:18.0") testCompile("org.dbunit:dbunit:2.5.1") testCompile("com.icegreen:greenmail:1.4.1") // for Doma-Gen domaGenRuntime("org.seasar.doma:doma-gen:2.3.1") domaGenRuntime("${jdbcDriver}") }
- Thymeleaf テンプレートファイル内で Spring security dialect を使用するので、
compile("org.thymeleaf.extras:thymeleaf-extras-springsecurity3")
を追加します。
loginsuccess.html
<!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org" xmlns:sec="http://www.w3.org/1999/xhtml"> <head> <meta charset="UTF-8"/> <meta http-equiv="X-UA-Compatible" content="IE=edge"/> <title>テスト用ログイン成功ページ</title> <!-- Tell the browser to be responsive to screen width --> <meta content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no" name="viewport"/> <!-- Bootstrap 3.3.4 --> <link href="/css/bootstrap.min.css" rel="stylesheet" type="text/css"/> <!-- Font Awesome Icons --> <link href="/css/font-awesome.min.css" rel="stylesheet" type="text/css"/> <!-- Ionicons --> <link href="/css/ionicons.min.css" rel="stylesheet" type="text/css"/> <!-- Theme style --> <link href="/css/AdminLTE.min.css" rel="stylesheet" type="text/css"/> <!-- AdminLTE Skins. Choose a skin from the css/skins folder instead of downloading all of them to reduce the load. --> <link href="/css/skins/_all-skins.min.css" rel="stylesheet" type="text/css"/> <!-- HTML5 Shim and Respond.js IE8 support of HTML5 elements and media queries --> <!-- WARNING: Respond.js doesn't work if you view the page via file:// --> <!--[if lt IE 9]> <script src="/js/html5shiv.min.js"></script> <script src="/js/respond.min.js"></script> <![endif]--> <style> <!-- .content-wrapper { background-color: #fffafa; padding-top: 50px; } .form-group { margin-bottom: 5px; } --> </style> </head> <!-- ADD THE CLASS layout-top-nav TO REMOVE THE SIDEBAR. --> <body class="skin-blue layout-top-nav"> <div class="wrapper"> <!-- Full Width Column --> <div class="content-wrapper"> <div class="container"> <!-- Main content --> <section class="content"> ログイン成功!<br/> ユーザ名: <span sec:authentication="name">Bob</span><br/> メールアドレス: <span sec:authentication="principal.mailAddress">test@sample.com</span><br/> Roles: <span sec:authentication="principal.authorities">[ROLE_USER, ROLE_ADMIN]</span><br/> <div sec:authorize="hasRole('ROLE_ADMIN')">ROLE_ADMIN が付与されています</div> <div sec:authorize="hasRole('ROLE_APPROVER')">ROLE_APPROVER が付与されています</div> <div sec:authorize="hasRole('ROLE_USER')">ROLE_USER が付与されています</div> </section> <!-- /.content --> </div> <!-- /.container --> </div> </div> <!-- ./wrapper --> <!-- jQuery 2.1.4 --> <script src="/js/jQuery-2.1.4.min.js" type="text/javascript"></script> <!-- Bootstrap 3.3.2 JS --> <script src="/js/bootstrap.min.js" type="text/javascript"></script> <!-- AdminLTE App --> <script src="/js/app.min.js" type="text/javascript"></script> <script type="text/javascript"> <!-- --> </script> </body> </html>
履歴
2015/07/18
初版発行。