Springセキュリティフィルターチェーンは、非常に複雑で柔軟なエンジンです。
チェーン内の主要なフィルターは(順序で)
- SecurityContextPersistenceFilter(JSESSIONIDから認証を復元します)
- UsernamePasswordAuthenticationFilter(認証を実行)
- ExceptionTranslationFilter(FilterSecurityInterceptorからのセキュリティ例外をキャッチ)
- FilterSecurityInterceptor(認証と承認の例外をスローする場合があります)
見てみると、現在の安定版リリース4.2.1ドキュメント、セクション発注13.3フィルターあなたが全体のフィルター・チェーンのフィルター組織を見ることができます。
13.3フィルターの順序
フィルターがチェーンで定義される順序は非常に重要です。実際に使用しているフィルターに関係なく、順序は次のようになります。
ChannelProcessingFilter(別のプロトコルにリダイレクトする必要がある可能性があるため)
SecurityContextPersistenceFilter。これにより、Web要求の開始時にSecurityContextHolderでSecurityContextを設定でき、Web要求が終了したときにSecurityContextへの変更をHttpSessionにコピーできます(次のWeb要求で使用する準備ができています)。
ConcurrentSessionFilter(SecurityContextHolder機能を使用し、プリンシパルからの進行中の要求を反映するようにSessionRegistryを更新する必要があるため)
認証処理メカニズム-
UsernamePasswordAuthenticationFilter、CasAuthenticationFilter、
BasicAuthenticationFilterなど- SecurityContextHolderが有効な認証要求トークンを含むように修飾することができるように
SecurityContextHolderAwareRequestFilter、あなたのサーブレットコンテナに春のセキュリティ意識HttpServletRequestWrapperクラスをインストールするためにそれを使用している場合
JaasApiIntegrationFilterがあれば、JaasAuthenticationTokenは SecurityContextHolderである、これはJaasAuthenticationTokenで件名としてれるFilterChainを処理します
RememberMeAuthenticationFilter。これにより、以前の認証処理メカニズムがSecurityContextHolderを更新せず、リクエストがremember-meサービスの実行を可能にするcookieを提示する場合、適切な記憶された認証オブジェクトがそこに配置されます。
AnonymousAuthenticationFilter。これにより、以前の認証処理メカニズムがSecurityContextHolderを更新しなかった場合、匿名認証オブジェクトがそこに配置されます。
ExceptionTranslationFilter、Spring Security例外をキャッチして、HTTPエラー応答を返すか、適切なAuthenticationEntryPointを起動できるようにします。
FilterSecurityInterceptor、Web URIを保護し、アクセスが拒否されたときに例外を発生させる
さて、私はあなたの質問を一つずつ続けようとします:
これらのフィルターの使用方法がわかりません。春に提供されるフォームログインの場合、UsernamePasswordAuthenticationFilterは/ loginにのみ使用され、後者のフィルターは使用されないのですか?form-login名前空間要素はこれらのフィルターを自動構成しますか?すべてのリクエスト(認証済みかどうかにかかわらず)は、非ログインURLのFilterSecurityInterceptorに到達しますか?
設定したら <security-http>
セクションセクションごとに少なくとも1つの認証メカニズムを提供する必要があります。これは、私が参照したばかりのSpring Securityドキュメントの13.3 Filter Orderingセクションのグループ4に一致するフィルターの1つである必要があります。
これは、構成可能な最小の有効なsecurity:http要素です。
<security:http authentication-manager-ref="mainAuthenticationManager"
entry-point-ref="serviceAccessDeniedHandler">
<security:intercept-url pattern="/sectest/zone1/**" access="hasRole('ROLE_ADMIN')"/>
</security:http>
それを行うだけで、これらのフィルターはフィルターチェーンプロキシで構成されます。
{
"1": "org.springframework.security.web.context.SecurityContextPersistenceFilter",
"2": "org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter",
"3": "org.springframework.security.web.header.HeaderWriterFilter",
"4": "org.springframework.security.web.csrf.CsrfFilter",
"5": "org.springframework.security.web.savedrequest.RequestCacheAwareFilter",
"6": "org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter",
"7": "org.springframework.security.web.authentication.AnonymousAuthenticationFilter",
"8": "org.springframework.security.web.session.SessionManagementFilter",
"9": "org.springframework.security.web.access.ExceptionTranslationFilter",
"10": "org.springframework.security.web.access.intercept.FilterSecurityInterceptor"
}
注:FilterChainProxyを@Autowireし、そのコンテンツを返す単純なRestControllerを作成することで、それらを取得します。
@Autowired
private FilterChainProxy filterChainProxy;
@Override
@RequestMapping("/filterChain")
public @ResponseBody Map<Integer, Map<Integer, String>> getSecurityFilterChainProxy(){
return this.getSecurityFilterChainProxy();
}
public Map<Integer, Map<Integer, String>> getSecurityFilterChainProxy(){
Map<Integer, Map<Integer, String>> filterChains= new HashMap<Integer, Map<Integer, String>>();
int i = 1;
for(SecurityFilterChain secfc : this.filterChainProxy.getFilterChains()){
//filters.put(i++, secfc.getClass().getName());
Map<Integer, String> filters = new HashMap<Integer, String>();
int j = 1;
for(Filter filter : secfc.getFilters()){
filters.put(j++, filter.getClass().getName());
}
filterChains.put(i++, filters);
}
return filterChains;
}
ここでは<security:http>
、1つの最小構成で要素を宣言するだけで、すべてのデフォルトフィルターが含まれていますが、認証タイプのものはありません(13.3フィルターの順序付けセクションの4番目のグループ)。したがって、実際には、security:http
要素を宣言するだけで、SecurityContextPersistenceFilter、ExceptionTranslationFilter、およびFilterSecurityInterceptorが自動構成されます。
実際、1つの認証処理メカニズムを構成する必要があり、セキュリティ名前空間Beanもその要求を処理し、起動時にエラーをスローしますが、entry-point-ref属性を <http:security>
基本<form-login>
を構成に追加すると、次のようになります。
<security:http authentication-manager-ref="mainAuthenticationManager">
<security:intercept-url pattern="/sectest/zone1/**" access="hasRole('ROLE_ADMIN')"/>
<security:form-login />
</security:http>
これで、filterChainは次のようになります。
{
"1": "org.springframework.security.web.context.SecurityContextPersistenceFilter",
"2": "org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter",
"3": "org.springframework.security.web.header.HeaderWriterFilter",
"4": "org.springframework.security.web.csrf.CsrfFilter",
"5": "org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter",
"6": "org.springframework.security.web.authentication.ui.DefaultLoginPageGeneratingFilter",
"7": "org.springframework.security.web.savedrequest.RequestCacheAwareFilter",
"8": "org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter",
"9": "org.springframework.security.web.authentication.AnonymousAuthenticationFilter",
"10": "org.springframework.security.web.session.SessionManagementFilter",
"11": "org.springframework.security.web.access.ExceptionTranslationFilter",
"12": "org.springframework.security.web.access.intercept.FilterSecurityInterceptor"
}
これで、この2つのフィルターorg.springframework.security.web.authentication.UsernamePasswordAuthenticationFilterとorg.springframework.security.web.authentication.ui.DefaultLoginPageGeneratingFilterが作成され、FilterChainProxyで構成されます。
だから、今、質問:
春に提供されたフォームログインの場合、UsernamePasswordAuthenticationFilterは/ loginにのみ使用され、後者のフィルターは使用されないのですか?
はい、リクエストがUsernamePasswordAuthenticationFilter URLと一致する場合に、ログイン処理メカニズムを完了しようとするために使用されます。このURLは、すべての要求に一致するように構成したり、動作を変更したりできます。
同じFilterchainProxy(HttpBasic、CASなど)で複数の認証処理メカニズムを構成することもできます。
form-login名前空間要素はこれらのフィルターを自動構成しますか?
いいえ、form-login要素はUsernamePasswordAUthenticationFilterを構成し、ログインページのURLを指定しない場合は、単純な自動生成ログインで終了するorg.springframework.security.web.authentication.ui.DefaultLoginPageGeneratingFilterも構成します。ページ。
その他のフィルターは<security:http>
、security:"none"
属性のない要素を作成するだけで、デフォルトで自動構成されます。
すべてのリクエスト(認証済みかどうかにかかわらず)は、非ログインURLのFilterSecurityInterceptorに到達しますか?
リクエストがリクエストされたURLに到達する権限を持っているかどうかを処理する要素であるため、すべてのリクエストはそれに到達する必要があります。ただし、以前に処理されたフィルタの一部は、を呼び出さずにフィルタチェーン処理を停止する場合がありFilterChain.doFilter(request, response);
ます。たとえば、リクエストにcsrfパラメータがない場合、CSRFフィルタはフィルタチェーン処理を停止する可能性があります。
ログインから取得したJWTトークンでREST APIを保護したい場合はどうなりますか?2つの名前空間構成httpタグ、権利を構成する必要がありますか?/ login用のUsernamePasswordAuthenticationFilter
もう1つと、REST URL用の、もう1つはcustom JwtAuthenticationFilter
です。
いいえ、このようにする必要はありません。UsernamePasswordAuthenticationFilter
との両方をJwtAuthenticationFilter
同じhttp要素で宣言することもできますが、それはこの各フィルターの具体的な動作に依存します。どちらのアプローチも可能であり、最終的にどちらを選択するかは、ユーザーの好みによって異なります。
2つのhttp要素を構成すると、2つのspringSecurityFitlerChainsが作成されますか?
はい、そうです
form-loginを宣言するまで、UsernamePasswordAuthenticationFilterはデフォルトでオフになっていますか?
はい、私が投稿した設定のそれぞれで発生したフィルターでそれを見ることができました
JSESSIONIDではなく既存のJWTトークンから認証を取得するSecurityContextPersistenceFilterを置き換えるにはどうすればよいですか?
でセッション戦略を構成するだけで、SecurityContextPersistenceFilterを回避できます<http:element>
。次のように設定するだけです:
<security:http create-session="stateless" >
または、この場合、次のように<security:http>
要素内で別のフィルターで上書きできます。
<security:http ...>
<security:custom-filter ref="myCustomFilter" position="SECURITY_CONTEXT_FILTER"/>
</security:http>
<beans:bean id="myCustomFilter" class="com.xyz.myFilter" />
編集:
「同じFilterchainProxyで複数の認証処理メカニズムを構成することもできます」に関する1つの質問。複数の(Spring実装)認証フィルターを宣言する場合、後者は最初の認証によって実行される認証を上書きしますか?これは複数の認証プロバイダーを持つことにどのように関係しますか?
これは最終的に各フィルター自体の実装に依存しますが、後者の認証フィルターは少なくとも、前のフィルターによって最終的に行われたすべての以前の認証を上書きできるという事実は事実です。
しかし、これは必ずしも発生しません。セキュリティで保護されたRESTサービスで、HTTPヘッダーとして、またはリクエストボディ内で提供できる一種の認証トークンを使用するプロダクションケースがあります。したがって、トークンを回復する2つのフィルターを構成します。1つはHttpヘッダーから、もう1つは独自のRESTリクエストのリクエスト本文からです。1つのhttpリクエストがその認証トークンをHttpヘッダーとリクエスト本文の両方として提供する場合、両方のフィルターがそれをマネージャーに委任する認証メカニズムを実行しようとするのは事実ですが、リクエストがdoFilter()
各フィルターのメソッドの開始時にすでに認証されています。
複数の認証フィルターを持つことは、複数の認証プロバイダーを持つことに関連しますが、それを強制しないでください。以前に公開したケースでは、2つの認証フィルターがありますが、認証プロバイダーは1つしかありません。両方のフィルターが同じタイプの認証オブジェクトを作成するため、どちらの場合も認証マネージャーが同じプロバイダーに委任します。
これとは逆に、UsernamePasswordAuthenticationFilterを1つだけ公開するシナリオもありますが、ユーザー資格情報は両方ともDBまたはLDAPに含めることができるため、2つのUsernamePasswordAuthenticationTokenサポートプロバイダーがあり、AuthenticationManagerは、フィルターからプロバイダーへの認証試行を委任します秘密裏に資格情報を検証します。
したがって、認証フィルターの量によって認証プロバイダーの量が決まることも、プロバイダーの量によってフィルターの量が決まることも明らかです。
また、ドキュメントにはSecurityContextPersistenceFilterがSecurityContextのクリーンアップを担当することが記載されていますが、これはスレッドプールのために重要です。省略するか、カスタム実装を提供する場合、クリーニングを手動で実装する必要がありますよね?チェーンをカスタマイズする際に、同様の落とし穴はありますか?
以前はこのフィルターを注意深く調べていませんでしたが、最後の質問の後で、フィルターの実装を確認しました。通常のSpringと同様に、ほぼすべての構成、拡張、または上書きが可能です。
SecurityContextPersistenceFilterの中に委譲しSecurityContextRepository実装たSecurityContextを検索します。デフォルトではHttpSessionSecurityContextRepositoryが使用されますが、フィルターのコンストラクターの1つを使用して変更できます。したがって、ニーズに合ったSecurityContextRepositoryを作成し、SecurityContextPersistenceFilterで構成するだけで、すべてを最初から作成するのではなく、証明された動作を信頼する方がよい場合があります。