このバージョンはまだ開発中であり、まだ安定しているとは見なされていません。最新の安定バージョンについては、Spring Security 6.5.6 を使用してください! |
サーブレット認証アーキテクチャ
この説明では、サーブレットのセキュリティ: 全体像を拡張して、Spring Security がサーブレット認証で使用する主要なアーキテクチャコンポーネントについて説明します。これらの要素がどのように組み合わされるかを説明する具体的なフローが必要な場合は、認証メカニズム固有のセクションを参照してください。
SecurityContextHolder -
SecurityContextHolderは、Spring Security が認証されたユーザーの詳細を格納する場所です。SecurityContext -
SecurityContextHolderから取得され、現在認証されているユーザーのAuthenticationが含まれています。認証 -
AuthenticationManagerへの入力として、ユーザーが認証のために提供した資格情報、またはSecurityContextからの現在のユーザーを提供できます。GrantedAuthority -
Authenticationのプリンシパルに付与される権限 (すなわち、ロール、スコープなど。)AuthenticationManager - Spring Security のフィルターが認証を実行する方法を定義する API。
ProviderManager -
AuthenticationManagerの最も一般的な実装。AuthenticationProvider -
ProviderManagerが特定の型の認証を実行するために使用します。AuthenticationEntryPointを使用した資格情報のリクエスト - クライアントから資格情報をリクエストするために使用 (つまり、ログインページへのリダイレクト、WWW-Authenticateレスポンスの送信など。)AbstractAuthenticationProcessingFilter - 認証に使用されるベース
Filter。また、これにより、認証の高レベルのフローと各部分がどのように連携するかについての良いアイデアが得られます。
SecurityContextHolder
Spring Security の認証モデルの中核は SecurityContextHolder です。SecurityContext が含まれています。

SecurityContextHolder は、Spring Security が認証されたユーザーの詳細を格納する場所です。Spring Security は、SecurityContextHolder がどのように設定されているかを気にしません。値が含まれている場合は、現在認証されているユーザーとして使用されます。
ユーザーが認証されていることを示す最も簡単な方法は、SecurityContextHolder を直接設定することです。
SecurityContextHolder の設定 Java
Kotlin
SecurityContext context = SecurityContextHolder.createEmptyContext(); (1)
Authentication authentication =
new TestingAuthenticationToken("username", "password", "ROLE_USER"); (2)
context.setAuthentication(authentication);
SecurityContextHolder.setContext(context); (3)
val context: SecurityContext = SecurityContextHolder.createEmptyContext() (1)
val authentication: Authentication = TestingAuthenticationToken("username", "password", "ROLE_USER") (2)
context.authentication = authentication
SecurityContextHolder.setContext(context) (3)
| 1 | 空の SecurityContext を作成することから始めます。複数のスレッド間での競合状態を回避するには、SecurityContextHolder.getContext().setAuthentication(authentication) を使用する代わりに、新しい SecurityContext インスタンスを作成する必要があります。 |
| 2 | 次に、新しい Authentication オブジェクトを作成します。Spring Security は、SecurityContext にどの型の Authentication 実装が設定されているかを気にしません。ここでは、非常に単純な TestingAuthenticationToken を使用します。より一般的な本番シナリオは UsernamePasswordAuthenticationToken(userDetails, password, authorities) です。 |
| 3 | 最後に、SecurityContextHolder に SecurityContext を設定します。Spring Security は、この情報を認証に使用します。 |
認証されたプリンシパルに関する情報を取得するには、SecurityContextHolder にアクセスします。
Java
Kotlin
SecurityContext context = SecurityContextHolder.getContext();
Authentication authentication = context.getAuthentication();
String username = authentication.getName();
Object principal = authentication.getPrincipal();
Collection<? extends GrantedAuthority> authorities = authentication.getAuthorities();
val context = SecurityContextHolder.getContext()
val authentication = context.authentication
val username = authentication.name
val principal = authentication.principal
val authorities = authentication.authorities
デフォルトでは、SecurityContextHolder は ThreadLocal を使用してこれらの詳細を格納します。つまり、SecurityContext が引数としてこれらのメソッドに明示的に渡されない場合でも、SecurityContext は常に同じスレッド内のメソッドで使用できます。現在のプリンシパルのリクエストが処理された後にスレッドをクリアするように注意すれば、このように ThreadLocal を使用することは非常に安全です。Spring Security の FilterChainProxy は、SecurityContext が常にクリアされることを保証します。
一部のアプリケーションは、スレッドを操作する特定の方法のため、ThreadLocal の使用に完全には適していません。例: Swing クライアントは、Java 仮想マシンのすべてのスレッドが同じセキュリティコンテキストを使用することを望んでいる場合があります。起動時の戦略を使用して SecurityContextHolder を構成し、コンテキストの保存方法を指定できます。スタンドアロンアプリケーションの場合は、SecurityContextHolder.MODE_GLOBAL 戦略を使用します。他のアプリケーションでも、セキュアスレッドによって生成されたスレッドに同じセキュリティ ID を想定させたい場合があります。これは、SecurityContextHolder.MODE_INHERITABLETHREADLOCAL を使用して実現できます。モードは、デフォルトの SecurityContextHolder.MODE_THREADLOCAL から 2 つの方法で変更できます。1 つは、システムプロパティを設定することです。2 つ目は、SecurityContextHolder で静的メソッドを呼び出すことです。ほとんどのアプリケーションは、デフォルトから変更する必要はありません。ただし、そうする場合は、SecurityContextHolder の JavaDoc を参照して詳細を確認してください。
SecurityContext
SecurityContext (Javadoc) は SecurityContextHolder から取得されます。SecurityContext には認証オブジェクトが含まれています。
認証
Authentication (Javadoc) インターフェースは、Spring Security 内で 2 つの主な目的を果たします。
ユーザーが認証のために提供した資格情報を提供する
AuthenticationManagerへの入力。このシナリオで使用すると、isAuthenticated()はfalseを返します。現在認証されているユーザーを表します。現在の
Authenticationは SecurityContext から取得できます。
Authentication には以下が含まれます。
principal: ユーザーを識別します。ユーザー名 / パスワードで認証する場合、これは多くの場合UserDetailsのインスタンスです。credentials: 多くの場合、パスワード。多くの場合、これはユーザーが認証された後にクリアされ、リークされないようにします。authorities:GrantedAuthorityインスタンスは、ユーザーに付与される高レベルの権限です。2 つの例は、ロールとスコープです。
また、既存の Authentication インスタンスを変更し、別のインスタンスと統合できる AdditionalRequiredFactorsBuilder も搭載されています。これは、フォームログインなどの認証手順から権限を取得し、ワンタイムトークンログインなどの別の認証手順に適用するといったシナリオで役立ちます。
Java
Kotlin
Authentication lastestResult = authenticationManager.authenticate(authenticationRequest);
Authentication previousResult = SecurityContextHolder.getContext().getAuthentication();
if (previousResult != null && previousResult.isAuthenticated()) {
lastestResult = lastestResult.toBuilder()
.authorities((a) -> a.addAll(previous.getAuthorities()))
.build();
}var latestResult: Authentication = authenticationManager.authenticate(authenticationRequest)
val previousResult = SecurityContextHolder.getContext().authentication;
if (previousResult?.isAuthenticated == true) {
latestResult = latestResult.toBuilder().authorities { a ->
a.addAll(previousResult.authorities)
}.build()
}GrantedAuthority
GrantedAuthority (Javadoc) インスタンスは、ユーザーに付与される高レベルの権限です。2 つの例は、ロールとスコープです。
Authentication.getAuthorities() メソッドから GrantedAuthority インスタンスを取得できます。このメソッドは、GrantedAuthority オブジェクトの Collection を提供します。GrantedAuthority は、当然のことながら、プリンシパルに付与される権限です。このような権限は通常、ROLE_ADMINISTRATOR や ROLE_HR_SUPERVISOR などの「ロール」です。これらのロールは、後で Web 認可、メソッド認可、ドメインオブジェクト認可用に構成されます。Spring Security の他の部分は、これらの権限を解釈し、それらが存在することを期待します。ユーザー名 / パスワードベースの認証を使用する場合、GrantedAuthority インスタンスは通常 UserDetailsService によってロードされます。
通常、GrantedAuthority オブジェクトはアプリケーション全体の権限です。これらは、特定のドメインオブジェクトに固有のものではありません。Employee オブジェクト番号 54 へのアクセス許可を表す GrantedAuthority がない可能性があります。これは、そのような権限が数千ある場合、メモリがすぐに不足する(または、少なくともアプリケーションに長い時間がかかる)ためです。ユーザーを認証するため)。もちろん、Spring Security はこの一般的な要件を処理するように明示的に設計されていますが、代わりに、この目的のためにプロジェクトのドメインオブジェクトセキュリティ機能を使用する必要があります。
AuthenticationManager
AuthenticationManager は、Spring Security のフィルターが認証を実行する方法を定義する API です。返された Authentication は、AuthenticationManager を呼び出したコントローラー(つまり、Spring Security の Filters インスタンス)によって SecurityContextHolder に設定されます。Spring Security の Filters インスタンスと統合していない場合は、SecurityContextHolder を直接設定でき、AuthenticationManager を使用する必要はありません。
AuthenticationManager の実装は何でもかまいませんが、最も一般的な実装は ProviderManager です。
ProviderManager
ProviderManager (Javadoc) は、AuthenticationManager の最も一般的に使用される実装です。ProviderManager は、AuthenticationProvider インスタンスの List に委譲します。各 AuthenticationProvider には、認証が成功、失敗、決定できないことを示し、ダウンストリーム AuthenticationProvider が決定できるようにする機会があります。構成された AuthenticationProvider インスタンスのいずれも認証できない場合、認証は ProviderNotFoundException で失敗します。これは、ProviderManager が渡された Authentication の型をサポートするように構成されていないことを示す特別な AuthenticationException です。

実際には、各 AuthenticationProvider は、特定の型の認証を実行する方法を知っています。例: 1 つの AuthenticationProvider がユーザー名 / パスワードを検証できる可能性があり、別の AuthenticationProvider が SAML アサーションを認証できる可能性があります。これにより、各 AuthenticationProvider は、複数の型の認証をサポートしながら、非常に特定の型の認証を実行し、単一の AuthenticationManager Bean のみを公開できます。
ProviderManager では、オプションの親 AuthenticationManager を構成することもできます。これは、AuthenticationProvider が認証を実行できない場合に参照されます。親は任意の型の AuthenticationManager にすることができますが、多くの場合、ProviderManager のインスタンスです。

実際、複数の ProviderManager インスタンスが同じ親 AuthenticationManager を共有する場合があります。これは、認証が共通する複数の SecurityFilterChain インスタンス(共有親 AuthenticationManager)が存在するシナリオでは多少一般的ですが、異なる認証メカニズム(異なる ProviderManager インスタンス)もあります。

デフォルトでは、ProviderManager は、成功した認証リクエストによって返される Authentication オブジェクトから機密の資格情報をクリアしようとします。これにより、パスワードなどの情報が HttpSession で必要以上に長く保持されるのを防ぎます。
|
これにより、たとえば、ステートレスアプリケーションのパフォーマンスを向上させるために、ユーザーオブジェクトのキャッシュを使用するときに問題が発生する可能性があります。Authentication にキャッシュ内のオブジェクト(UserDetails インスタンスなど)への参照が含まれていて、その資格情報が削除されている場合、キャッシュされた値に対して認証することはできなくなります。キャッシュを使用する場合は、これを考慮する必要があります。明らかな解決策は、最初に、キャッシュ実装または返された Authentication オブジェクトを作成する AuthenticationProvider のいずれかで、オブジェクトのコピーを作成することです。または、ProviderManager の eraseCredentialsAfterAuthentication プロパティを無効にすることもできます。ProviderManager (Javadoc) クラスについては Javadoc を参照してください。
AuthenticationProvider
複数の AuthenticationProvider (Javadoc) インスタンスを ProviderManager に注入できます。各 AuthenticationProvider は、特定の型の認証を実行します。例: DaoAuthenticationProvider はユーザー名 / パスワードベースの認証をサポートし、JwtAuthenticationProvider は JWT トークンの認証をサポートします。
AuthenticationEntryPoint を使用した資格情報のリクエスト
AuthenticationEntryPoint (Javadoc) は、クライアントから資格情報をリクエストする HTTP レスポンスを送信するために使用されます。
場合によっては、クライアントがリソースをリクエストするための資格情報(ユーザー名やパスワードなど)を事前に含めることがあります。このような場合、Spring Security は、クライアントに資格情報をリクエストする HTTP レスポンスを提供する必要はありません。これは、それらがすでに含まれているためです。
AbstractAuthenticationProcessingFilter
AbstractAuthenticationProcessingFilter (Javadoc) は、ユーザーの資格情報を認証するためのベース Filter として使用されます。資格情報を認証する前に、Spring Security は通常 AuthenticationEntryPoint を使用して資格情報をリクエストします。
次に、AbstractAuthenticationProcessingFilter は送信された認証リクエストを認証できます。

ユーザーが資格情報を送信すると、AbstractAuthenticationProcessingFilter は認証される HttpServletRequest から Authentication を作成します。作成される Authentication の型は、AbstractAuthenticationProcessingFilter のサブクラスによって異なります。例: UsernamePasswordAuthenticationFilter は、HttpServletRequest で送信されたユーザー名とパスワードから UsernamePasswordAuthenticationToken を作成します。
次に、Authentication が AuthenticationManager に渡されて認証されます。
認証が失敗した場合、Failure。
SecurityContextHolder はクリアされます。
RememberMeServices.loginFailが呼び出されます。remember me が設定されていない場合、これは何も行いません。rememberme パッケージを参照してください。AuthenticationFailureHandlerが呼び出されます。AuthenticationFailureHandler(Javadoc) インターフェースを参照してください。
認証が成功した場合は、Success .
SessionAuthenticationStrategyには新しいログインが通知されます。SessionAuthenticationStrategy(Javadoc) インターフェースを参照してください。SecurityContextHolder 内のすでに認証されている
Authenticationがロードされ、その権限が返された認証に追加されます。認証は SecurityContextHolder にセットされています。後で、将来のリクエストで自動的に設定できるように
SecurityContextを保存する必要がある場合は、SecurityContextRepository#saveContextを明示的に呼び出す必要があります。SecurityContextHolderFilter(Javadoc) クラスを参照してください。RememberMeServices.loginSuccessが呼び出されます。remember me が設定されていない場合、これは何も行いません。rememberme パッケージを参照してください。ApplicationEventPublisherはInteractiveAuthenticationSuccessEventを公開します。AuthenticationSuccessHandlerが呼び出されます。AuthenticationSuccessHandler(Javadoc) インターフェースを参照してください。