web-dev-qa-db-ja.com

Spring Securityフィルタチェーンのしくみ

私は、Springセキュリティはリクエストの傍受、認証の検出、認証エントリポイントへのリダイレクト、あるいはリクエストを認可サービスに渡す、一連のフィルタの上に構築されていることを認識しています。 (未認証または未認証) DelegatingFitlerProxy はこれらのフィルターを結合します。それらのタスクを実行するために、これらのフィルタは UserDetailsS​​ervice AuthenticationManager のようなサービスにアクセスします。

チェーン内の重要なフィルタは(順番に)

  • SecurityContextPersistenceFilter(JSESSIONIDから認証を復元します)
  • UsernamePasswordAuthenticationFilter(認証を行います)
  • ExceptionTranslationFilter(FilterSecurityInterceptorからセキュリティ例外をキャッチ)
  • FilterSecurityInterceptor(認証および承認の例外をスローする可能性があります)

私はこれらのフィルタがどのように使われているか混乱しています。春に提供されたform-loginでは、 UsernamePasswordAuthenticationFilter /login にのみ使用され、後者のフィルタは使用されません。 form-login namespace要素はこれらのフィルタを自動設定しますか?すべての要求(認証されているかどうかにかかわらず)は、 FilterSecurityInterceptor にログイン以外のURLに到達しますか。

ログインから取得した JWT-token でREST AP​​Iを保護する場合はどうすればよいですか?2つの名前空間設定httpタグを設定しなければなりません、権利?他のものはUsernamePasswordAuthenticationFilterを持つ/login 用、そしてカスタムのJwtAuthenticationFilterを持つREST url用です。

2つのhttp要素を設定すると2つのspringSecurityFitlerChainsが作成されますか? form-loginを宣言するまで、UsernamePasswordAuthenticationFilterはデフォルトでオフになっていますか?どうすればSecurityContextPersistenceFilterを1に置き換えることができます。そうすれば、Authenticationではなく既存のJWT-tokenからJSESSIONIDを取得できますか?

87
Tuomas Toivonen

Springセキュリティフィルタチェーンは非常に複雑で柔軟なエンジンです。

チェーン内の重要なフィルタは(順番に)

  • SecurityContextPersistenceFilter(JSESSIONIDから認証を復元します)
  • UsernamePasswordAuthenticationFilter(認証を行います)
  • ExceptionTranslationFilter(FilterSecurityInterceptorからセキュリティ例外をキャッチ)
  • FilterSecurityInterceptor(認証および承認の例外をスローする可能性があります)

現在の安定版リリース4.2.1のドキュメント 、セクション 13.3フィルタの順序付け を見ると、フィルタチェーン全体のフィルタ構成がわかります。

13.3フィルタの順序

フィルタがチェーン内で定義されている順序は非常に重要です。実際に使用しているフィルタに関係なく、順序は次のとおりです。

  1. ChannelProcessingFilter、別のプロトコルにリダイレクトする必要がある可能性があるため

  2. SecurityContextPersistenceFilterなので、Webリクエストの開始時にSecurityContextHolderにSecurityContextを設定できます。また、SecurityContextに変更を加えることができます。 Web要求が終了したときにHttpSessionにコピーされます(次のWeb要求で使用できるようになります)。

  3. ConcurrentSessionFilter。これは、SecurityContextHolder機能を使用し、プリンシパルからの進行中の要求を反映するようにSessionRegistryを更新する必要があるためです。

  4. 認証処理メカニズム - UsernamePasswordAuthenticationFilterCasAuthenticationFilterBasicAuthenticationFilterなど - SecurityContextHolderを変更して有効な認証要求トークンを含めることができます。

  5. サーブレットコンテナにSpring Security対応のHttpServletRequestWrapperをインストールするために使用している場合は、SecurityContextHolderAwareRequestFilter

  6. JaasApiIntegrationFilterJaasAuthenticationTokenがSecurityContextHolderにある場合、これはFilterChainをSubjectとして処理します。 JaasAuthenticationToken内に

  7. RememberMeAuthenticationFilter、以前の認証処理メカニズムでSecurityContextHolderが更新されていない場合は、要求によって、私を忘れないでサービスを受けられるようにするクッキーが提示されます。場所には、適切な記憶された認証オブジェクトが配置されます。

  8. AnonymousAuthenticationFilterなので、以前の認証処理メカニズムでSecurityContextHolderが更新されていない場合は、匿名の認証オブジェクトがそこに配置されます。

  9. HTTPエラー応答を返すことができるように、または適切なAuthenticationEntryPointを起動することができるように、Spring Securityの例外をキャッチするためのExceptionTranslationFilter

  10. FilterSecurityInterceptor。WebURIを保護し、アクセスが拒否されたときに例外を発生させます。

さて、私はあなたの質問によって一つずつ続けようとします:

私はこれらのフィルタがどのように使われているか混乱しています。春に提供されたform-loginでは、UsernamePasswordAuthenticationFilterは/ loginにのみ使用され、後者のフィルターは使用されないということですか? form-login名前空間要素はこれらのフィルタを自動設定しますか?すべての要求(認証されているかどうかにかかわらず)が、非ログインURLのFilterSecurityInterceptorに到達しますか?

<security-http>セクションを設定したら、各セクションに対して少なくとも1つの認証メカニズムを提供する必要があります。これは、先ほど参照したSpring Securityのドキュメントの13.3フィルタの順序のセクションのグループ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を@Autowiresしてその内容を返す単純な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でその要求を処理して起動時にエラーをスローすることさえありますが、<http:security>にentry-point-ref属性を追加することは回避できます。

基本的な<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で作成され、設定されています。

だから、今、質問:

春に提供されたform-loginでは、UsernamePasswordAuthenticationFilterは/ loginにのみ使用され、後者のフィルターは使用されないということですか?

はい、リクエストがUsernamePasswordAuthenticationFilterのURLと一致する場合に、ログイン処理メカニズムを完了しようとするために使用されます。このURLは設定することも変更することもでき、すべてのリクエストに合わせてその動作を変更することができます。

同じFilterchainProxyに複数の認証処理メカニズムを設定することもできます(HttpBasic、CASなど)。

Form-login名前空間要素はこれらのフィルタを自動設定しますか?

いいえ、form-login要素はUsernamePasswordAUthenticationFilterを設定し、ログインページのURLを指定しない場合はorg.springframework.security.web.authentication.uiも設定します。これは単純な自動生成ログインで終了します。ページ。

他のフィルタは、<security:http>属性を指定せずにsecurity:"none"要素を作成するだけで、デフォルトで自動設定されます。

すべての要求(認証されているかどうかにかかわらず)が、非ログインURLのFilterSecurityInterceptorに到達しますか?

リクエストがリクエストされたURLに到達する権限を持っているかどうかを処理する要素であるため、すべてのリクエストはそれに到達する必要があります。しかし、以前に処理されたフィルタの中には、FilterChain.doFilter(request, response);を呼び出さないでフィルタチェーン処理を停止させるものがあります。たとえば、要求にcsrfパラメータが含まれていない場合、CSRFフィルタはフィルタチェーン処理を停止することがあります。

私のREST AP​​Iを、ログインから取得したJWT-tokenで保護したい場合はどうすればいいですか?私は2つの名前空間設定httpタグを設定しなければなりません、権利?もう1つはUsernamePasswordAuthenticationFilterを持つ/ login用、もう1つはカスタムJwtAuthenticationFilterを持つREST url用です。

いいえ、あなたはこの方法を強制されていません。 UsernamePasswordAuthenticationFilterJwtAuthenticationFilterの両方を同じhttp要素内で宣言することもできますが、それはこの各フィルタの具体的な動作によって異なります。どちらの方法も可能で、どちらを選択するかは最終的には自分の好みによって決まります。

2つのhttp要素を設定すると2つのspringSecurityFitlerChainsが作成されますか?

はい、そうです

Form-loginを宣言するまで、UsernamePasswordAuthenticationFilterはデフォルトでオフになっていますか?

はい、あなたは私が投稿した設定のそれぞれで上げられたフィルタでそれを見ることができました

SecurityContextPersistenceFilterをJSESSIONIDではなく既存のJWTトークンから認証を取得するものに置き換えるにはどうすればよいですか。

<http:element>セッションストラテジー を設定するだけで、SecurityContextPersistenceFilterを避けることができます。このように設定するだけです:

<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ヘッダーとしても要求本体の内部にも提供できる一種の承認トークンを使用する場合があります。そのため、1つはHttpヘッダーから、もう1つは独自のrestリクエストのリクエストボディからトークンを復元する2つのフィルタを設定します。 1つのhttpリクエストがその認証トークンをHttpヘッダとしてもリクエストボディの内部にも提供する場合、両方のフィルタがそれをマネージャに委譲する認証メカニズムを実行しようとしますが、単にリクエストが各フィルタのdoFilter()メソッドの開始時にすでに認証済みです。

複数の認証フィルタを持つことは、複数の認証プロバイダを持つことに関連しますが、それを強制しないでください。以前に公開したケースでは、認証フィルターは2つありますが、認証プロバイダーは1つだけです。どちらのフィルターも同じタイプの認証オブジェクトを作成するので、どちらの場合も認証マネージャーが同じプロバイダーに委任します。

これとは反対に、私はUsernamePasswordAuthenticationFilterを1つだけ公開しますが、ユーザー資格情報は両方ともDBまたはLDAPに含めることができるので、2つのUsernamePasswordAuthenticationTokenサポートプロバイダーがあり、AuthenticationManagerはフィルターからの認証試行をプロバイダーに委任します資格情報を検証するために.

したがって、認証フィルタの数によって認証プロバイダの数が決定されることも、プロバイダの量によってフィルタの量が決定されることもないことは明らかです。

また、ドキュメントには、SecurityContextPersistenceFilterがSecurityContextのクリーニングを担当していると記載されています。これは、スレッドプールのために重要です。それを省略したり、カスタム実装を提供したりする場合は、手動でクリーニングを実装する必要があります。チェーンをカスタマイズするときに似たような問題はありますか?

私はこのフィルタを以前は注意深く調べていませんでしたが、最後の質問で実装をチェックしてきました。通常のSpringのように、ほぼすべてを設定、拡張、上書きすることができました。

SecurityContextPersistenceFilter は、 SecurityContextRepository 実装内でSecurityContextの検索を委任します。デフォルトでは、 HttpSessionSecurityContextRepository が使用されますが、これはフィルタのコンストラクタの1つを使用して変更できます。したがって、最初からすべてを作成するのではなく、証明された動作を信頼して、自分のニーズに合ったSecurityContextRepositoryを作成し、それをSecurityContextPersistenceFilterに設定するだけでよいでしょう。

140
jlumietu

UsernamePasswordAuthenticationFilter/loginにのみ使用され、後者のフィルタは使用されませんか?

いいえ、UsernamePasswordAuthenticationFilterAbstractAuthenticationProcessingFilterを拡張します。これにはRequestMatcherが含まれます。つまり、独自の処理URLを定義できます。このフィルタは要求のURLに一致するRequestMatcherのみを処理し、デフォルトの処理URLは/loginです。

UsernamePasswordAuthenticationFilterchain.doFilter(request, response);を実行している場合でも、後のフィルタで要求を処理できます。

コアフィットの詳細

Form-login名前空間要素はこれらのフィルタを自動設定しますか?

UsernamePasswordAuthenticationFilter<form-login>によって作成されます、これらは 標準のフィルタエイリアスと順序 です

すべての要求(認証されているかどうかにかかわらず)が、非ログインURLのFilterSecurityInterceptorに到達しますか?

それは前のフィトラーが成功したかどうかによって異なりますが、FilterSecurityInterceptorは通常最後のフィトラーです。

2つのhttp要素を設定すると2つのspringSecurityFitlerChainsが作成されますか?

はい、すべてのfitlerChainにはRequestMatcherがあります。RequestMatcherが要求と一致する場合、要求はfitlerチェーン内のfitlerによって処理されます。

パターンを設定しない場合、デフォルトのRequestMatcherがすべての要求に一致します。または、特定のURL(<http pattern="/rest/**")を設定することもできます。

フィトラーについてもっと知りたければ、春のセキュリティでソースコードをチェックできると思います。 doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain)

4
chaoluo