今回の目標
前回はインメモリにユーザー情報を登録して認証する方法を説明しました。またユーザーの権限によるページのアクセス制限の設定方法と、認証したユーザー情報の取得方法についても説明しました。

今回はインメモリではなくDBからユーザー情報を取得して認証する方法について説明していきます。
ユーザー情報の検索
最初におさらいですが、インメモリにユーザー情報を設定するには、設定クラスの以下のcofigureメソッドをオーバーライドしました。
@Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { PasswordEncoder encoder = PasswordEncoderFactories.createDelegatingPasswordEncoder(); auth.inMemoryAuthentication() .withUser("user").password(encoder.encode("password")).roles("USER") .and() .withUser("admin").password(encoder.encode("adminpassword")).roles("ADMIN"); }
インメモリに登録さたユーザー情報を検索し、該当のユーザーが存在すれば認証されたことになり、セッションにユーザー情報が登録されます。
では、ユーザー情報は何によって検索されているのでしょうか?
UserDetailsService
configureメソッドの引数であるAuthenticationManagerBuilderのソースを確認してみます。
public class AuthenticationManagerBuilder extends AbstractConfiguredSecurityBuilder<AuthenticationManager, AuthenticationManagerBuilder> implements ProviderManagerBuilder<AuthenticationManagerBuilder> { private final Log logger = LogFactory.getLog(getClass()); private AuthenticationManager parentAuthenticationManager; private List<AuthenticationProvider> authenticationProviders = new ArrayList<>(); private UserDetailsService defaultUserDetailsService; private Boolean eraseCredentials; private AuthenticationEventPublisher eventPublisher; //以下省略 }
実はこのフィールドの中にユーザー情報を検索する役割を担っているものがあります。それがUserDetailsServiceです。UserDetailsServiceはインターフェースなので、実際には実装クラスのインスタンスが保持されます。
インメモリの場合はInMemoryUserDetailsManagerというクラスで実装されています。
UserDetailsServiceのソースを確認すると、宣言されているのはUserDetailsを返すloadUserByUsernameメソッドだけです。ここにユーザーの検索処理を実装します。
public interface UserDetailsService { UserDetails loadUserByUsername(String username) throws UsernameNotFoundException; }
では実装クラスとしてUserDetailsServiceImplを作成します。DB認証の前に、オンコードで擬似的に検索処理を実装してみます。
public class UserDetailsServiceImpl implements UserDetailsService { @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { //adminだけ認証する if (username == null || !"admin".equals(username)) { throw new UsernameNotFoundException("Not found username : " + username); } //パスワードの設定 PasswordEncoder encoder = PasswordEncoderFactories.createDelegatingPasswordEncoder(); String password = encoder.encode("adminpassword"); //権限の設定 Collection<GrantedAuthority> authorities = new ArrayList<>(); authorities.add(new SimpleGrantedAuthority("ROLE_ADMIN")); //ユーザー情報を作成 User user = new User(username, password, authorities); return user; } }
ここではadminというユーザーのみ存在するものとします。ユーザーが存在しない場合は、UsernameNotFoundExceptionの例外をスローします。
ユーザー情報は前回の記事でも紹介した、Spring Securityで実装されているUserクラスを用います。いくつかフィールドがありますが、最低限ユーザー名、パスワード、権限があればインスタンスが作成できます。
これでUserDetailsServiceが実装できました。あとは以下のように設定ファイルでUserDetailsServiceを設定するだけです。
@Autowired UserDetailsServiceImpl service; @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth.userDetailsService(service); }
実際に認証できることを確認してみてください。
DB認証
この記事ではJava Data JPAを用いた方法を説明します。Java Data JPA自体の説明はこの記事ではしません。
以下のAccountテーブルにユーザー情報が登録されているとします。内容としては、先程オンコードで設定したユーザー情報がそのまま登録されているイメージです。つまりパスワードはハッシュ化された値が登録されています。
CREATE TABLE account ( username varchar NOT NULL, "password" varchar NOT NULL, "role" varchar NOT NULL, CONSTRAINT account_pk PRIMARY KEY (username) );
このテーブルに対応するEntityクラスを作成します。
@Entity public class Account { @Id private String username; private String password; private String role; public Account() {} //Setter、Getterは省略 }
そしてRepositoryインターフェースを作成するのですが、今回はusernameがキーなので検索用のメソッドを宣言しておきます。
@Repository public interface AccountRepository extends JpaRepository<Account, String> { Account findByUsername(String username); }
あとはUserServiceにRepositoryを使ったユーザー検索を実装するだけです。
public class UserDetailsServiceImpl implements UserDetailsService { private AccountRepository repository; @Autowired public UserDetailsServiceImpl(AccountRepository repository) { this.repository = repository; } @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { if (StringUtils.isEmpty(username)) { throw new UsernameNotFoundException("username is empty"); } Account account = repository.findByUsername(username); if (account == null) { throw new UsernameNotFoundException("Not found username : " + username); } Collection<GrantedAuthority> authorities = new ArrayList<>(); authorities.add(new SimpleGrantedAuthority(account.getRole())); User user = new User(account.getUsername(), account.getPassword(), authorities); return user; } }
あとがき
今回はDB認証について説明しました。UserDetailsServiceにユーザーの検索処理を実装することでDB認証が可能になります。
今回使用したテーブルはかなりシンプルなものでしたが、実際にはもう少し多くのフィールドを持っていると思います。このような場合にどうすればよいのかを次回の記事にしたいと思います。
- Spring Bootのおすすめ書籍はコチラ -
コメント