UserDetails と UserDetailsService:ユーザー情報を扱うコンポーネントを理解しよう
Spring Security は、認証機能の主要な処理を実行するクラスを提供しています。 そのため、各アプリケーションに認証機能を実装する際には、アプリケーション固有の処理が必要な部分だけをカスタマイズします。
UserDetails と UserDetailsService は、Spring Security の挙動をカスタマイズする際に登場するインターフェースです。
このレクチャーでは、これらのインターフェースがどのような役割を持つか、また、どのように実装クラスを作成するかを解説します。
目次
UserDetails とは?
UserDetails はユーザー情報を保持するインターフェースです。
Spring Security が提供しています。
このインターフェースには、以下のようなメソッドが定義されています
public interface UserDetails extends Serializable {
Collection<? extends GrantedAuthority> getAuthorities();
String getPassword();
String getUsername();
boolean isAccountNonExpired();
boolean isAccountNonLocked();
boolean isCredentialsNonExpired();
boolean isEnabled();
}
これらのメソッドは、以下のようなユーザーの情報を取得するためのものです:
getAuthorities():権限を返しますgetPassword():パスワードを返しますgetUsername():ユーザー名を返しますisAccountNonExpired():アカウントの有効期限が切れていないかを返しますisAccountNonLocked():アカウントがロックされていないかを返しますisCredentialsNonExpired():アカウントの認証情報の有効期限が切れていないかを返しますisEnabled():アカウントが有効かを返します
Spring Security に対して、ユーザー情報を提供する際には、このインターフェースを実装したクラスを作成します。
UserDetailsService とは?
UserDetailsService は、UserDetails オブジェクトを取得するためのインターフェースです。
以下のように定義されています:
public interface UserDetailsService {
UserDetails loadUserByUsername(String username) throws UsernameNotFoundException;
}
UserDetailsService インターフェースには、loadUserByUsername メソッドだけが定義されています。
loadUserByUsername は、ユーザー名を引数に取り、UserDetails オブジェクトを返します。
loadUserByUsername メソッドの内部では、ユーザー情報が保存されているデータベースやファイルなどから、ユーザー情報を取得する処理を記述します。
どこからユーザー情報を取得するかは、アプリケーションによって自由に決めることができます。
UserDetailsService インターフェースの実装
UserDetailsService はインターフェースです。
アプリケーションで処理を実現するには、このインターフェースを implements した実装クラスを作成する必要があります。
次の UserService クラスは UserDetailsService インターフェースの実装クラスの例です:
@Service
@RequiredArgsConstructor
public class UserService implements UserDetailsService {
private final UserRepository userRepository;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
return userRepository.findByUsername(username)
.map(u -> User.builder()
.username(u.username())
.password(u.password())
.build()
)
.orElseThrow(() -> new UsernameNotFoundException(username + " not found"));
}
}
このクラスは、指定されたユーザー名に基づいてユーザー情報をデータベースから取得し、UserDetailsオブジェクトを作成します。
データベースへのアクセスは、UserRepository クラスに委譲しています。
User.builder() は、UserDetails インターフェースを実装した User クラスのビルダーです。
User クラスは Spring Security が提供しているクラスです。
複雑なカスタマイズする必要がなければ、独自のクラスを定義せずこのクラスを使うこともできます。
UserDetailsService の実装クラスを Spring Security と統合する方法
UserDetailsService クラスの実装クラスを作成したら、Spring Security に統合することで、アプリケーション固有のユーザー認証ロジックを実現できます。
以下のようなコードを SecurityConfig クラスに追加することで、UserDetailsService の実装クラスを統合できます:
@Configuration
@EnableWebSecurity
@RequiredArgsConstructor
public class SecurityConfig {
private final UserDetailsService userDetailsService;
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
// ...
.userDetailsService(userDetailsService)
// ...
;
return http.build();
}
上記のコードでは、UserDetailsService を DI によって取得し、HttpSecurity#userDetailsService メソッドに渡しています。
まとめ:UserDetails と UserDetailsService の役割と実装方法
今回は UserDetails と UserDetailsService の役割と実装方法を学びました。
UserDetailsはユーザー情報を保持するためのインターフェースですUserDetailsServiceはユーザー情報を取得するためのインターフェースですUserDetailsServiceインターフェースを implements するクラスを作成し、Spring Security に統合することで、アプリケーション固有のユーザー認証ロジックを実装できます