最新の安定バージョンについては、Spring Security 6.3.1 を使用してください!

アーキテクチャー

このセクションでは、サーブレットベースのアプリケーションにおける Spring Security のハイレベルなアーキテクチャについて説明します。このハイレベルな理解に基づいて、リファレンスの認証認可エクスプロイトに対する保護に関するセクションで説明します。

Filter のレビュー

Spring Security のサーブレットサポートは、サーブレット Filter に基づいているため、一般に最初に Filter のロールを確認すると役立ちます。以下の図は、単一の HTTP リクエストのハンドラーの典型的な階層化を示しています。

filterchain
図 1: FilterChain

クライアントはアプリケーションにリクエストを送信し、コンテナーはリクエスト URI のパスに基づいて HttpServletRequest を処理する Filter と Servlet を含む FilterChain を作成します。Spring MVC アプリケーションでは、Servlet は DispatcherServlet のインスタンスです。多くても 1 つの Servlet が 1 つの HttpServletRequest および HttpServletResponse を処理できます。ただし、複数の Filter を次の目的で使用できます。

  • ダウンストリーム Filter または Servlet が呼び出されないようにします。この場合、Filter は通常 HttpServletResponse を書き込みます。

  • ダウンストリーム Filter および Servlet が使用する HttpServletRequest または HttpServletResponse を変更します

Filter のパワーは、それに渡される FilterChain から得られます。

FilterChain の使用例
  • Java

  • Kotlin

public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) {
	// do something before the rest of the application
    chain.doFilter(request, response); // invoke the rest of the application
    // do something after the rest of the application
}
fun doFilter(request: ServletRequest, response: ServletResponse, chain: FilterChain) {
    // do something before the rest of the application
    chain.doFilter(request, response) // invoke the rest of the application
    // do something after the rest of the application
}

Filter は下流の Filter と Servlet にのみ影響を与えるため、各 Filter が呼び出される順序は非常に重要です。

DelegatingFilterProxy

Spring は、サーブレットコンテナーのライフサイクルと Spring の ApplicationContext の間のブリッジングを可能にする DelegatingFilterProxy (Javadoc) という名前の Filter 実装を提供します。サーブレットコンテナーでは、独自の標準を使用して Filter を登録できますが、Spring 定義の Bean を認識しません。DelegatingFilterProxy は標準のサーブレットコンテナーメカニズムを介して登録できますが、すべての作業を Filter を実装する Spring Bean に委譲します。

DelegatingFilterProxy が Filter および FilterChain にどのように適合するかの図です。

delegatingfilterproxy
図 2: DelegatingFilterProxy

DelegatingFilterProxy は ApplicationContext から Bean フィルター 0 を検索し、その後 Bean フィルター 0 を起動ます。DelegatingFilterProxy の擬似コードは以下のとおりです。

DelegatingFilterProxy 擬似コード
  • Java

  • Kotlin

public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) {
	// Lazily get Filter that was registered as a Spring Bean
	// For the example in DelegatingFilterProxy delegate is an instance of Bean Filter0
	Filter delegate = getFilterBean(someBeanName);
	// delegate work to the Spring Bean
	delegate.doFilter(request, response);
}
fun doFilter(request: ServletRequest, response: ServletResponse, chain: FilterChain) {
	// Lazily get Filter that was registered as a Spring Bean
	// For the example in DelegatingFilterProxy delegate is an instance of Bean Filter0
	val delegate: Filter = getFilterBean(someBeanName)
	// delegate work to the Spring Bean
	delegate.doFilter(request, response)
}

DelegatingFilterProxy のもう 1 つの利点は、Filter Bean インスタンスの表示を遅らせることができることです。コンテナーは、コンテナーを起動する前に Filter インスタンスを登録する必要があるため、これは重要です。ただし、Spring は通常 ContextLoaderListener を使用して Spring Bean をロードしますが、これは Filter インスタンスの登録が必要になるまで実行されません。

FilterChainProxy

Spring Security のサーブレットサポートは FilterChainProxy に含まれています。FilterChainProxy は、SecurityFilterChain を介して多くの Filter インスタンスに委譲できる、Spring Security によって提供される特別な Filter です。FilterChainProxy は Bean であるため、通常は DelegatingFilterProxy にラップされます。

filterchainproxy
図 3: FilterChainProxy

SecurityFilterChain

SecurityFilterChain (Javadoc) は、このリクエストに対してどの Spring Security Filter を呼び出す必要があるかを判別するために FilterChainProxy によって使用されます。

securityfilterchain
図 4: SecurityFilterChain

SecurityFilterChain のセキュリティフィルターは通常 Beans ですが、DelegatingFilterProxy ではなく FilterChainProxy に登録されています。FilterChainProxy は、サーブレットコンテナーまたは DelegatingFilterProxy に直接登録することに対して多くの利点を提供します。まず、Spring Security のすべてのサーブレットサポートの開始点を提供します。そのため、Spring Security のサーブレットサポートのトラブルシューティングを試みる場合は、FilterChainProxy にデバッグポイントを追加することから始めるのが最適です。

第二に、FilterChainProxy は Spring Security の使用の中心であるため、オプションとして表示されないタスクを実行できます。例: SecurityContext をクリアして、メモリリークを回避します。また、Spring Security の HttpFirewall を適用して、特定の種類の攻撃からアプリケーションを保護します。

さらに、SecurityFilterChain を呼び出すタイミングをより柔軟に決定できます。サーブレットコンテナーでは、Filter は URL のみに基づいて呼び出されます。ただし、FilterChainProxy は、RequestMatcher インターフェースを活用することにより、HttpServletRequest の内容に基づいて呼び出しを判別できます。

実際、FilterChainProxy を使用して、使用する SecurityFilterChain を決定できます。これにより、アプリケーションのさまざまなスライスに完全に別個の構成を提供できます。

multi securityfilterchain
図 5: 複数の SecurityFilterChain

複数の SecurityFilterChain 図では、FilterChainProxy はどの SecurityFilterChain を使用するかを決定します。一致する最初の SecurityFilterChain のみが呼び出されます。/api/messages/ の URL がリクエストされた場合、SecurityFilterChain0 のパターン /api/** で最初に一致するため、SecurityFilterChainn でも一致しますが、SecurityFilterChain0 のみが呼び出されます。/messages/ の URL がリクエストされた場合、SecurityFilterChain0 の /api/** のパターンと一致しないため、FilterChainProxy は各 SecurityFilterChain の試行を続けます。他のものがないと仮定すると、SecurityFilterChain インスタンスは SecurityFilterChainn と一致します。

SecurityFilterChain0 には 3 つのセキュリティ Filter インスタンスのみが構成されていることに注意してください。ただし、SecurityFilterChainn には 4 つのセキュリティ Filter が構成されています。各 SecurityFilterChain は一意であり、個別に構成できることに注意することが重要です。実際、アプリケーションが Spring Security に特定のリクエストを無視させたい場合、SecurityFilterChain のセキュリティ Filter はゼロになる可能性があります。

セキュリティフィルター

セキュリティフィルターは、SecurityFilterChain API を使用して FilterChainProxy に挿入されます。Filter の順序は重要です。通常、Spring Security の Filter の順序を知る必要はありません。ただし、順序を知ることが有益な場合があります

以下は、Spring Security フィルターの順序の包括的なリストです。

  • ForceEagerSessionCreationFilter

  • ChannelProcessingFilter

  • WebAsyncManagerIntegrationFilter

  • SecurityContextPersistenceFilter

  • HeaderWriterFilter

  • CorsFilter

  • CsrfFilter

  • LogoutFilter

  • OAuth2AuthorizationRequestRedirectFilter

  • Saml2WebSsoAuthenticationRequestFilter

  • X509AuthenticationFilter

  • AbstractPreAuthenticatedProcessingFilter

  • CasAuthenticationFilter

  • OAuth2LoginAuthenticationFilter

  • Saml2WebSsoAuthenticationFilter

  • UsernamePasswordAuthenticationFilter

  • OpenIDAuthenticationFilter

  • DefaultLoginPageGeneratingFilter

  • DefaultLogoutPageGeneratingFilter

  • ConcurrentSessionFilter

  • DigestAuthenticationFilter

  • BearerTokenAuthenticationFilter

  • BasicAuthenticationFilter

  • RequestCacheAwareFilter

  • SecurityContextHolderAwareRequestFilter

  • JaasApiIntegrationFilter

  • RememberMeAuthenticationFilter

  • AnonymousAuthenticationFilter

  • OAuth2AuthorizationCodeGrantFilter

  • SessionManagementFilter

  • ExceptionTranslationFilter

  • FilterSecurityInterceptor

  • SwitchUserFilter

セキュリティ例外の処理

ExceptionTranslationFilter は、セキュリティフィルターの 1 つとして FilterChainProxy に挿入されます。

exceptiontranslationfilter
  • number 1 まず、ExceptionTranslationFilter が FilterChain.doFilter(request, response) を呼び出して、残りのアプリケーションを呼び出します。

  • number 2 ユーザーが認証されていない場合、または AuthenticationException の場合は、認証を開始します。

    • SecurityContextHolder はクリアされます

    • HttpServletRequest は RequestCache (Javadoc) に保存されます。ユーザーが正常に認証されると、RequestCache を使用して元のリクエストが再生されます。

    • AuthenticationEntryPoint は、クライアントから資格情報をリクエストするために使用されます。例: ログインページにリダイレクトするか、WWW-Authenticate ヘッダーを送信する場合があります。

  • number 3 それ以外の場合は AccessDeniedException であり、アクセスが拒否されました。AccessDeniedHandler は、拒否されたアクセスを処理するために呼び出されます。

アプリケーションが AccessDeniedException または AuthenticationException をスローしない場合、ExceptionTranslationFilter は何もしません。

ExceptionTranslationFilter の擬似コードは次のようになります。

ExceptionTranslationFilter 擬似コード
try {
	filterChain.doFilter(request, response); (1)
} catch (AccessDeniedException | AuthenticationException ex) {
	if (!authenticated || ex instanceof AuthenticationException) {
		startAuthentication(); (2)
	} else {
		accessDenied(); (3)
	}
}
1Filter のレビューから、FilterChain.doFilter(request, response) を呼び出すことは、アプリケーションの残りを呼び出すことと同等であることを思い出してください。これは、アプリケーションの別の部分(つまり、FilterSecurityInterceptor またはメソッドセキュリティ)が AuthenticationException または AccessDeniedException をスローすると、ここでキャッチされて処理されることを意味します。
2 ユーザーが認証されていないか、AuthenticationException である場合、認証開始します。
3 それ以外の場合、アクセスは拒否されました