はじめに
最近、Spring Securityでのログイン機能の実装方法に関して詳しく取り組んでいます。その中で得られた知見を元に、この記事では具体的な実装手順を省いて、Spring Securityを用いた実装の背後にあるコンセプトやイメージに焦点を当てて解説します。
具体的な実装方法については以下に記事を作成しました。参考にしてみてください。
- 開発環境の構築とログイン画面の実装について
- 入力されたユーザ名とパスワードを固定値の情報と照合する方法
- 入力情報をデータベースのユーザ情報と照合する方法
- ログアウト機能の実装手法
- ユーザ登録機能の実装について
※以降は現在作成中です。
Bean登録について
Bean登録は、アプリケーションの起動時に以下のプロセスを行います。
- クラスに関するBean登録:
@Component
,@Service
,@Repository
などのアノテーションを持つクラスがスキャンされ、Beanとしてインスタンス化されます。 - メソッドに関するBean登録:
@Bean
が付与されたメソッドが実行され、その結果として得られたオブジェクトがBeanとして登録されます。このメソッドは、@Configuration
が付与されたクラス内で有効です。
Bean登録を行う場合は以下のようなアノテーションをクラスやメソッドに付与します。
-
@Bean
:メソッドに付与。@Configuration
クラス内で有効。 -
@Component
:クラスに一般的に付与。 -
@Service
:ビジネスロジックを持つサービスクラスに付与。 -
@Configuration
:Bean定義を含む設定クラスに付与。 -
@Repository
:データベース操作を行うクラスに付与。
例えば以下のクラスの場合は、@Configuration
が付与されているので PasswordEncoderConfig
のインスタンスの生成を行い、@Bean
が付与されているので passwordEncoder
が実行されます。つまり、PasswordEncoder(BCryptPasswordEncoder)
のインスタンスも生成されます。
@Configuration
public class PasswordEncoderConfig {
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
認証・認可を実装するイメージについて
Spring Securityはアプリケーションを不正アクセスや操作から保護するフレームワークです。この保護の仕組みを物理的なセキュリティにたとえると、「建物を囲む堅固な壁」や、「入口で訪問者の身分を確認する警備員」のような役割を果たします。Spring Securityの設定を行うことは、壁の材質や強度を選び、警備員に何を確認させるかの指示を出すといったことを決めることです。そしてアプリケーションが起動すると、これらの設定に基づいた壁の作成や警備員の配置を行い即座に活動を開始します。
イメージを具体化する
前の章で、Spring Securityの設定のイメージを物理的なセキュリティの「壁」と「警備員」に例えました。ここでは、その比喩を基に、実際のコードの中でどのようにそれが具体化されているかを見ていきましょう。
以下は、Spring Securityの設定を示すクラスの一例です。
@Configuration
@EnableWebSecurity
public class SecurityConfig {
private final CustomAuthenticationProvider customAuthenticationProvider;
public SecurityConfig(
CustomAuthenticationProvider customAuthenticationProvider) {
this.customAuthenticationProvider = customAuthenticationProvider;
}
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.authenticationProvider(customAuthenticationProvider)
.cors(customizer -> customizer.configurationSource(corsConfigurationSource()))
.csrf(csrf -> csrf.disable())
.authorizeHttpRequests(authorizeRequests -> authorizeRequests
.requestMatchers("/login", "/signup").permitAll()
.anyRequest().authenticated())
.formLogin(formLogin -> formLogin
.loginProcessingUrl("/login")
.loginPage("http://127.0.0.1:5500/front/login.html")
.defaultSuccessUrl("http://127.0.0.1:5500/front/index.html")
.failureUrl("http://127.0.0.1:5500/front/error.html"))
.logout(logout -> logout
.logoutUrl("/logout")
.logoutSuccessUrl("http://127.0.0.1:5500/front/login.html"));
return http.build();
}
@Bean
public UrlBasedCorsConfigurationSource corsConfigurationSource() {
CorsConfiguration configuration = new CorsConfiguration();
configuration.setAllowCredentials(true);
configuration.addAllowedOrigin("http://127.0.0.1:5500");
configuration.addAllowedHeader("*");
configuration.addAllowedMethod("*");
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", configuration);
return source;
}
}
このクラスでは、特に SecurityFilterChain
の型を持つメソッドが、認証・認可の設定の中心となります。また、このクラスには @Configuration
や @Bean
のアノテーションが付与されているため、アプリケーションの起動時にこれらの設定が実行されます。これは、前の章で言及した「壁や警備員の作成や配置」に相当します。
-
壁としての設定
- authenticationProvider()
- cors()
- csrf()
- authorizeHttpRequests()
- formLogin()
- logout()
-
警備員が確認する内容
- customAuthenticationProvider
- customizer -> customizer.configurationSource(corsConfigurationSource())`
- csrf -> csrf.disable()
- csrf -> csrf.disable()
- authorizeRequests -> authorizeRequests.~
- formLogin -> formLogin.~
- logout -> logout.~
これらの設定を行うことで、Spring Securityによるアプリケーションの保護が実現されます。
Spring Securityでよく使用されるクラスとその役割
-
AuthenticationProvider
役割: 認証を行う際の主要なインターフェイス。
具体的な機能: 与えられた認証情報(通常はユーザー名とパスワード)に基づいてユーザーを認証する役割。認証が成功すると、完全に構築されたAuthentication
オブジェクトを返す。
対応するイメージ: 訪問者の身分を確認する警備員 -
UserDetailsService
役割: ユーザー名に基づいてUserDetails
オブジェクトをロードするためのインターフェイス。
具体的な機能: 与えられたユーザー名でユーザーの詳細情報を取得。UserDetails
インターフェイスの実装オブジェクトを返す。
対応するイメージ: 警備員の手元の名簿やデータベース -
UserDetails
役割: Spring Securityにおけるユーザーの詳細情報を提供するインターフェイス。
具体的な機能: ユーザー名、パスワード、権限、アカウントの有効性などのユーザー関連の情報を提供。
対応するイメージ: 訪問者のIDカードやパスポート -
PasswordEncoder
役割: パスワードのエンコードとマッチングを行うためのインターフェイス。
具体的な機能: 生のパスワードをエンコードしたり、エンコード済みのパスワードと生のパスワードを比較したりする。一般的には、ハッシュ関数を使用してパスワードを安全に保存。
対応するイメージ: 鍵と錠前 -
Authentication
役割: 認証情報を保持する主要なインターフェイス。
具体的な機能: 認証時の原始的な情報や、認証後に取得されたPrincipal(通常はUserDetails
)、権限(GrantedAuthority
)などを保持。
対応するイメージ: 訪問者の身分証明やバッジ -
GrantedAuthority
役割: 権限を表現するインターフェイス。
具体的な機能: ユーザーに与えられる権限やロールを表現。例: "ROLE_USER", "ROLE_ADMIN" など。
対応するイメージ: 警備員が参照する訪問者の許可ラベルやアクセスレベル -
SecurityContextHolder
役割: 現在のセキュリティコンテキストを保持するためのクラス。
具体的な機能: 現在のAuthentication
オブジェクト(現在のユーザー)を保持するスレッドローカル変数を提供。
対応するイメージ: 警備員のメモ帳や現在の訪問者リスト -
AccessDecisionManager
役割: 認可判断を行うインターフェイス。
具体的な機能: 与えられたAuthentication
と目的のオブジェクトに対するアクセスを許可または拒否する決定を下す。
対応するイメージ: 訪問者が指定されたエリアへのアクセスを許可されているかをチェックする警備員 -
FilterSecurityInterceptor
役割: Spring Securityのフィルタチェーンの一部として、HTTPリクエストのセキュリティを評価するクラス。
具体的な機能: 認可の判断をAccessDecisionManager
に委譲し、リクエストが安全であるかどうかを判断する。
対応するイメージ: 建物の入口に立つ、訪問者のアクセスを制御する警備員