サーブレット認証アーキテクチャ

この説明では、サーブレットのセキュリティ: 全体像を拡張して、サーブレット認証で使用される Spring Security の主要なアーキテクチャコンポーネントについて説明します。これらの部品がどのように組み合わされるかを説明する具体的なフローが必要な場合は、認証メカニズム固有のセクションを参照してください。

SecurityContextHolder

Spring Security の認証モデルの中核は SecurityContextHolder です。SecurityContext が含まれています。

securitycontextholder

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 です。

providermanager

実際には、各 AuthenticationProvider は、特定の型の認証を実行する方法を知っています。例: 1 つの AuthenticationProvider がユーザー名 / パスワードを検証できる可能性があり、別の AuthenticationProvider が SAML アサーションを認証できる可能性があります。これにより、各 AuthenticationProvider は、複数の型の認証をサポートしながら、非常に特定の型の認証を実行し、単一の AuthenticationManager Bean のみを公開できます。

ProviderManager では、オプションの親 AuthenticationManager を構成することもできます。これは、AuthenticationProvider が認証を実行できない場合に参照されます。親は任意の型の AuthenticationManager にすることができますが、多くの場合、ProviderManager のインスタンスです。

providermanager parent

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

providermanagers parent

デフォルトでは、ProviderManager は、成功した認証リクエストによって返される Authentication オブジェクトから機密の資格情報をクリアしようとします。これにより、パスワードなどの情報が HttpSession で必要以上に長く保持されるのを防ぎます。

CredentialsContainer インターフェースは、認証プロセスにおいて重要なロールを果たします。資格情報が不要になったら消去できるため、機密データが必要以上に長く保持されないようにして、セキュリティが強化されます。

これにより、たとえば、ステートレスアプリケーションのパフォーマンスを向上させるために、ユーザーオブジェクトのキャッシュを使用するときに問題が発生する可能性があります。Authentication にキャッシュ内のオブジェクト(UserDetails インスタンスなど)への参照が含まれていて、その資格情報が削除されている場合、キャッシュされた値に対して認証することはできなくなります。キャッシュを使用する場合は、これを考慮する必要があります。明らかな解決策は、最初に、キャッシュ実装または返された Authentication オブジェクトを作成する AuthenticationProvider のいずれかで、オブジェクトのコピーを作成することです。または、ProviderManager の eraseCredentialsAfterAuthentication プロパティを無効にすることもできます。ProviderManager (Javadoc) クラスについては Javadoc を参照してください。

AuthenticationProvider

複数の AuthenticationProvider (Javadoc) インスタンスを ProviderManager に注入できます。各 AuthenticationProvider は、特定の型の認証を実行します。例: DaoAuthenticationProvider はユーザー名 / パスワードベースの認証をサポートし、JwtAuthenticationProvider は JWT トークンの認証をサポートします。

AuthenticationEntryPoint を使用した資格情報のリクエスト

AuthenticationEntryPoint (Javadoc) は、クライアントから資格情報をリクエストする HTTP レスポンスを送信するために使用されます。

場合によっては、クライアントがリソースをリクエストするための資格情報(ユーザー名やパスワードなど)を事前に含めることがあります。このような場合、Spring Security は、クライアントに資格情報をリクエストする HTTP レスポンスを提供する必要はありません。これは、それらがすでに含まれているためです。

その他の場合、クライアントは、アクセスが認可されていないリソースに対して認証されていないリクエストを行います。この場合、AuthenticationEntryPoint の実装を使用して、クライアントに資格情報をリクエストします。AuthenticationEntryPoint 実装は、ログインページへのリダイレクトを実行したり、WWW 認証ヘッダーで応答したり、その他のアクションを実行したりする場合があります。

AbstractAuthenticationProcessingFilter

AbstractAuthenticationProcessingFilter (Javadoc) は、ユーザーの資格情報を認証するためのベース Filter として使用されます。資格情報を認証する前に、Spring Security は通常 AuthenticationEntryPoint を使用して資格情報をリクエストします。

次に、AbstractAuthenticationProcessingFilter は送信された認証リクエストを認証できます。

abstractauthenticationprocessingfilter

number 1 ユーザーが資格情報を送信すると、AbstractAuthenticationProcessingFilter は認証される HttpServletRequest から Authentication を作成します。作成される Authentication の型は、AbstractAuthenticationProcessingFilter のサブクラスによって異なります。例: UsernamePasswordAuthenticationFilter は、HttpServletRequest で送信されたユーザー名パスワードから UsernamePasswordAuthenticationToken を作成します。

number 2 次に、AuthenticationAuthenticationManager に渡されて認証されます。

number 3 認証が失敗した場合、Failure

  • SecurityContextHolder はクリアされます。

  • RememberMeServices.loginFail が呼び出されます。remember me が設定されていない場合、これは何も行いません。rememberme パッケージを参照してください。

  • AuthenticationFailureHandler が呼び出されます。AuthenticationFailureHandler (Javadoc) インターフェースを参照してください。

number 4 認証が成功した場合は、Success .

  • SessionAuthenticationStrategy には新しいログインが通知されます。SessionAuthenticationStrategy (Javadoc) インターフェースを参照してください。

  • 認証SecurityContextHolder にセットされています。後で、将来のリクエストで自動的に設定できるように SecurityContext を保存する必要がある場合は、SecurityContextRepository#saveContext を明示的に呼び出す必要があります。SecurityContextHolderFilter (Javadoc) クラスを参照してください。

  • RememberMeServices.loginSuccess が呼び出されます。remember me が設定されていない場合、これは何も行いません。rememberme パッケージを参照してください。

  • ApplicationEventPublisher は InteractiveAuthenticationSuccessEvent を公開します。

  • AuthenticationSuccessHandler が呼び出されます。AuthenticationSuccessHandler (Javadoc) インターフェースを参照してください。