テーブル認証

データベース上のユーザー情報を使用して認証処理を行う方法について説明します。

認証処理の実装には Spring Security を使用します。

以下のサンプルコードの動作確認環境については、 動作確認環境と依存ライブラリについて を参照してください。

Tip

このサンプルでは、データベースへのアクセスに Doma2 を使用しています。 Doma2を使用することで、SQLを外部ファイルで管理できるなどのメリットがあります。

認証で使用するテーブルの準備

データベースのテーブルを使って認証処理を行うため、ユーザ情報(ユーザ名やパスワード)を保持するテーブルを事前に作成します。 この例では、認証処理に最低限必要となるユーザ名とパスワードのみを保持しています。

create table users (
  username varchar(255) not null,
  password varchar(60) not null,
  primary key (username)
);

create table user_role (
  username varchar(255) not null,
  role     varchar(255) not null,
  primary key (username, role)
);

実装例

Spring Securityの Form Login および Logout を参考に、作成したログインページを表示するように設定します。

Spring Securityに対する設定
@Configuration
public class SecurityConfig {

    // role-start
    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        return http
                .authorizeHttpRequests(authorize -> authorize
                        .requestMatchers("/admin/**").hasRole("admin")
                        .requestMatchers("/user/**").hasRole("user")
                        .anyRequest().authenticated())
                .formLogin(form -> form
                        .loginPage("/login")
                        .usernameParameter("username")
                        .passwordParameter("password")
                        .defaultSuccessUrl("/top", true)
                        .permitAll())
                .logout(logout -> logout
                        .logoutSuccessUrl("/login?logout")
                        .invalidateHttpSession(true)
                        .permitAll())
                .headers(configurer -> configurer
                        .referrerPolicy(referrer -> referrer
                                .policy(ReferrerPolicyHeaderWriter.ReferrerPolicy.STRICT_ORIGIN_WHEN_CROSS_ORIGIN)))
                .build();
    }

    @Bean
    public RoleHierarchy roleHierarchy() {
        // 権限の階層構造の設定をします。
        // admin権限は、user権限を含む権限となります。
        RoleHierarchyImpl hierarchy = new RoleHierarchyImpl();
        hierarchy.setHierarchy("ROLE_admin > ROLE_user");
        return hierarchy;
    }
    // role-end

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }
}

ユーザ情報を取得するため、Doma2を使用してユーザ情報を取得するためのDaoとServiceを作成します。 ServiceではSpring Securityが提供している UserDetailService を実装し、Bean定義することで、Spring Securityから呼び出されます。

Dao
@Dao
@ConfigAutowireable
public interface UserDao {

    @Select
    Optional<UserEntity> loadUserByUserName(String username);

    @Select
    List<String> loadUserRoles(String username);

}
SQL
select /*%expand*/*
from users
where username = /*username*/'username'
Service
// UserDetailsServiceを実装して、ユーザ名に紐づく情報を取得するloadUserByUsernameメソッドを実装します。
@Service
public class UserService implements UserDetailsService {

    private final UserDao userDao;

    public UserService(UserDao userDao) {
        this.userDao = userDao;
    }

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        // Daoを使用してユーザ情報を取得します。
        // ユーザ情報が存在しない場合には、UsernameNotFoundExceptionを送出し
        // Spring Security側での認証エラーの処理が行われるようにします。
        return userDao.loadUserByUserName(username)
                      // ユーザ情報には、ログインユーザに割り当てられた権限(ロール)も設定します。
                      .map(e -> new User(e.getUsername(), e.getPassword(), loadAuthorities(e.getUsername())))
                      .orElseThrow(() -> new UsernameNotFoundException("user not found."));
    }

    /**
     * ユーザ名に紐づく権限リストを取得します。
     *
     * @param username ユーザ名
     * @return ユーザ名に紐づく権限リスト(存在しない場合は空のリスト)
     */
    private List<GrantedAuthority> loadAuthorities(String username) {
        return userDao.loadUserRoles(username)
                      .stream()
                      .map(SimpleGrantedAuthority::new)
                      .collect(toList());
    }
}

サンプル全体は table-authentication-sample を参照してください。