サーブレット認証アーキテクチャ
この説明では、サーブレットのセキュリティ: 全体像を拡張して、サーブレット認証で使用される 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 つの例は、ロールとスコープです。
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 にセットされています。後で、将来のリクエストで自動的に設定できるように
SecurityContext
を保存する必要がある場合は、SecurityContextRepository#saveContext
を明示的に呼び出す必要があります。SecurityContextHolderFilter
(Javadoc) クラスを参照してください。RememberMeServices.loginSuccess
が呼び出されます。remember me が設定されていない場合、これは何も行いません。rememberme パッケージを参照してください。ApplicationEventPublisher
はInteractiveAuthenticationSuccessEvent
を公開します。AuthenticationSuccessHandler
が呼び出されます。AuthenticationSuccessHandler
(Javadoc) インターフェースを参照してください。