Spring Boot セキュリティ関連

Spring Boot上でSpring Securityで認証時にDB上のユーザーを利用してみた

今回は、Spring Securityを利用してユーザー認証を行う際に、データベースから取得したログインユーザーを利用するように修正してみたので、そのサンプルプログラムを共有する。

前提条件

下記記事の実装が完了していること。

Spring Boot上で独自ログイン画面上でSpring Securityの認証を行ってみた今回は、Spring Securityを利用して、独自ログイン画面を作成した上で、Spring Securityの認証処理を実装してみた...

また、Oracleデータベース上に、以下のuser_passテーブルが作成できていること

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
create table user_pass (
name VARCHAR(40) PRIMARY KEY NOT NULL,
pass VARCHAR(120) NOT NULL
);
create table user_pass ( name VARCHAR(40) PRIMARY KEY NOT NULL, pass VARCHAR(120) NOT NULL );
create table user_pass (
    name VARCHAR(40) PRIMARY KEY NOT NULL,
    pass VARCHAR(120) NOT NULL
);
user_passテーブル作成

サンプルプログラムの作成

作成したサンプルプログラムの構成は以下の通り。
サンプルプログラムの構成
なお、上記の赤枠は、前提条件のプログラムから変更したプログラムである。

Spring Securityの設定を行うクラスの内容は以下の通りで、「configure(AuthenticationManagerBuilder auth)」メソッドでSpring-Security用のユーザーアカウント情報をデータベースからロードするサービスクラスの呼び出しを行うように変更すると共に、パスワードをBCryptで暗号化するクラスのオブジェクトを作成している。

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
package com.example.demo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
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.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.firewall.DefaultHttpFirewall;
@Configuration
@EnableWebSecurity
public class DemoSecurityConfig extends WebSecurityConfigurerAdapter {
/**
* Spring-Security用のユーザーアカウント情報を
* 取得・設定するサービスへのアクセス
*/
@Autowired
private UserPassAccountService userDetailsService;
@Override
public void configure(WebSecurity web) {
//org.springframework.security.web.firewall.RequestRejectedException:
//The request was rejected because the URL contained a potentially malicious String ";"
//というエラーログがコンソールに出力されるため、下記を追加
DefaultHttpFirewall firewall = new DefaultHttpFirewall();
web.httpFirewall(firewall);
}
/**
* SpringSecurityによる認証を設定
* @param http HttpSecurityオブジェクト
* @throws Exception 例外
*/
@Override
protected void configure(HttpSecurity http) throws Exception {
//初期表示画面を表示する際にログイン画面を表示する
http.formLogin()
//ログイン画面は常にアクセス可能とする
.loginPage("/login").permitAll()
//ログインに成功したら検索画面に遷移する
.defaultSuccessUrl("/")
.and()
//ログイン画面のcssファイルとしても共通のdemo.cssを利用するため、
//src/main/resources/static/cssフォルダ下は常にアクセス可能とする
.authorizeRequests().antMatchers("/css/**").permitAll()
.and() //かつ
//それ以外の画面は全て認証を有効にする
.authorizeRequests().anyRequest().authenticated()
.and() //かつ
//ログアウト時はログイン画面に遷移する
.logout().logoutSuccessUrl("/login").permitAll();
}
/**
* 認証するユーザー情報をデータベースからロードする処理
* @param auth 認証マネージャー生成ツール
* @throws Exception
*/
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
//認証するユーザー情報をデータベースからロードする
//その際、パスワードはBCryptで暗号化した値を利用する
auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
}
/**
* パスワードをBCryptで暗号化するクラス
* @return パスワードをBCryptで暗号化するクラスオブジェクト
*/
@Bean
PasswordEncoder passwordEncoder(){
return new BCryptPasswordEncoder();
}
}
package com.example.demo; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; 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.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.security.web.firewall.DefaultHttpFirewall; @Configuration @EnableWebSecurity public class DemoSecurityConfig extends WebSecurityConfigurerAdapter { /** * Spring-Security用のユーザーアカウント情報を * 取得・設定するサービスへのアクセス */ @Autowired private UserPassAccountService userDetailsService; @Override public void configure(WebSecurity web) { //org.springframework.security.web.firewall.RequestRejectedException: //The request was rejected because the URL contained a potentially malicious String ";" //というエラーログがコンソールに出力されるため、下記を追加 DefaultHttpFirewall firewall = new DefaultHttpFirewall(); web.httpFirewall(firewall); } /** * SpringSecurityによる認証を設定 * @param http HttpSecurityオブジェクト * @throws Exception 例外 */ @Override protected void configure(HttpSecurity http) throws Exception { //初期表示画面を表示する際にログイン画面を表示する http.formLogin() //ログイン画面は常にアクセス可能とする .loginPage("/login").permitAll() //ログインに成功したら検索画面に遷移する .defaultSuccessUrl("/") .and() //ログイン画面のcssファイルとしても共通のdemo.cssを利用するため、 //src/main/resources/static/cssフォルダ下は常にアクセス可能とする .authorizeRequests().antMatchers("/css/**").permitAll() .and() //かつ //それ以外の画面は全て認証を有効にする .authorizeRequests().anyRequest().authenticated() .and() //かつ //ログアウト時はログイン画面に遷移する .logout().logoutSuccessUrl("/login").permitAll(); } /** * 認証するユーザー情報をデータベースからロードする処理 * @param auth 認証マネージャー生成ツール * @throws Exception */ @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { //認証するユーザー情報をデータベースからロードする //その際、パスワードはBCryptで暗号化した値を利用する auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder()); } /** * パスワードをBCryptで暗号化するクラス * @return パスワードをBCryptで暗号化するクラスオブジェクト */ @Bean PasswordEncoder passwordEncoder(){ return new BCryptPasswordEncoder(); } }
package com.example.demo;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
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.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.firewall.DefaultHttpFirewall;

@Configuration
@EnableWebSecurity
public class DemoSecurityConfig extends WebSecurityConfigurerAdapter {

    /**
     * Spring-Security用のユーザーアカウント情報を
     * 取得・設定するサービスへのアクセス
     */
    @Autowired
    private UserPassAccountService userDetailsService;

    @Override
    public void configure(WebSecurity web) {
        //org.springframework.security.web.firewall.RequestRejectedException:
        //The request was rejected because the URL contained a potentially malicious String ";"
        //というエラーログがコンソールに出力されるため、下記を追加
        DefaultHttpFirewall firewall = new DefaultHttpFirewall();
        web.httpFirewall(firewall);
    }

    /**
     * SpringSecurityによる認証を設定
     * @param http HttpSecurityオブジェクト
     * @throws Exception 例外
     */
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        //初期表示画面を表示する際にログイン画面を表示する
        http.formLogin()
                //ログイン画面は常にアクセス可能とする
                .loginPage("/login").permitAll()
                //ログインに成功したら検索画面に遷移する
                .defaultSuccessUrl("/")
                .and()
                //ログイン画面のcssファイルとしても共通のdemo.cssを利用するため、
                //src/main/resources/static/cssフォルダ下は常にアクセス可能とする
                .authorizeRequests().antMatchers("/css/**").permitAll()
                .and()    //かつ
                //それ以外の画面は全て認証を有効にする
                .authorizeRequests().anyRequest().authenticated()
                .and()    //かつ
                //ログアウト時はログイン画面に遷移する
                .logout().logoutSuccessUrl("/login").permitAll();
    }

    /**
     * 認証するユーザー情報をデータベースからロードする処理
     * @param auth 認証マネージャー生成ツール
     * @throws Exception
     */
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        //認証するユーザー情報をデータベースからロードする
        //その際、パスワードはBCryptで暗号化した値を利用する
        auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
    }

    /**
     * パスワードをBCryptで暗号化するクラス
     * @return パスワードをBCryptで暗号化するクラスオブジェクト
     */
    @Bean
    PasswordEncoder passwordEncoder(){
        return new BCryptPasswordEncoder();
    }
}



また、Spring-Security用のサービスクラスの内容は以下の通りで、Spring-Security用のユーザーアカウント情報をデータベースからロードする処理と、ユーザーアカウント情報をデータベースに登録する処理を定義している。

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
package com.example.demo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;
@Service
public class UserPassAccountService implements UserDetailsService {
/**
* ユーザーパスワードデータテーブル(user_pass)へアクセスするマッパー
*/
@Autowired
private UserPassMapper mapper;
/**
* 指定したユーザー名をもつSpring-Security用のユーザーアカウント情報を取得する
* @param username ユーザー名
* @return 指定したユーザー名をもつSpring-Security用のユーザーアカウント情報
* @throws UsernameNotFoundException
*/
@Override
public UserDetails loadUserByUsername(String username)
throws UsernameNotFoundException {
if (StringUtils.isEmpty(username)) {
throw new UsernameNotFoundException("ユーザー名を入力してください");
}
//指定したユーザー名をもつUserPassオブジェクトを取得する
UserPass userPass = mapper.findByName(username);
if(userPass == null){
throw new UsernameNotFoundException("ユーザーが見つかりません");
}
//指定したユーザー名をもつSpring-Security用のユーザーアカウント情報を取得する
return new UserPassAccount(userPass, AuthorityUtils.createAuthorityList("USER"));
}
/**
* 指定したユーザー名・パスワードをもつレコードをユーザーパスワードデータ
* テーブル(user_pass)に登録する
* @param username ユーザー名
* @param password パスワード
*/
@Transactional
public void registerUser(String username, String password){
if(StringUtils.isEmpty(username) || StringUtils.isEmpty(password)){
return;
}
//指定したユーザー名をもつUserPassオブジェクトを取得する
UserPass userPass = mapper.findByName(username);
//UserPassオブジェクトが無ければ追加・あれば更新する
if(userPass == null){
userPass = new UserPass(username, password);
mapper.create(userPass);
}else{
mapper.update(userPass);
}
}
}
package com.example.demo; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.core.authority.AuthorityUtils; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.core.userdetails.UsernameNotFoundException; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.util.StringUtils; @Service public class UserPassAccountService implements UserDetailsService { /** * ユーザーパスワードデータテーブル(user_pass)へアクセスするマッパー */ @Autowired private UserPassMapper mapper; /** * 指定したユーザー名をもつSpring-Security用のユーザーアカウント情報を取得する * @param username ユーザー名 * @return 指定したユーザー名をもつSpring-Security用のユーザーアカウント情報 * @throws UsernameNotFoundException */ @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { if (StringUtils.isEmpty(username)) { throw new UsernameNotFoundException("ユーザー名を入力してください"); } //指定したユーザー名をもつUserPassオブジェクトを取得する UserPass userPass = mapper.findByName(username); if(userPass == null){ throw new UsernameNotFoundException("ユーザーが見つかりません"); } //指定したユーザー名をもつSpring-Security用のユーザーアカウント情報を取得する return new UserPassAccount(userPass, AuthorityUtils.createAuthorityList("USER")); } /** * 指定したユーザー名・パスワードをもつレコードをユーザーパスワードデータ * テーブル(user_pass)に登録する * @param username ユーザー名 * @param password パスワード */ @Transactional public void registerUser(String username, String password){ if(StringUtils.isEmpty(username) || StringUtils.isEmpty(password)){ return; } //指定したユーザー名をもつUserPassオブジェクトを取得する UserPass userPass = mapper.findByName(username); //UserPassオブジェクトが無ければ追加・あれば更新する if(userPass == null){ userPass = new UserPass(username, password); mapper.create(userPass); }else{ mapper.update(userPass); } } }
package com.example.demo;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

@Service
public class UserPassAccountService implements UserDetailsService {

    /**
     * ユーザーパスワードデータテーブル(user_pass)へアクセスするマッパー
     */
    @Autowired
    private UserPassMapper mapper;

    /**
     * 指定したユーザー名をもつSpring-Security用のユーザーアカウント情報を取得する
     * @param username ユーザー名
     * @return 指定したユーザー名をもつSpring-Security用のユーザーアカウント情報
     * @throws UsernameNotFoundException
     */
    @Override
    public UserDetails loadUserByUsername(String username) 
                               throws UsernameNotFoundException {
        if (StringUtils.isEmpty(username)) {
            throw new UsernameNotFoundException("ユーザー名を入力してください");
        }
        //指定したユーザー名をもつUserPassオブジェクトを取得する
        UserPass userPass = mapper.findByName(username);
        if(userPass == null){
            throw new UsernameNotFoundException("ユーザーが見つかりません");
        }
        //指定したユーザー名をもつSpring-Security用のユーザーアカウント情報を取得する
        return new UserPassAccount(userPass, AuthorityUtils.createAuthorityList("USER"));
    }

    /**
     * 指定したユーザー名・パスワードをもつレコードをユーザーパスワードデータ
     * テーブル(user_pass)に登録する
     * @param username ユーザー名
     * @param password パスワード
     */
    @Transactional
    public void registerUser(String username, String password){
        if(StringUtils.isEmpty(username) || StringUtils.isEmpty(password)){
            return;
        }
        //指定したユーザー名をもつUserPassオブジェクトを取得する
        UserPass userPass = mapper.findByName(username);
        //UserPassオブジェクトが無ければ追加・あれば更新する
        if(userPass == null){
            userPass = new UserPass(username, password);
            mapper.create(userPass);
        }else{
            mapper.update(userPass);
        }
    }
}



また、Spring-Security用のユーザーアカウント情報を定義しているクラスの内容は以下の通り。

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
package com.example.demo;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import java.util.Collection;
public class UserPassAccount implements UserDetails {
/** UserPassオブジェクト */
private UserPass userPass;
/** ユーザー権限情報 */
private Collection<GrantedAuthority> authorities;
/**
* Spring-Security用のユーザーアカウント情報(UserDetails)を作成する
* @param userPass UserPassオブジェクト
* @param authorities ユーザー権限情報
*/
public UserPassAccount(UserPass userPass, Collection<GrantedAuthority> authorities){
this.userPass = userPass;
this.authorities = authorities;
}
/**
* ユーザー権限情報を取得する
* @return ユーザー権限情報
*/
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return this.authorities;
}
/**
* パスワードを取得する
* @return パスワード
*/
@Override
public String getPassword() {
return userPass.getPass();
}
/**
* ユーザー名を取得する
* @return ユーザー名
*/
@Override
public String getUsername() {
return userPass.getName();
}
/**
* アカウントが期限切れでないかを取得する
* @return アカウントが期限切れでないか
*/
@Override
public boolean isAccountNonExpired() {
return true;
}
/**
* アカウントがロックされていないかを取得する
* @return アカウントがロックされていないか
*/
@Override
public boolean isAccountNonLocked() {
return true;
}
/**
* アカウントが認証期限切れでないかを取得する
* @return アカウントが認証期限切れでないか
*/
@Override
public boolean isCredentialsNonExpired() {
return true;
}
/**
* アカウントが利用可能かを取得する
* @return アカウントが利用可能か
*/
@Override
public boolean isEnabled() {
return true;
}
}
package com.example.demo; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.userdetails.UserDetails; import java.util.Collection; public class UserPassAccount implements UserDetails { /** UserPassオブジェクト */ private UserPass userPass; /** ユーザー権限情報 */ private Collection<GrantedAuthority> authorities; /** * Spring-Security用のユーザーアカウント情報(UserDetails)を作成する * @param userPass UserPassオブジェクト * @param authorities ユーザー権限情報 */ public UserPassAccount(UserPass userPass, Collection<GrantedAuthority> authorities){ this.userPass = userPass; this.authorities = authorities; } /** * ユーザー権限情報を取得する * @return ユーザー権限情報 */ @Override public Collection<? extends GrantedAuthority> getAuthorities() { return this.authorities; } /** * パスワードを取得する * @return パスワード */ @Override public String getPassword() { return userPass.getPass(); } /** * ユーザー名を取得する * @return ユーザー名 */ @Override public String getUsername() { return userPass.getName(); } /** * アカウントが期限切れでないかを取得する * @return アカウントが期限切れでないか */ @Override public boolean isAccountNonExpired() { return true; } /** * アカウントがロックされていないかを取得する * @return アカウントがロックされていないか */ @Override public boolean isAccountNonLocked() { return true; } /** * アカウントが認証期限切れでないかを取得する * @return アカウントが認証期限切れでないか */ @Override public boolean isCredentialsNonExpired() { return true; } /** * アカウントが利用可能かを取得する * @return アカウントが利用可能か */ @Override public boolean isEnabled() { return true; } }
package com.example.demo;

import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import java.util.Collection;

public class UserPassAccount implements UserDetails {

    /** UserPassオブジェクト */
    private UserPass userPass;

    /** ユーザー権限情報 */
    private Collection<GrantedAuthority> authorities;

    /**
     * Spring-Security用のユーザーアカウント情報(UserDetails)を作成する
     * @param userPass UserPassオブジェクト
     * @param authorities ユーザー権限情報
     */
    public UserPassAccount(UserPass userPass, Collection<GrantedAuthority> authorities){
        this.userPass = userPass;
        this.authorities = authorities;
    }

    /**
     * ユーザー権限情報を取得する
     * @return ユーザー権限情報
     */
    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        return this.authorities;
    }

    /**
     * パスワードを取得する
     * @return パスワード
     */
    @Override
    public String getPassword() {
        return userPass.getPass();
    }

    /**
     * ユーザー名を取得する
     * @return ユーザー名
     */
    @Override
    public String getUsername() {
        return userPass.getName();
    }

    /**
     * アカウントが期限切れでないかを取得する
     * @return アカウントが期限切れでないか
     */
    @Override
    public boolean isAccountNonExpired() {
        return true;
    }

    /**
     * アカウントがロックされていないかを取得する
     * @return アカウントがロックされていないか
     */
    @Override
    public boolean isAccountNonLocked() {
        return true;
    }

    /**
     * アカウントが認証期限切れでないかを取得する
     * @return アカウントが認証期限切れでないか
     */
    @Override
    public boolean isCredentialsNonExpired() {
        return true;
    }

    /**
     * アカウントが利用可能かを取得する
     * @return アカウントが利用可能か
     */
    @Override
    public boolean isEnabled() {
        return true;
    }
}



さらに、user_passテーブルにアクセスする処理の内容は以下の通りで、MyBatisを利用するようにしている。

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
package com.example.demo;
import lombok.Data;
@Data
public class UserPass {
/**
* 指定したユーザー名・パスワードをもつUserPassオブジェクトを作成する
* @param name ユーザー名
* @param pass パスワード
*/
public UserPass(String name, String pass){
this.name = name;
this.pass = pass;
}
/** ユーザー名 */
private String name;
/** パスワード */
private String pass;
}
package com.example.demo; import lombok.Data; @Data public class UserPass { /** * 指定したユーザー名・パスワードをもつUserPassオブジェクトを作成する * @param name ユーザー名 * @param pass パスワード */ public UserPass(String name, String pass){ this.name = name; this.pass = pass; } /** ユーザー名 */ private String name; /** パスワード */ private String pass; }
package com.example.demo;

import lombok.Data;

@Data
public class UserPass {

    /**
     * 指定したユーザー名・パスワードをもつUserPassオブジェクトを作成する
     * @param name ユーザー名
     * @param pass パスワード
     */
    public UserPass(String name, String pass){
        this.name = name;
        this.pass = pass;
    }

    /** ユーザー名 */
    private String name;

    /** パスワード */
    private String pass;

}
Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
package com.example.demo;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface UserPassMapper {
/**
* ユーザーパスワードデータテーブル(user_pass)からユーザー名をキーにデータを取得する
* @param name ユーザー名
* @return ユーザー名をもつデータ
*/
UserPass findByName(String name);
/**
* 指定したユーザーパスワードデータテーブル(user_pass)のデータを追加する
* @param userPass ユーザーパスワードデータテーブル(user_pass)の追加データ
*/
void create(UserPass userPass);
/**
* 指定したユーザーパスワードデータテーブル(user_pass)のデータを更新する
* @param userPass ユーザーパスワードデータテーブル(user_pass)の更新データ
*/
void update(UserPass userPass);
}
package com.example.demo; import org.apache.ibatis.annotations.Mapper; @Mapper public interface UserPassMapper { /** * ユーザーパスワードデータテーブル(user_pass)からユーザー名をキーにデータを取得する * @param name ユーザー名 * @return ユーザー名をもつデータ */ UserPass findByName(String name); /** * 指定したユーザーパスワードデータテーブル(user_pass)のデータを追加する * @param userPass ユーザーパスワードデータテーブル(user_pass)の追加データ */ void create(UserPass userPass); /** * 指定したユーザーパスワードデータテーブル(user_pass)のデータを更新する * @param userPass ユーザーパスワードデータテーブル(user_pass)の更新データ */ void update(UserPass userPass); }
package com.example.demo;

import org.apache.ibatis.annotations.Mapper;

@Mapper
public interface UserPassMapper {

    /**
     * ユーザーパスワードデータテーブル(user_pass)からユーザー名をキーにデータを取得する
     * @param name ユーザー名
     * @return ユーザー名をもつデータ
     */
    UserPass findByName(String name);

    /**
     * 指定したユーザーパスワードデータテーブル(user_pass)のデータを追加する
     * @param userPass ユーザーパスワードデータテーブル(user_pass)の追加データ
     */
    void create(UserPass userPass);

    /**
     * 指定したユーザーパスワードデータテーブル(user_pass)のデータを更新する
     * @param userPass ユーザーパスワードデータテーブル(user_pass)の更新データ
     */
    void update(UserPass userPass);

}
Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.demo.UserPassMapper">
<resultMap id="userPassResultMap" type="com.example.demo.UserPass" >
<result column="name" property="name" jdbcType="VARCHAR" />
<result column="pass" property="pass" jdbcType="VARCHAR" />
</resultMap>
<select id="findByName" resultMap="userPassResultMap">
SELECT name, pass FROM USER_PASS WHERE name = #{name}
</select>
<insert id="create" parameterType="com.example.demo.UserPass">
INSERT INTO USER_PASS ( name, pass )
VALUES (#{name}, #{pass})
</insert>
<update id="update" parameterType="com.example.demo.UserPass">
UPDATE USER_PASS SET pass = #{pass}
WHERE name = #{name}
</update>
</mapper>
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="com.example.demo.UserPassMapper"> <resultMap id="userPassResultMap" type="com.example.demo.UserPass" > <result column="name" property="name" jdbcType="VARCHAR" /> <result column="pass" property="pass" jdbcType="VARCHAR" /> </resultMap> <select id="findByName" resultMap="userPassResultMap"> SELECT name, pass FROM USER_PASS WHERE name = #{name} </select> <insert id="create" parameterType="com.example.demo.UserPass"> INSERT INTO USER_PASS ( name, pass ) VALUES (#{name}, #{pass}) </insert> <update id="update" parameterType="com.example.demo.UserPass"> UPDATE USER_PASS SET pass = #{pass} WHERE name = #{name} </update> </mapper>
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.demo.UserPassMapper">
    <resultMap id="userPassResultMap" type="com.example.demo.UserPass" >
        <result column="name" property="name" jdbcType="VARCHAR" />
        <result column="pass" property="pass" jdbcType="VARCHAR" />
    </resultMap>
    <select id="findByName" resultMap="userPassResultMap">
        SELECT name, pass FROM USER_PASS WHERE name = #{name}
    </select>
    <insert id="create" parameterType="com.example.demo.UserPass">
        INSERT INTO USER_PASS ( name, pass )
        VALUES (#{name}, #{pass})
    </insert>
    <update id="update" parameterType="com.example.demo.UserPass">
        UPDATE USER_PASS SET pass = #{pass}
        WHERE name = #{name}
    </update>
</mapper>



また、コントローラクラスの内容は以下の通りで、ログイン画面を表示するメソッド内で、user_passテーブルにログインユーザーのデータを登録する処理を追加している。

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
package com.example.demo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.SessionAttributes;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.support.SessionStatus;
import org.springframework.data.domain.Pageable;
import java.util.ArrayList;
import java.util.List;
@Controller
@SessionAttributes(types = {DemoForm.class, SearchForm.class})
public class DemoController {
/**
* Demoサービスクラスへのアクセス
*/
@Autowired
private DemoService demoService;
/**
* Spring-Security用のユーザーアカウント情報を
* 取得・設定するサービスへのアクセス
*/
@Autowired
private UserPassAccountService userDetailsService;
/**
* パスワードをBCryptで暗号化するクラスへのアクセス
*/
@Autowired
private PasswordEncoder passwordEncoder;
/**
* ユーザーデータテーブル(user_data)のデータを取得して返却する
* @return ユーザーデータリスト
*/
@ModelAttribute("demoFormList")
public List<DemoForm> userDataList(){
List<DemoForm> demoFormList = new ArrayList<>();
return demoFormList;
}
/**
* 追加・更新用Formオブジェクトを初期化して返却する
* @return 追加・更新用Formオブジェクト
*/
@ModelAttribute("demoForm")
public DemoForm createDemoForm(){
DemoForm demoForm = new DemoForm();
return demoForm;
}
/**
* 検索用Formオブジェクトを初期化して返却する
* @return 検索用Formオブジェクト
*/
@ModelAttribute("searchForm")
public SearchForm createSearchForm(){
SearchForm searchForm = new SearchForm();
return searchForm;
}
/**
* ログイン用ユーザーのデータを登録する
* @return ログイン画面へのパス
*/
@GetMapping("/login")
public String login(){
//ユーザー名「user」、パスワード「pass」をユーザーパスワードデータテーブル(user_pass)
//へ登録する。その際、パスワードはBCryptで暗号化する
userDetailsService.registerUser(
"user", passwordEncoder.encode("pass"));
//ログイン画面に遷移する
return "login";
}
/**
* 初期表示(検索)画面に遷移する
* @return 検索画面へのパス
*/
@RequestMapping("/")
public String index(){
return "search";
}
/**
* 検索処理を行い、一覧画面に遷移する
* @param searchForm 検索用Formオブジェクト
* @param result バインド結果
* @param model Modelオブジェクト
* @return 一覧画面へのパス
*/
@PostMapping("/search")
public String search(@Validated SearchForm searchForm
, BindingResult result, Model model){
//検索用Formオブジェクトのチェック処理でエラーがある場合は、
//検索画面のままとする
if(result.hasErrors()){
return "search";
}
//現在ページ数を1ページ目に設定し、一覧画面に遷移する
searchForm.setCurrentPageNum(1);
return movePageInList(model, searchForm);
}
/**
* 一覧画面で「先頭へ」リンク押下時に次ページを表示する
* @param searchForm 検索用Formオブジェクト
* @param model Modelオブジェクト
* @return 一覧画面へのパス
*/
@GetMapping("/firstPage")
public String firstPage(SearchForm searchForm, Model model){
//現在ページ数を先頭ページに設定する
searchForm.setCurrentPageNum(1);
return movePageInList(model, searchForm);
}
/**
* 一覧画面で「前へ」リンク押下時に次ページを表示する
* @param searchForm 検索用Formオブジェクト
* @param model Modelオブジェクト
* @return 一覧画面へのパス
*/
@GetMapping("/backPage")
public String backPage(SearchForm searchForm, Model model){
//現在ページ数を前ページに設定する
searchForm.setCurrentPageNum(searchForm.getCurrentPageNum() - 1);
return movePageInList(model, searchForm);
}
/**
* 一覧画面で「次へ」リンク押下時に次ページを表示する
* @param searchForm 検索用Formオブジェクト
* @param model Modelオブジェクト
* @return 一覧画面へのパス
*/
@GetMapping("/nextPage")
public String nextPage(SearchForm searchForm, Model model){
//現在ページ数を次ページに設定する
searchForm.setCurrentPageNum(searchForm.getCurrentPageNum() + 1);
return movePageInList(model, searchForm);
}
/**
* 一覧画面で「最後へ」リンク押下時に次ページを表示する
* @param searchForm 検索用Formオブジェクト
* @param model Modelオブジェクト
* @return 一覧画面へのパス
*/
@GetMapping("/lastPage")
public String lastPage(SearchForm searchForm, Model model){
//現在ページ数を最終ページに設定する
searchForm.setCurrentPageNum(demoService.getAllPageNum(searchForm));
return movePageInList(model, searchForm);
}
/**
* 更新処理を行う画面に遷移する
* @param id 更新対象のID
* @param model Modelオブジェクト
* @return 入力・更新画面へのパス
*/
@GetMapping("/update")
public String update(@RequestParam("id") String id, Model model){
//更新対象のユーザーデータを取得
DemoForm demoForm = demoService.findById(id);
//ユーザーデータを更新
model.addAttribute("demoForm", demoForm);
return "input";
}
/**
* 削除確認画面に遷移する
* @param id 更新対象のID
* @param model Modelオブジェクト
* @return 削除確認画面へのパス
*/
@GetMapping("/delete_confirm")
public String delete_confirm(@RequestParam("id") String id, Model model){
//削除対象のユーザーデータを取得
DemoForm demoForm = demoService.findById(id);
//ユーザーデータを更新
model.addAttribute("demoForm", demoForm);
return "confirm_delete";
}
/**
* 削除処理を行う
* @param demoForm 追加・更新用Formオブジェクト
* @return 一覧画面の表示処理
*/
@PostMapping(value = "/delete", params = "next")
public String delete(DemoForm demoForm){
//指定したユーザーデータを削除
demoService.deleteById(demoForm.getId());
//一覧画面に遷移
return "redirect:/to_index";
}
/**
* 削除完了後に一覧画面に戻る
* @param searchForm 検索用Formオブジェクト
* @param model Modelオブジェクト
* @return 一覧画面
*/
@GetMapping("/to_index")
public String toIndex(SearchForm searchForm, Model model){
//一覧画面に戻り、1ページ目のリストを表示する
searchForm.setCurrentPageNum(1);
return movePageInList(model, searchForm);
}
/**
* 削除確認画面から一覧画面に戻る
* @param model Modelオブジェクト
* @param searchForm 検索用Formオブジェクト
* @return 一覧画面
*/
@PostMapping(value = "/delete", params = "back")
public String confirmDeleteBack(Model model, SearchForm searchForm){
//一覧画面に戻る
return movePageInList(model, searchForm);
}
/**
* 追加処理を行う画面に遷移する
* @param model Modelオブジェクト
* @return 入力・更新画面へのパス
*/
@PostMapping(value = "/add", params = "next")
public String add(Model model){
model.addAttribute("demoForm", new DemoForm());
return "input";
}
/**
* 追加処理を行う画面から検索画面に戻る
* @return 検索画面へのパス
*/
@PostMapping(value = "/add", params = "back")
public String addBack(){
return "search";
}
/**
* エラーチェックを行い、エラーが無ければ確認画面に遷移し、
* エラーがあれば入力画面のままとする
* @param demoForm 追加・更新用Formオブジェクト
* @param result バインド結果
* @return 確認画面または入力画面へのパス
*/
@PostMapping(value = "/confirm", params = "next")
public String confirm(@Validated DemoForm demoForm, BindingResult result){
//追加・更新用Formオブジェクトのチェック処理でエラーがある場合は、
//入力画面のままとする
if(result.hasErrors()){
return "input";
}
//エラーが無ければ確認画面に遷移する
return "confirm";
}
/**
* 一覧画面に戻る
* @param model Modelオブジェクト
* @param searchForm 検索用Formオブジェクト
* @return 一覧画面の表示処理
*/
@PostMapping(value = "/confirm", params = "back")
public String confirmBack(Model model, SearchForm searchForm){
//一覧画面に戻る
return movePageInList(model, searchForm);
}
/**
* 完了画面に遷移する
* @param demoForm 追加・更新用Formオブジェクト
* @param result バインド結果
* @return 完了画面
*/
@PostMapping(value = "/send", params = "next")
public String send(DemoForm demoForm, BindingResult result){
//チェック処理を行い、エラーがなければ、更新・追加処理を行う
if(result.hasErrors()){
return "input";
}
demoService.createOrUpdate(demoForm);
return "redirect:/complete";
}
/**
* 完了画面に遷移する
* @param sessionStatus セッションステータス
* @return 完了画面
*/
@GetMapping("/complete")
public String complete(SessionStatus sessionStatus){
//セッションオブジェクトを破棄
sessionStatus.setComplete();
return "complete";
}
/**
* 入力画面に戻る
* @return 入力画面
*/
@PostMapping(value = "/send", params = "back")
public String sendBack(){
return "input";
}
/**
* 一覧画面に戻り、指定した現在ページのリストを表示する
* @param model Modelオブジェクト
* @param searchForm 検索用Formオブジェクト
* @return 一覧画面の表示処理
*/
private String movePageInList(Model model, SearchForm searchForm){
//現在ページ数, 総ページ数を設定
model.addAttribute("currentPageNum", searchForm.getCurrentPageNum());
model.addAttribute("allPageNum", demoService.getAllPageNum(searchForm));
//ページング用オブジェクトを生成し、現在ページのユーザーデータリストを取得
Pageable pageable = demoService.getPageable(searchForm.getCurrentPageNum());
List<DemoForm> demoFormList = demoService.demoFormList(searchForm, pageable);
//ユーザーデータリストを更新
model.addAttribute("demoFormList", demoFormList);
return "list";
}
}
package com.example.demo; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.SessionAttributes; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ModelAttribute; import org.springframework.validation.BindingResult; import org.springframework.web.bind.support.SessionStatus; import org.springframework.data.domain.Pageable; import java.util.ArrayList; import java.util.List; @Controller @SessionAttributes(types = {DemoForm.class, SearchForm.class}) public class DemoController { /** * Demoサービスクラスへのアクセス */ @Autowired private DemoService demoService; /** * Spring-Security用のユーザーアカウント情報を * 取得・設定するサービスへのアクセス */ @Autowired private UserPassAccountService userDetailsService; /** * パスワードをBCryptで暗号化するクラスへのアクセス */ @Autowired private PasswordEncoder passwordEncoder; /** * ユーザーデータテーブル(user_data)のデータを取得して返却する * @return ユーザーデータリスト */ @ModelAttribute("demoFormList") public List<DemoForm> userDataList(){ List<DemoForm> demoFormList = new ArrayList<>(); return demoFormList; } /** * 追加・更新用Formオブジェクトを初期化して返却する * @return 追加・更新用Formオブジェクト */ @ModelAttribute("demoForm") public DemoForm createDemoForm(){ DemoForm demoForm = new DemoForm(); return demoForm; } /** * 検索用Formオブジェクトを初期化して返却する * @return 検索用Formオブジェクト */ @ModelAttribute("searchForm") public SearchForm createSearchForm(){ SearchForm searchForm = new SearchForm(); return searchForm; } /** * ログイン用ユーザーのデータを登録する * @return ログイン画面へのパス */ @GetMapping("/login") public String login(){ //ユーザー名「user」、パスワード「pass」をユーザーパスワードデータテーブル(user_pass) //へ登録する。その際、パスワードはBCryptで暗号化する userDetailsService.registerUser( "user", passwordEncoder.encode("pass")); //ログイン画面に遷移する return "login"; } /** * 初期表示(検索)画面に遷移する * @return 検索画面へのパス */ @RequestMapping("/") public String index(){ return "search"; } /** * 検索処理を行い、一覧画面に遷移する * @param searchForm 検索用Formオブジェクト * @param result バインド結果 * @param model Modelオブジェクト * @return 一覧画面へのパス */ @PostMapping("/search") public String search(@Validated SearchForm searchForm , BindingResult result, Model model){ //検索用Formオブジェクトのチェック処理でエラーがある場合は、 //検索画面のままとする if(result.hasErrors()){ return "search"; } //現在ページ数を1ページ目に設定し、一覧画面に遷移する searchForm.setCurrentPageNum(1); return movePageInList(model, searchForm); } /** * 一覧画面で「先頭へ」リンク押下時に次ページを表示する * @param searchForm 検索用Formオブジェクト * @param model Modelオブジェクト * @return 一覧画面へのパス */ @GetMapping("/firstPage") public String firstPage(SearchForm searchForm, Model model){ //現在ページ数を先頭ページに設定する searchForm.setCurrentPageNum(1); return movePageInList(model, searchForm); } /** * 一覧画面で「前へ」リンク押下時に次ページを表示する * @param searchForm 検索用Formオブジェクト * @param model Modelオブジェクト * @return 一覧画面へのパス */ @GetMapping("/backPage") public String backPage(SearchForm searchForm, Model model){ //現在ページ数を前ページに設定する searchForm.setCurrentPageNum(searchForm.getCurrentPageNum() - 1); return movePageInList(model, searchForm); } /** * 一覧画面で「次へ」リンク押下時に次ページを表示する * @param searchForm 検索用Formオブジェクト * @param model Modelオブジェクト * @return 一覧画面へのパス */ @GetMapping("/nextPage") public String nextPage(SearchForm searchForm, Model model){ //現在ページ数を次ページに設定する searchForm.setCurrentPageNum(searchForm.getCurrentPageNum() + 1); return movePageInList(model, searchForm); } /** * 一覧画面で「最後へ」リンク押下時に次ページを表示する * @param searchForm 検索用Formオブジェクト * @param model Modelオブジェクト * @return 一覧画面へのパス */ @GetMapping("/lastPage") public String lastPage(SearchForm searchForm, Model model){ //現在ページ数を最終ページに設定する searchForm.setCurrentPageNum(demoService.getAllPageNum(searchForm)); return movePageInList(model, searchForm); } /** * 更新処理を行う画面に遷移する * @param id 更新対象のID * @param model Modelオブジェクト * @return 入力・更新画面へのパス */ @GetMapping("/update") public String update(@RequestParam("id") String id, Model model){ //更新対象のユーザーデータを取得 DemoForm demoForm = demoService.findById(id); //ユーザーデータを更新 model.addAttribute("demoForm", demoForm); return "input"; } /** * 削除確認画面に遷移する * @param id 更新対象のID * @param model Modelオブジェクト * @return 削除確認画面へのパス */ @GetMapping("/delete_confirm") public String delete_confirm(@RequestParam("id") String id, Model model){ //削除対象のユーザーデータを取得 DemoForm demoForm = demoService.findById(id); //ユーザーデータを更新 model.addAttribute("demoForm", demoForm); return "confirm_delete"; } /** * 削除処理を行う * @param demoForm 追加・更新用Formオブジェクト * @return 一覧画面の表示処理 */ @PostMapping(value = "/delete", params = "next") public String delete(DemoForm demoForm){ //指定したユーザーデータを削除 demoService.deleteById(demoForm.getId()); //一覧画面に遷移 return "redirect:/to_index"; } /** * 削除完了後に一覧画面に戻る * @param searchForm 検索用Formオブジェクト * @param model Modelオブジェクト * @return 一覧画面 */ @GetMapping("/to_index") public String toIndex(SearchForm searchForm, Model model){ //一覧画面に戻り、1ページ目のリストを表示する searchForm.setCurrentPageNum(1); return movePageInList(model, searchForm); } /** * 削除確認画面から一覧画面に戻る * @param model Modelオブジェクト * @param searchForm 検索用Formオブジェクト * @return 一覧画面 */ @PostMapping(value = "/delete", params = "back") public String confirmDeleteBack(Model model, SearchForm searchForm){ //一覧画面に戻る return movePageInList(model, searchForm); } /** * 追加処理を行う画面に遷移する * @param model Modelオブジェクト * @return 入力・更新画面へのパス */ @PostMapping(value = "/add", params = "next") public String add(Model model){ model.addAttribute("demoForm", new DemoForm()); return "input"; } /** * 追加処理を行う画面から検索画面に戻る * @return 検索画面へのパス */ @PostMapping(value = "/add", params = "back") public String addBack(){ return "search"; } /** * エラーチェックを行い、エラーが無ければ確認画面に遷移し、 * エラーがあれば入力画面のままとする * @param demoForm 追加・更新用Formオブジェクト * @param result バインド結果 * @return 確認画面または入力画面へのパス */ @PostMapping(value = "/confirm", params = "next") public String confirm(@Validated DemoForm demoForm, BindingResult result){ //追加・更新用Formオブジェクトのチェック処理でエラーがある場合は、 //入力画面のままとする if(result.hasErrors()){ return "input"; } //エラーが無ければ確認画面に遷移する return "confirm"; } /** * 一覧画面に戻る * @param model Modelオブジェクト * @param searchForm 検索用Formオブジェクト * @return 一覧画面の表示処理 */ @PostMapping(value = "/confirm", params = "back") public String confirmBack(Model model, SearchForm searchForm){ //一覧画面に戻る return movePageInList(model, searchForm); } /** * 完了画面に遷移する * @param demoForm 追加・更新用Formオブジェクト * @param result バインド結果 * @return 完了画面 */ @PostMapping(value = "/send", params = "next") public String send(DemoForm demoForm, BindingResult result){ //チェック処理を行い、エラーがなければ、更新・追加処理を行う if(result.hasErrors()){ return "input"; } demoService.createOrUpdate(demoForm); return "redirect:/complete"; } /** * 完了画面に遷移する * @param sessionStatus セッションステータス * @return 完了画面 */ @GetMapping("/complete") public String complete(SessionStatus sessionStatus){ //セッションオブジェクトを破棄 sessionStatus.setComplete(); return "complete"; } /** * 入力画面に戻る * @return 入力画面 */ @PostMapping(value = "/send", params = "back") public String sendBack(){ return "input"; } /** * 一覧画面に戻り、指定した現在ページのリストを表示する * @param model Modelオブジェクト * @param searchForm 検索用Formオブジェクト * @return 一覧画面の表示処理 */ private String movePageInList(Model model, SearchForm searchForm){ //現在ページ数, 総ページ数を設定 model.addAttribute("currentPageNum", searchForm.getCurrentPageNum()); model.addAttribute("allPageNum", demoService.getAllPageNum(searchForm)); //ページング用オブジェクトを生成し、現在ページのユーザーデータリストを取得 Pageable pageable = demoService.getPageable(searchForm.getCurrentPageNum()); List<DemoForm> demoFormList = demoService.demoFormList(searchForm, pageable); //ユーザーデータリストを更新 model.addAttribute("demoFormList", demoFormList); return "list"; } }
package com.example.demo;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.SessionAttributes;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.support.SessionStatus;
import org.springframework.data.domain.Pageable;
import java.util.ArrayList;
import java.util.List;

@Controller
@SessionAttributes(types = {DemoForm.class, SearchForm.class})
public class DemoController {

    /**
     * Demoサービスクラスへのアクセス
     */
    @Autowired
    private DemoService demoService;

    /**
     * Spring-Security用のユーザーアカウント情報を
     * 取得・設定するサービスへのアクセス
     */
    @Autowired
    private UserPassAccountService userDetailsService;

    /**
     * パスワードをBCryptで暗号化するクラスへのアクセス
     */
    @Autowired
    private PasswordEncoder passwordEncoder;

    /**
     * ユーザーデータテーブル(user_data)のデータを取得して返却する
     * @return ユーザーデータリスト
     */
    @ModelAttribute("demoFormList")
    public List<DemoForm> userDataList(){
        List<DemoForm> demoFormList = new ArrayList<>();
        return demoFormList;
    }

    /**
     * 追加・更新用Formオブジェクトを初期化して返却する
     * @return 追加・更新用Formオブジェクト
     */
    @ModelAttribute("demoForm")
    public DemoForm createDemoForm(){
        DemoForm demoForm = new DemoForm();
        return demoForm;
    }

    /**
     * 検索用Formオブジェクトを初期化して返却する
     * @return 検索用Formオブジェクト
     */
    @ModelAttribute("searchForm")
    public SearchForm createSearchForm(){
        SearchForm searchForm = new SearchForm();
        return searchForm;
    }

    /**
     * ログイン用ユーザーのデータを登録する
     * @return ログイン画面へのパス
     */
    @GetMapping("/login")
    public String login(){
        //ユーザー名「user」、パスワード「pass」をユーザーパスワードデータテーブル(user_pass)
        //へ登録する。その際、パスワードはBCryptで暗号化する
        userDetailsService.registerUser(
                "user", passwordEncoder.encode("pass"));
        //ログイン画面に遷移する
        return "login";
    }

    /**
     * 初期表示(検索)画面に遷移する
     * @return 検索画面へのパス
     */
    @RequestMapping("/")
    public String index(){
        return "search";
    }

    /**
     * 検索処理を行い、一覧画面に遷移する
     * @param searchForm 検索用Formオブジェクト
     * @param result バインド結果
     * @param model Modelオブジェクト
     * @return 一覧画面へのパス
     */
    @PostMapping("/search")
    public String search(@Validated SearchForm searchForm
            , BindingResult result, Model model){
        //検索用Formオブジェクトのチェック処理でエラーがある場合は、
        //検索画面のままとする
        if(result.hasErrors()){
            return "search";
        }
        //現在ページ数を1ページ目に設定し、一覧画面に遷移する
        searchForm.setCurrentPageNum(1);
        return movePageInList(model, searchForm);
    }

    /**
     * 一覧画面で「先頭へ」リンク押下時に次ページを表示する
     * @param searchForm 検索用Formオブジェクト
     * @param model Modelオブジェクト
     * @return 一覧画面へのパス
     */
    @GetMapping("/firstPage")
    public String firstPage(SearchForm searchForm, Model model){
        //現在ページ数を先頭ページに設定する
        searchForm.setCurrentPageNum(1);
        return movePageInList(model, searchForm);
    }

    /**
     * 一覧画面で「前へ」リンク押下時に次ページを表示する
     * @param searchForm 検索用Formオブジェクト
     * @param model Modelオブジェクト
     * @return 一覧画面へのパス
     */
    @GetMapping("/backPage")
    public String backPage(SearchForm searchForm, Model model){
        //現在ページ数を前ページに設定する
        searchForm.setCurrentPageNum(searchForm.getCurrentPageNum() - 1);
        return movePageInList(model, searchForm);
    }

    /**
     * 一覧画面で「次へ」リンク押下時に次ページを表示する
     * @param searchForm 検索用Formオブジェクト
     * @param model Modelオブジェクト
     * @return 一覧画面へのパス
     */
    @GetMapping("/nextPage")
    public String nextPage(SearchForm searchForm, Model model){
        //現在ページ数を次ページに設定する
        searchForm.setCurrentPageNum(searchForm.getCurrentPageNum() + 1);
        return movePageInList(model, searchForm);
    }

    /**
     * 一覧画面で「最後へ」リンク押下時に次ページを表示する
     * @param searchForm 検索用Formオブジェクト
     * @param model Modelオブジェクト
     * @return 一覧画面へのパス
     */
    @GetMapping("/lastPage")
    public String lastPage(SearchForm searchForm, Model model){
        //現在ページ数を最終ページに設定する
        searchForm.setCurrentPageNum(demoService.getAllPageNum(searchForm));
        return movePageInList(model, searchForm);
    }

    /**
     * 更新処理を行う画面に遷移する
     * @param id 更新対象のID
     * @param model Modelオブジェクト
     * @return 入力・更新画面へのパス
     */
    @GetMapping("/update")
    public String update(@RequestParam("id") String id, Model model){
        //更新対象のユーザーデータを取得
        DemoForm demoForm = demoService.findById(id);
        //ユーザーデータを更新
        model.addAttribute("demoForm", demoForm);
        return "input";
    }

    /**
     * 削除確認画面に遷移する
     * @param id 更新対象のID
     * @param model Modelオブジェクト
     * @return 削除確認画面へのパス
     */
    @GetMapping("/delete_confirm")
    public String delete_confirm(@RequestParam("id") String id, Model model){
        //削除対象のユーザーデータを取得
        DemoForm demoForm = demoService.findById(id);
        //ユーザーデータを更新
        model.addAttribute("demoForm", demoForm);
        return "confirm_delete";
    }

    /**
     * 削除処理を行う
     * @param demoForm 追加・更新用Formオブジェクト
     * @return 一覧画面の表示処理
     */
    @PostMapping(value = "/delete", params = "next")
    public String delete(DemoForm demoForm){
        //指定したユーザーデータを削除
        demoService.deleteById(demoForm.getId());
        //一覧画面に遷移
        return "redirect:/to_index";
    }

    /**
     * 削除完了後に一覧画面に戻る
     * @param searchForm 検索用Formオブジェクト
     * @param model Modelオブジェクト
     * @return 一覧画面
     */
    @GetMapping("/to_index")
    public String toIndex(SearchForm searchForm, Model model){
        //一覧画面に戻り、1ページ目のリストを表示する
        searchForm.setCurrentPageNum(1);
        return movePageInList(model, searchForm);
    }

    /**
     * 削除確認画面から一覧画面に戻る
     * @param model Modelオブジェクト
     * @param searchForm 検索用Formオブジェクト
     * @return 一覧画面
     */
    @PostMapping(value = "/delete", params = "back")
    public String confirmDeleteBack(Model model, SearchForm searchForm){
        //一覧画面に戻る
        return movePageInList(model, searchForm);
    }

    /**
     * 追加処理を行う画面に遷移する
     * @param model Modelオブジェクト
     * @return 入力・更新画面へのパス
     */
    @PostMapping(value = "/add", params = "next")
    public String add(Model model){
        model.addAttribute("demoForm", new DemoForm());
        return "input";
    }

    /**
     * 追加処理を行う画面から検索画面に戻る
     * @return 検索画面へのパス
     */
    @PostMapping(value = "/add", params = "back")
    public String addBack(){
        return "search";
    }

    /**
     * エラーチェックを行い、エラーが無ければ確認画面に遷移し、
     * エラーがあれば入力画面のままとする
     * @param demoForm 追加・更新用Formオブジェクト
     * @param result バインド結果
     * @return 確認画面または入力画面へのパス
     */
    @PostMapping(value = "/confirm", params = "next")
    public String confirm(@Validated DemoForm demoForm, BindingResult result){
        //追加・更新用Formオブジェクトのチェック処理でエラーがある場合は、
        //入力画面のままとする
        if(result.hasErrors()){
            return "input";
        }
        //エラーが無ければ確認画面に遷移する
        return "confirm";
    }

    /**
     * 一覧画面に戻る
     * @param model Modelオブジェクト
     * @param searchForm 検索用Formオブジェクト
     * @return 一覧画面の表示処理
     */
    @PostMapping(value = "/confirm", params = "back")
    public String confirmBack(Model model, SearchForm searchForm){
        //一覧画面に戻る
        return movePageInList(model, searchForm);
    }

    /**
     * 完了画面に遷移する
     * @param demoForm 追加・更新用Formオブジェクト
     * @param result バインド結果
     * @return 完了画面
     */
    @PostMapping(value = "/send", params = "next")
    public String send(DemoForm demoForm, BindingResult result){
        //チェック処理を行い、エラーがなければ、更新・追加処理を行う
        if(result.hasErrors()){
            return "input";
        }
        demoService.createOrUpdate(demoForm);
        return "redirect:/complete";
    }

    /**
     * 完了画面に遷移する
     * @param sessionStatus セッションステータス
     * @return 完了画面
     */
    @GetMapping("/complete")
    public String complete(SessionStatus sessionStatus){
        //セッションオブジェクトを破棄
        sessionStatus.setComplete();
        return "complete";
    }

    /**
     * 入力画面に戻る
     * @return 入力画面
     */
    @PostMapping(value = "/send", params = "back")
    public String sendBack(){
        return "input";
    }

    /**
     * 一覧画面に戻り、指定した現在ページのリストを表示する
     * @param model Modelオブジェクト
     * @param searchForm 検索用Formオブジェクト
     * @return 一覧画面の表示処理
     */
    private String movePageInList(Model model, SearchForm searchForm){
        //現在ページ数, 総ページ数を設定
        model.addAttribute("currentPageNum", searchForm.getCurrentPageNum());
        model.addAttribute("allPageNum", demoService.getAllPageNum(searchForm));
        //ページング用オブジェクトを生成し、現在ページのユーザーデータリストを取得
        Pageable pageable = demoService.getPageable(searchForm.getCurrentPageNum());
        List<DemoForm> demoFormList = demoService.demoFormList(searchForm, pageable);
        //ユーザーデータリストを更新
        model.addAttribute("demoFormList", demoFormList);
        return "list";
    }
}

その他のソースコード内容は、以下のサイトを参照のこと。
https://github.com/purin-it/java/tree/master/spring-boot-security-db/demo

サンプルプログラムの実行結果

サンプルプログラムの実行結果は、以下の通り。

1) Spring Bootアプリケーションを起動し、「http:// (ホスト名):(ポート番号)」とアクセスすると、以下のログイン画面が表示される
サンプルプログラムの実行_1

2) このタイミングで、user_passテーブルを確認すると、名前が「user」で、パスワードが暗号化された値(passを暗号化している)で登録されていることが確認できる
サンプルプログラムの実行_2

3) データベースに登録したユーザーと違うユーザー名またはパスワードを入力し、「ログイン」ボタンを押下
サンプルプログラムの実行_3

4) 以下のように、ログインができず、ログイン画面にエラーメッセージが表示される
サンプルプログラムの実行_4

5) データベースに登録したユーザーと同じユーザー名・パスワードを入力し、「ログイン」ボタンを押下
サンプルプログラムの実行_5

6) 以下のように、ログインでき、検索画面が表示されることが確認できるので、「ログアウト」ボタンを押下
サンプルプログラムの実行_6

7) 以下のように、ログイン画面に遷移することが確認できる
サンプルプログラムの実行_7

要点まとめ

  • WebSecurityConfigurerAdapterクラスを継承したクラス内のconfigure(AuthenticationManagerBuilder auth)メソッドで、userDetailsServiceとpasswordEncoderを指定することで、データベースを利用したSpring-Security用の認証処理を行える。
  • Spring-Security用のユーザーアカウント情報を利用して認証するサービスクラスは、UserDetailsServiceインタフェースを実装して作成する。
  • Spring-Security用のユーザーアカウントは、UserDetailsインタフェースを実装して作成する。