最新の安定バージョンについては、Spring Security 6.4.5 を使用してください! |
アーキテクチャー
このセクションでは、サーブレットベースのアプリケーションにおける Spring Security のハイレベルなアーキテクチャについて説明します。このハイレベルな理解に基づいて、リファレンスの認証、認可、エクスプロイトに対する保護に関するセクションで説明します。
Filter
のレビュー
Spring Security のサーブレットサポートは、サーブレット Filter
に基づいているため、一般に最初に Filter
のロールを確認すると役立ちます。以下の図は、単一の HTTP リクエストのハンドラーの典型的な階層化を示しています。
クライアントはアプリケーションにリクエストを送信し、コンテナーはリクエスト 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
は 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 にラップされます。
SecurityFilterChain
SecurityFilterChain
(Javadoc) は、このリクエストに対してどの Spring Security Filter
を呼び出す必要があるかを判別するために FilterChainProxy によって使用されます。
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
を決定できます。これにより、アプリケーションのさまざまなスライスに完全に別個の構成を提供できます。
複数の 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 フィルターの順序の包括的なリストです。
ChannelProcessingFilter
WebAsyncManagerIntegrationFilter
SecurityContextPersistenceFilter
HeaderWriterFilter
CorsFilter
CsrfFilter
LogoutFilter
OAuth2AuthorizationRequestRedirectFilter
Saml2WebSsoAuthenticationRequestFilter
X509AuthenticationFilter
AbstractPreAuthenticatedProcessingFilter
CasAuthenticationFilter
OAuth2LoginAuthenticationFilter
Saml2WebSsoAuthenticationFilter
OpenIDAuthenticationFilter
DefaultLoginPageGeneratingFilter
DefaultLogoutPageGeneratingFilter
ConcurrentSessionFilter
BearerTokenAuthenticationFilter
RequestCacheAwareFilter
SecurityContextHolderAwareRequestFilter
JaasApiIntegrationFilter
RememberMeAuthenticationFilter
AnonymousAuthenticationFilter
OAuth2AuthorizationCodeGrantFilter
SessionManagementFilter
SwitchUserFilter
セキュリティ例外の処理
ExceptionTranslationFilter
(Javadoc) では、AccessDeniedException
(Javadoc) および AuthenticationException
(Javadoc) を HTTP レスポンスに変換できます。
ExceptionTranslationFilter
は、セキュリティフィルターの 1 つとして FilterChainProxy に挿入されます。
まず、
ExceptionTranslationFilter
がFilterChain.doFilter(request, response)
を呼び出して、残りのアプリケーションを呼び出します。ユーザーが認証されていない場合、または
AuthenticationException
の場合は、認証を開始します。SecurityContextHolder はクリアされます
HttpServletRequest
はRequestCache
(Javadoc) に保存されます。ユーザーが正常に認証されると、RequestCache
を使用して元のリクエストが再生されます。AuthenticationEntryPoint
は、クライアントから資格情報をリクエストするために使用されます。例: ログインページにリダイレクトするか、WWW-Authenticate
ヘッダーを送信する場合があります。
それ以外の場合は
AccessDeniedException
であり、アクセスが拒否されました。AccessDeniedHandler
は、拒否されたアクセスを処理するために呼び出されます。
アプリケーションが |
ExceptionTranslationFilter
の擬似コードは次のようになります。
try {
filterChain.doFilter(request, response); (1)
} catch (AccessDeniedException | AuthenticationException ex) {
if (!authenticated || ex instanceof AuthenticationException) {
startAuthentication(); (2)
} else {
accessDenied(); (3)
}
}
1 | Filter のレビューから、FilterChain.doFilter(request, response) を呼び出すことは、アプリケーションの残りを呼び出すことと同等であることを思い出してください。これは、アプリケーションの別の部分(つまり、FilterSecurityInterceptor またはメソッドセキュリティ)が AuthenticationException または AccessDeniedException をスローすると、ここでキャッチされて処理されることを意味します。 |
2 | ユーザーが認証されていないか、AuthenticationException である場合、認証を開始します。 |
3 | それ以外の場合、アクセスは拒否されました |