Spring Security Filter Chain 如何工作

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/41480102/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-08 01:18:26  来源:igfitidea点击:

How Spring Security Filter Chain works

springauthenticationspring-securityfilterjwt

提问by Tuomas Toivonen

I realize that Spring security build on chain of filters, which will intercept the request, detect (absence of) authentication, redirect to authentication entry point or pass the request to authorization service, and eventually let the request either hit the servlet or throw security exception (unauthenticated or unauthorized). DelegatingFitlerProxyglues these filters together. To perform their tasks, these filter access services such as UserDetailsServiceand AuthenticationManager.

我意识到 Spring security 建立在过滤器链上,它将拦截请求,检测(缺少)身份验证,重定向到身份验证入口点或将请求传递给授权服务,并最终让请求命中 servlet 或抛出安全异常(未经身份验证或未经授权)。DelegatingFitlerProxy将这些过滤器粘合在一起。为了执行它们的任务,这些过滤器访问服务,例如UserDetailsS​​erviceAuthenticationManager

Key filters in the chain are (in the order)

链中的关键过滤器是(按顺序)

  • SecurityContextPersistenceFilter (restores Authentication from JSESSIONID)
  • UsernamePasswordAuthenticationFilter (performs authentication)
  • ExceptionTranslationFilter (catch security exceptions from FilterSecurityInterceptor)
  • FilterSecurityInterceptor (may throw authentication and authorization exceptions)
  • SecurityContextPersistenceFilter(从 JSESSIONID 恢复身份验证)
  • UsernamePasswordAuthenticationFilter(执行身份验证)
  • ExceptionTranslationFilter(从 FilterSecurityInterceptor 捕获安全异常)
  • FilterSecurityInterceptor(可能抛出身份验证和授权异常)

I'm confused how these filters are used. Is it that for the spring provided form-login, UsernamePasswordAuthenticationFilteris only used for /login, and latter filters are not? Does the form-loginnamespace element auto-configure these filters? Does every request (authenticated or not) reach FilterSecurityInterceptorfor non-login url?

我很困惑如何使用这些过滤器。是不是 spring 提供的表单登录,UsernamePasswordAuthenticationFilter只用于/login,后面的过滤器不是?form-login命名空间元素是否会自动配置这些过滤器?每个请求(是否经过身份验证)是否都会到达FilterSecurityInterceptor以获取非登录 url?

What if I want to secure my REST API with JWT-token, which is retrieved from login?I must configure two namespace configuration httptags, rights? One for /loginwith UsernamePasswordAuthenticationFilter, and another one for REST url's, with custom JwtAuthenticationFilter.

如果我想使用从登录中检索的JWT-token保护我的 REST API怎么办?我必须配置两个命名空间配置http标签,权限?一个用于/loginwith UsernamePasswordAuthenticationFilter,另一个用于 REST url,带有自定义JwtAuthenticationFilter.

Does configuring two httpelements create two springSecurityFitlerChains? Is UsernamePasswordAuthenticationFilterturned off by default, until I declare form-login? How do I replace SecurityContextPersistenceFilterwith a filter which will obtain Authenticationfrom existing JWT-tokenrather than JSESSIONID?

配置两个http元素会创建两个springSecurityFitlerChains吗?是UsernamePasswordAuthenticationFilter默认是关闭的,直到我宣布form-login?如何替换SecurityContextPersistenceFilterAuthentication从现有JWT-token而不是从现有过滤器获得的过滤器JSESSIONID

回答by jlumietu

The Spring security filter chain is a very complex and flexible engine.

Spring 安全过滤器链是一个非常复杂和灵活的引擎。

Key filters in the chain are (in the order)

  • SecurityContextPersistenceFilter (restores Authentication from JSESSIONID)
  • UsernamePasswordAuthenticationFilter (performs authentication)
  • ExceptionTranslationFilter (catch security exceptions from FilterSecurityInterceptor)
  • FilterSecurityInterceptor (may throw authentication and authorization exceptions)

链中的关键过滤器是(按顺序)

  • SecurityContextPersistenceFilter(从 JSESSIONID 恢复身份验证)
  • UsernamePasswordAuthenticationFilter(执行身份验证)
  • ExceptionTranslationFilter(从 FilterSecurityInterceptor 捕获安全异常)
  • FilterSecurityInterceptor(可能抛出身份验证和授权异常)

Looking at the current stable release 4.2.1 documentation, section 13.3 Filter Orderingyou could see the whole filter chain's filter organization:

查看当前稳定版本 4.2.1 文档的13.3 过滤器排序部分,您可以看到整个过滤器链的过滤器组织:

13.3 Filter Ordering

The order that filters are defined in the chain is very important. Irrespective of which filters you are actually using, the order should be as follows:

  1. ChannelProcessingFilter, because it might need to redirect to a different protocol

  2. SecurityContextPersistenceFilter, so a SecurityContext can be set up in the SecurityContextHolder at the beginning of a web request, and any changes to the SecurityContext can be copied to the HttpSession when the web request ends (ready for use with the next web request)

  3. ConcurrentSessionFilter, because it uses the SecurityContextHolder functionality and needs to update the SessionRegistry to reflect ongoing requests from the principal

  4. Authentication processing mechanisms - UsernamePasswordAuthenticationFilter, CasAuthenticationFilter, BasicAuthenticationFilteretc - so that the SecurityContextHolder can be modified to contain a valid Authentication request token

  5. The SecurityContextHolderAwareRequestFilter, if you are using it to install a Spring Security aware HttpServletRequestWrapper into your servlet container

  6. The JaasApiIntegrationFilter, if a JaasAuthenticationTokenis in the SecurityContextHolder this will process the FilterChain as the Subject in the JaasAuthenticationToken

  7. RememberMeAuthenticationFilter, so that if no earlier authentication processing mechanism updated the SecurityContextHolder, and the request presents a cookie that enables remember-me services to take place, a suitable remembered Authentication object will be put there

  8. AnonymousAuthenticationFilter, so that if no earlier authentication processing mechanism updated the SecurityContextHolder, an anonymous Authentication object will be put there

  9. ExceptionTranslationFilter, to catch any Spring Security exceptions so that either an HTTP error response can be returned or an appropriate AuthenticationEntryPoint can be launched

  10. FilterSecurityInterceptor, to protect web URIs and raise exceptions when access is denied

13.3 过滤器排序

过滤器在链中定义的顺序非常重要。无论您实际使用哪种过滤器,顺序都应如下所示:

  1. ChannelProcessingFilter,因为它可能需要重定向到不同的协议

  2. SecurityContextPersistenceFilter,因此可以在 Web 请求开始时在 SecurityContextHolder 中设置 SecurityContext,并且可以在 Web 请求结束时将 SecurityContext 的任何更改复制到 HttpSession(准备用于下一个 Web 请求)

  3. ConcurrentSessionFilter,因为它使用 SecurityContextHolder 功能并且需要更新 SessionRegistry 以反映来自主体的正在进行的请求

  4. 身份验证处理机制 - UsernamePasswordAuthenticationFilterCasAuthenticationFilterBasicAuthenticationFilter等 - 以便可以修改 SecurityContextHolder 以包含有效的身份验证请求令牌

  5. SecurityContextHolderAwareRequestFilter,如果你使用它来安装一个Spring安全意识了HttpServletRequestWrapper到你的servlet容器

  6. JaasApiIntegrationFilter,如果JaasAuthenticationToken在SecurityContextHolder中,这将处理FilterChain作为JaasAuthenticationToken主题

  7. RememberMeAuthenticationFilter,这样如果没有更早的身份验证处理机制更新了 SecurityContextHolder,并且请求提供了一个 cookie 来启用记住我的服务,那么一个合适的记住的 Authentication 对象将被放置在那里

  8. AnonymousAuthenticationFilter,这样如果没有更早的认证处理机制更新 SecurityContextHolder,一个匿名 Authentication 对象将被放置在那里

  9. ExceptionTranslationFilter,用于捕获任何 Spring Security 异常,以便可以返回 HTTP 错误响应或可以启动适当的 AuthenticationEntryPoint

  10. FilterSecurityInterceptor,用于保护 Web URI 并在访问被拒绝时引发异常

Now, I'll try to go on by your questions one by one:

现在,我将尝试一一回答您的问题:

I'm confused how these filters are used. Is it that for the spring provided form-login, UsernamePasswordAuthenticationFilter is only used for /login, and latter filters are not? Does the form-login namespace element auto-configure these filters? Does every request (authenticated or not) reach FilterSecurityInterceptor for non-login url?

我很困惑如何使用这些过滤器。是不是spring提供的form-login,UsernamePasswordAuthenticationFilter只用于/login,后面的filter不是?form-login 命名空间元素是否会自动配置这些过滤器?每个请求(是否经过身份验证)是否都会到达 FilterSecurityInterceptor 以获取非登录 url?

Once you are configuring a <security-http>section, for each one you must at least provide one authentication mechanism. This must be one of the filters which match group 4 in the 13.3 Filter Ordering section from the Spring Security documentation I've just referenced.

一旦您配置了一个<security-http>部分,您必须至少为每个部分提供一种身份验证机制。这必须是与我刚刚引用的 Spring Security 文档中的 13.3 过滤器排序部分中的组 4 匹配的过滤器之一。

This is the minimum valid security:http element which can be configured:

这是可以配置的最小有效 security:http 元素:

<security:http authentication-manager-ref="mainAuthenticationManager" 
               entry-point-ref="serviceAccessDeniedHandler">
    <security:intercept-url pattern="/sectest/zone1/**" access="hasRole('ROLE_ADMIN')"/>
</security:http>

Just doing it, these filters are configured in the filter chain proxy:

就这么干吧,在过滤器链代理中配置了这些过滤器:

{
        "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"
    }

Note: I get them by creating a simple RestController which @Autowires the FilterChainProxy and returns it's contents:

注意:我通过创建一个简单的 RestController 来获取它们,它 @Autowires FilterChainProxy 并返回它的内容:

    @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;
    }

Here we could see that just by declaring the <security:http>element with one minimum configuration, all the default filters are included, but none of them is of a Authentication type (4th group in 13.3 Filter Ordering section). So it actually means that just by declaring the security:httpelement, the SecurityContextPersistenceFilter, the ExceptionTranslationFilter and the FilterSecurityInterceptor are auto-configured.

在这里我们可以看到,仅通过声明<security:http>具有一个最低配置的元素,所有默认过滤器都包括在内,但它们都不是身份验证类型(13.3 过滤器排序部分中的第 4 组)。所以它实际上意味着仅仅通过声明security:http元素,SecurityContextPersistenceFilter、ExceptionTranslationFilter 和 FilterSecurityInterceptor 是自动配置的。

In fact, one authentication processing mechanism should be configured, and even security namespace beans processing claims for that, throwing an error during startup, but it can be bypassed adding an entry-point-ref attribute in <http:security>

实际上,应该配置一个认证处理机制,甚至安全命名空间bean处理声明,在启动时抛出错误,但可以绕过它添加一个入口点引用属性 <http:security>

If I add a basic <form-login>to the configuration, this way:

如果我<form-login>在配置中添加一个基本的,这样:

<security:http authentication-manager-ref="mainAuthenticationManager">
    <security:intercept-url pattern="/sectest/zone1/**" access="hasRole('ROLE_ADMIN')"/>
    <security:form-login />
</security:http>

Now, the filterChain will be like this:

现在,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"
    }

Now, this two filters org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilterand org.springframework.security.web.authentication.ui.DefaultLoginPageGeneratingFilter are created and configured in the FilterChainProxy.

现在,这两个过滤器org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter和 org.springframework.security.web.authentication.ui.DefaultLoginPageGeneratingFilter 在 FilterChainProxy 中创建和配置。

So, now, the questions:

所以,现在,问题:

Is it that for the spring provided form-login, UsernamePasswordAuthenticationFilter is only used for /login, and latter filters are not?

是不是spring提供的form-login,UsernamePasswordAuthenticationFilter只用于/login,后面的filter不是?

Yes, it is used to try to complete a login processing mechanism in case the request matches the UsernamePasswordAuthenticationFilter url. This url can be configured or even changed it's behaviour to match every request.

是的,它用于在请求与 UsernamePasswordAuthenticationFilter url 匹配的情况下尝试完成登录处理机制。可以配置甚至更改此 url 的行为以匹配每个请求。

You could too have more than one Authentication processing mechanisms configured in the same FilterchainProxy (such as HttpBasic, CAS, etc).

您也可以在同一个 FilterchainProxy 中配置不止一种身份验证处理机制(例如 HttpBasic、CAS 等)。

Does the form-login namespace element auto-configure these filters?

form-login 命名空间元素是否会自动配置这些过滤器?

No, the form-login element configures the UsernamePasswordAUthenticationFilter, and in case you don't provide a login-page url, it also configures the org.springframework.security.web.authentication.ui.DefaultLoginPageGeneratingFilter, which ends in a simple autogenerated login page.

不,表单登录元素配置 UsernamePasswordAUthenticationFilter,如果您不提供登录页面 url,它还配置 org.springframework.security.web.authentication.ui.DefaultLoginPageGeneratingFilter,它以简单的自动生成登录结束页。

The other filters are auto-configured by default just by creating a <security:http>element with no security:"none"attribute.

其他过滤器默认自动配置,只需创建一个<security:http>没有security:"none"属性的元素。

Does every request (authenticated or not) reach FilterSecurityInterceptor for non-login url?

每个请求(是否经过身份验证)是否都会到达 FilterSecurityInterceptor 以获取非登录 url?

Every request should reach it, as it is the element which takes care of whether the request has the rights to reach the requested url. But some of the filters processed before might stop the filter chain processing just not calling FilterChain.doFilter(request, response);. For example, a CSRF filter might stop the filter chain processing if the request has not the csrf parameter.

每个请求都应该到达它,因为它是负责处理请求是否有权到达请求的 url 的元素。但是之前处理的一些过滤器可能会停止过滤器链处理,只是不调用FilterChain.doFilter(request, response);. 例如,如果请求没有 csrf 参数,CSRF 过滤器可能会停止过滤器链处理。

What if I want to secure my REST API with JWT-token, which is retrieved from login? I must configure two namespace configuration http tags, rights? Other one for /login with UsernamePasswordAuthenticationFilter, and another one for REST url's, with custom JwtAuthenticationFilter.

如果我想使用从登录中检索的 JWT 令牌来保护我的 REST API,该怎么办?我必须配置两个命名空间配置http标签,权限?另一个用于 /login with UsernamePasswordAuthenticationFilter,另一个用于 REST url,带有 custom JwtAuthenticationFilter

No, you are not forced to do this way. You could declare both UsernamePasswordAuthenticationFilterand the JwtAuthenticationFilterin the same http element, but it depends on the concrete behaviour of each of this filters. Both approaches are possible, and which one to choose finnally depends on own preferences.

不,您不是被迫这样做的。您可以在同一个 http 元素中同时声明UsernamePasswordAuthenticationFilterJwtAuthenticationFilter,但这取决于每个过滤器的具体行为。这两种方法都是可能的,最终选择哪一种取决于自己的喜好。

Does configuring two http elements create two springSecurityFitlerChains?

配置两个 http 元素是否会创建两个 springSecurityFitlerChains?

Yes, that's true

是的,这是真的

Is UsernamePasswordAuthenticationFilter turned off by default, until I declare form-login?

在我声明表单登录之前,用户名密码验证过滤器是否默认关闭?

Yes, you could see it in the filters raised in each one of the configs I posted

是的,您可以在我发布的每个配置中提出的过滤器中看到它

How do I replace SecurityContextPersistenceFilter with one, which will obtain Authentication from existing JWT-token rather than JSESSIONID?

如何将 SecurityContextPersistenceFilter 替换为一个,它将从现有的 JWT 令牌而不是 JSESSIONID 获取身份验证?

You could avoid SecurityContextPersistenceFilter, just configuring session strategyin <http:element>. Just configure like this:

你可以避开SecurityContextPersistenceFilter,只是配置会话策略<http:element>。只需像这样配置:

<security:http create-session="stateless" >

<security:http create-session="stateless" >

Or, In this case you could overwrite it with another filter, this way inside the <security:http>element:

或者,在这种情况下,您可以用另一个过滤器覆盖它,这样在<security:http>元素内部:

<security:http ...>  
   <security:custom-filter ref="myCustomFilter" position="SECURITY_CONTEXT_FILTER"/>    
</security:http>
<beans:bean id="myCustomFilter" class="com.xyz.myFilter" />

EDIT:

编辑:

One question about "You could too have more than one Authentication processing mechanisms configured in the same FilterchainProxy". Will the latter overwrite the authentication performed by first one, if declaring multiple (Spring implementation) authentication filters? How this relates to having multiple authentication providers?

关于“您也可以在同一个 FilterchainProxy 中配置多个身份验证处理机制”的问题。如果声明多个(Spring 实现)身份验证过滤器,后者是否会覆盖第一个执行的身份验证?这与拥有多个身份验证提供程序有何关系?

This finally depends on the implementation of each filter itself, but it's true the fact that the latter authentication filters at least are able to overwrite any prior authentication eventually made by preceding filters.

这最终取决于每个过滤器本身的实现,但事实是,后面的身份验证过滤器至少能够覆盖由前面的过滤器最终做出的任何先前的身份验证。

But this won't necesarily happen. I have some production cases in secured REST services where I use a kind of authorization token which can be provided both as a Http header or inside the request body. So I configure two filters which recover that token, in one case from the Http Header and the other from the request body of the own rest request. It's true the fact that if one http request provides that authentication token both as Http header and inside the request body, both filters will try to execute the authentication mechanism delegating it to the manager, but it could be easily avoided simply checking if the request is already authenticated just at the begining of the doFilter()method of each filter.

但这不一定会发生。我在安全 REST 服务中有一些生产案例,其中我使用了一种授权令牌,该令牌可以作为 Http 标头或在请求正文中提供。因此,我配置了两个过滤器来恢复该令牌,一种是从 Http Header 中恢复,另一种是从自己的 rest 请求的请求正文中恢复。确实,如果一个 http 请求在 Http 标头和请求正文中都提供身份验证令牌,则两个过滤器都将尝试执行将其委托给管理器的身份验证机制,但只需检查请求是否正确就可以轻松避免已经在doFilter()每个过滤器的方法开始时进行了身份验证。

Having more than one authentication filter is related to having more than one authentication providers, but don't force it. In the case I exposed before, I have two authentication filter but I only have one authentication provider, as both of the filters create the same type of Authentication object so in both cases the authentication manager delegates it to the same provider.

拥有多个身份验证过滤器与拥有多个身份验证提供程序有关,但不要强求。在我之前公开的情况下,我有两个身份验证过滤器,但我只有一个身份验证提供程序,因为这两个过滤器创建了相同类型的身份验证对象,因此在这两种情况下,身份验证管理器都将其委托给同一个提供程序。

And opposite to this, I too have a scenario where I publish just one UsernamePasswordAuthenticationFilter but the user credentials both can be contained in DB or LDAP, so I have two UsernamePasswordAuthenticationToken supporting providers, and the AuthenticationManager delegates any authentication attempt from the filter to the providers secuentially to validate the credentials.

与此相反,我也有一个场景,我只发布一个 UsernamePasswordAuthenticationFilter 但用户凭据都可以包含在 DB 或 LDAP 中,所以我有两个 UsernamePasswordAuthenticationToken 支持提供程序,并且 AuthenticationManager 将来自过滤器的任何身份验证尝试委托给提供程序依次验证凭据。

So, I think it's clear that neither the amount of authentication filters determine the amount of authentication providers nor the amount of provider determine the amount of filters.

所以,我认为很明显,身份验证过滤器的数量既不能决定身份验证提供者的数量,也不能决定过滤器的数量。

Also, documentation states SecurityContextPersistenceFilter is responsible of cleaning the SecurityContext, which is important due thread pooling. If I omit it or provide custom implementation, I have to implement the cleaning manually, right? Are there more similar gotcha's when customizing the chain?

此外,文档指出 SecurityContextPersistenceFilter 负责清理 SecurityContext,这是重要的线程池。如果我省略它或提供自定义实现,我必须手动实现清理,对吗?定制链时是否有更多类似的问题?

I did not look carefully into this filter before, but after your last question I've been checking it's implementation, and as usually in Spring, nearly everything could be configured, extended or overwrited.

我之前没有仔细研究过这个过滤器,但是在你最后一个问题之后,我一直在检查它的实现,并且通常在 Spring 中,几乎所有东西都可以配置、扩展或覆盖。

The SecurityContextPersistenceFilterdelegates in a SecurityContextRepositoryimplementation the search for the SecurityContext. By default, a HttpSessionSecurityContextRepositoryis used, but this could be changed using one of the constructors of the filter. So it may be better to write an SecurityContextRepository which fits your needs and just configure it in the SecurityContextPersistenceFilter, trusting in it's proved behaviour rather than start making all from scratch.

SecurityContextPersistenceFilter在代表SecurityContextRepository执行搜索SecurityContext中。默认情况下,使用HttpSessionSecurityContextRepository,但这可以使用过滤器的构造函数之一进行更改。因此,最好编写一个适合您需求的 SecurityContextRepository 并在 SecurityContextPersistenceFilter 中配置它,相信它已被证明的行为,而不是从头开始制作所有内容。

回答by chaoluo

UsernamePasswordAuthenticationFilteris only used for /login, and latter filters are not?

UsernamePasswordAuthenticationFilter仅用于/login,后面的过滤器不是?

No, UsernamePasswordAuthenticationFilterextends AbstractAuthenticationProcessingFilter, and this contains a RequestMatcher, that means you can define your own processing url, this filter only handle the RequestMatchermatches the request url, the default processing url is /login.

不,UsernamePasswordAuthenticationFilterextends AbstractAuthenticationProcessingFilter,它包含一个RequestMatcher,这意味着你可以定义自己的处理 url,这个过滤器只处理RequestMatcher匹配请求 url,默认处理 url 是/login.

Later filters can still handle the request, if the UsernamePasswordAuthenticationFilterexecutes chain.doFilter(request, response);.

如果UsernamePasswordAuthenticationFilterexecutes ,后面的过滤器仍然可以处理请求chain.doFilter(request, response);

More details about core fitlers

有关核心过滤器的更多详细信息

Does the form-login namespace element auto-configure these filters?

form-login 命名空间元素是否会自动配置这些过滤器?

UsernamePasswordAuthenticationFilteris created by <form-login>, these are Standard Filter Aliases and Ordering

UsernamePasswordAuthenticationFilter由 创建<form-login>,这些是标准过滤器别名和排序

Does every request (authenticated or not) reach FilterSecurityInterceptor for non-login url?

每个请求(是否经过身份验证)是否都会到达 FilterSecurityInterceptor 以获取非登录 url?

It depends on whether the before fitlers are successful, but FilterSecurityInterceptoris the last fitler normally.

这取决于之前的fitler是否成功,但FilterSecurityInterceptor通常是最后一个fitler。

Does configuring two http elements create two springSecurityFitlerChains?

配置两个 http 元素是否会创建两个 springSecurityFitlerChains?

Yes, every fitlerChain has a RequestMatcher, if the RequestMatchermatches the request, the request will be handled by the fitlers in the fitler chain.

是的,每个 fitlerChain 都有一个RequestMatcher,如果RequestMatcher匹配请求,则请求将由 fitler 链中的 fitler 处理。

The default RequestMatchermatches all request if you don't config the pattern, or you can config the specific url (<http pattern="/rest/**").

RequestMatcher如果您不配置模式,则默认匹配所有请求,或者您可以配置特定的 url ( <http pattern="/rest/**")。

If you want to konw more about the fitlers, I think you can check source code in spring security. doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain)

如果你想了解更多关于 fitlers 的信息,我想你可以在 spring security 中查看源代码。 doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain)

回答by PraveenKumar Lalasangi

Spring security is a filter based framework, it plants a WALL(HttpFireWall) before your application in terms of proxy filters or spring managed beans. Your request has to pass through multiple filters to reach your API.

Spring security 是一个基于过滤器的框架,它在您的应用程序之前植入一个 WALL(HttpFireWall) 代理过滤器或 Spring 管理的 bean。您的请求必须通过多个过滤器才能到达您的 API。

Sequence of execution in Spring Security

Spring Security 中的执行顺序

  1. WebAsyncManagerIntegrationFilterProvides integration between the SecurityContext and Spring Web's WebAsyncManager.

  2. SecurityContextPersistenceFilterThis filter will only execute once per request, Populates the SecurityContextHolder with information obtained from the configured SecurityContextRepository prior to the request and stores it back in the repository once the request has completed and clearing the context holder.
    Request is checked for existing session. If new request, SecurityContext will be created else if request has session then existing security-context will be obtained from respository.

  3. HeaderWriterFilterFilter implementation to add headers to the current response.

  4. LogoutFilterIf request url is /logout(for default configuration) or if request url mathces RequestMatcherconfigured in LogoutConfigurerthen

    • clears security context.
    • invalidates the session
    • deletes all the cookies with cookie names configured in LogoutConfigurer
    • Redirects to default logout success url /or logout success url configured or invokes logoutSuccessHandler configured.
  5. UsernamePasswordAuthenticationFilter

    • For any request url other than loginProcessingUrl this filter will not process further but filter chain just continues.
    • If requested URL is matches(must be HTTP POST) default /loginor matches .loginProcessingUrl()configured in FormLoginConfigurerthen UsernamePasswordAuthenticationFilterattempts authentication.
    • default login form parameters are username and password, can be overridden by usernameParameter(String), passwordParameter(String).
    • setting .loginPage()overrides defaults
    • While attempting authentication
      • an Authenticationobject(UsernamePasswordAuthenticationTokenor any implementation of Authenticationin case of your custom auth filter) is created.
      • and authenticationManager.authenticate(authToken)will be invoked
      • Note that we can configure any number of AuthenticationProviderauthenticate method tries all auth providers and checks any of the auth provider supportsauthToken/authentication object, supporting auth provider will be used for authenticating. and returns Authentication object in case of successful authentication else throws AuthenticationException.
    • If authentication success session will be created and authenticationSuccessHandlerwill be invoked which redirects to the target url configured(default is /)
    • If authentication failed user becomes un-authenticated user and chain continues.
  6. SecurityContextHolderAwareRequestFilter, if you are using it to install a Spring Security aware HttpServletRequestWrapper into your servlet container

  7. AnonymousAuthenticationFilterDetects if there is no Authentication object in the SecurityContextHolder, if no authentication object found, creates Authenticationobject (AnonymousAuthenticationToken) with granted authority ROLE_ANONYMOUS. Here AnonymousAuthenticationTokenfacilitates identifying un-authenticated users subsequent requests.

  1. WebAsyncManagerIntegrationFilter提供 SecurityContext 和 Spring Web 的 WebAsyncManager 之间的集成。

  2. SecurityContextPersistenceFilter这个过滤器每个请求只会执行一次,在请求之前使用从配置的 SecurityContextRepository 获得的信息填充 SecurityContextHolder,并在请求完成并清除上下文持有者后将其存储回存储库。
    检查现有会话的请求。如果是新请求,则 SecurityContext 将被创建,否则,如果请求有会话,则现有的安全上下文将从 respository 获取

  3. HeaderWriterFilter过滤器实现以向当前响应添加标头。

  4. LogoutFilter如果请求 url 是/logout(对于默认配置)或者如果请求 url mathcesRequestMatcher配置在LogoutConfigurer然后

    • 清除安全上下文。
    • 使会话无效
    • 删除所有配置了 cookie 名称的 cookie LogoutConfigurer
    • 重定向到配置的默认注销成功 url /或注销成功 url 或调用配置的 logoutSuccessHandler。
  5. UsernamePasswordAuthenticationFilter

    • 对于 loginProcessingUrl 以外的任何请求 url,此过滤器不会进一步处理,但过滤器链会继续。
    • 如果请求的 URL 是匹配(必须是HTTP POST)默认值/login或匹配.loginProcessingUrl()配置,FormLoginConfigurerUsernamePasswordAuthenticationFilter尝试进行身份验证。
    • 默认的登录表单参数是用户名和密码,可以通过覆盖usernameParameter(String)passwordParameter(String)
    • 设置.loginPage()覆盖默认值
    • 在尝试身份验证时
      • 创建一个Authentication对象(UsernamePasswordAuthenticationTokenAuthentication在自定义身份验证过滤器的情况下的任何实现)。
      • 并且authenticationManager.authenticate(authToken)将被调用
      • 请注意,我们可以配置任意数量的AuthenticationProvider身份验证方法来尝试所有身份验证提供程序并检查任何身份验证提供程序的supportsauthToken/身份验证对象,支持身份验证提供程序将用于身份验证。并在身份验证成功的情况下返回 Authentication 对象 else throws AuthenticationException
    • 如果身份验证成功会话将被创建并被authenticationSuccessHandler调用,它会重定向到配置的目标 url(默认为/
    • 如果身份验证失败的用户成为未经身份验证的用户并且链继续。
  6. SecurityContextHolderAwareRequestFilter,如果您使用它来将 Spring Security 感知 HttpServletRequestWrapper 安装到您的 servlet 容器中

  7. AnonymousAuthenticationFilter检测 SecurityContextHolder 中是否没有 Authentication 对象,如果没有找到身份验证对象,则创建具有授权权限的Authentication对象 ( AnonymousAuthenticationToken) ROLE_ANONYMOUS。这里AnonymousAuthenticationToken有助于识别未经身份验证的用户后续请求。

Debug logs调试日志
DEBUG - /app/admin/app-config at position 9 of 12 in additional filter chain; firing Filter: 'AnonymousAuthenticationFilter'
DEBUG - Populated SecurityContextHolder with anonymous token: 'org.springframework.security.authentication.AnonymousAuthenticationToken@aeef7b36: Principal: anonymousUser; Credentials: [PROTECTED]; Authenticated: true; Details: org.springframework.security.web.authentication.WebAuthenticationDetails@b364: RemoteIpAddress: 0:0:0:0:0:0:0:1; SessionId: null; Granted Authorities: ROLE_ANONYMOUS' 
  1. ExceptionTranslationFilter, to catch any Spring Security exceptions so that either an HTTP error response can be returned or an appropriate AuthenticationEntryPoint can be launched

  2. FilterSecurityInterceptor
    There will be FilterSecurityInterceptorwhich comes almost last in the filter chain which gets Authentication object from SecurityContextand gets granted authorities list(roles granted) and it will make a decision whether to allow this request to reach the requested resource or not, decision is made by matching with the allowed AntMatchersconfigured in HttpSecurityConfiguration.

  1. ExceptionTranslationFilter, 捕获任何 Spring Security 异常,以便可以返回 HTTP 错误响应或可以启动适当的 AuthenticationEntryPoint

  2. FilterSecurityInterceptor
    会有FilterSecurityInterceptor哪些进来,从获得认证对象的过滤器链几乎是最后的SecurityContext和被授予的权限列表(角色授予),它会做出决定是否允许该请求到达请求的资源与否,决定由匹配与制造中AntMatchers配置的允许HttpSecurityConfiguration

Consider the exceptions 401-UnAuthorized and 403-Forbidden. These decisions will be done at the last in the filter chain

考虑例外 401-UnAuthorized 和 403-Forbidden。这些决定将在过滤器链的最后完成

  • Un authenticated user trying to access public resource - Allowed
  • Un authenticated user trying to access secured resource - 401-UnAuthorized
  • Authenticated user trying to access restricted resource(restricted for his role) - 403-Forbidden
  • 未经身份验证的用户尝试访问公共资源 -允许
  • 未经身份验证的用户尝试访问受保护的资源 - 401-UnAuthorized
  • 经过身份验证的用户尝试访问受限资源(受其角色限制)- 403-禁止访问


Note: User Request flows not only in above mentioned filters, but there are others filters too not shown here.(ConcurrentSessionFilter,RequestCacheAwareFilter,SessionManagementFilter...)
It will be different when you use your custom auth filter instead of UsernamePasswordAuthenticationFilter.
It will be different if you configure JWT auth filter and omit .formLogin() i.e, UsernamePasswordAuthenticationFilterit will become entirely different case.

注意:用户请求不仅在上述过滤器中流动,还有其他过滤器也未在此处显示。( ConcurrentSessionFilter, RequestCacheAwareFilter, SessionManagementFilter...)
当您使用自定义身份验证过滤器而不是UsernamePasswordAuthenticationFilter.
如果您配置 JWT auth 过滤器并省略.formLogin() i.e, UsernamePasswordAuthenticationFilter它,情况会有所不同,它将成为完全不同的情况。



仅供参考。spring-web 和 spring-security 中的过滤器


注意:refer package name in pic在 pic 中引用包名称,因为还有一些来自 orm 的其他过滤器和我自定义实现的过滤器。

enter image description here

在此处输入图片说明

From Documentationordering of filters is given as

  • ChannelProcessingFilter
  • ConcurrentSessionFilter
  • SecurityContextPersistenceFilter
  • LogoutFilter
  • X509AuthenticationFilter
  • AbstractPreAuthenticatedProcessingFilter
  • CasAuthenticationFilter
  • UsernamePasswordAuthenticationFilter
  • ConcurrentSessionFilter
  • OpenIDAuthenticationFilter
  • DefaultLoginPageGeneratingFilter
  • DefaultLogoutPageGeneratingFilter
  • ConcurrentSessionFilter
  • DigestAuthenticationFilter
  • BearerTokenAuthenticationFilter
  • BasicAuthenticationFilter
  • RequestCacheAwareFilter
  • SecurityContextHolderAwareRequestFilter
  • JaasApiIntegrationFilter
  • RememberMeAuthenticationFilter
  • AnonymousAuthenticationFilter
  • SessionManagementFilter
  • ExceptionTranslationFilter
  • FilterSecurityInterceptor
  • SwitchUserFilter

从文档中,过滤器的顺序为

  • 通道处理过滤器
  • 并发会话过滤器
  • 安全上下文持久性过滤器
  • 注销过滤器
  • X509认证过滤器
  • AbstractPreAuthenticatedProcessingFilter
  • CasAuthenticationFilter
  • 用户名密码验证过滤器
  • 并发会话过滤器
  • OpenID认证过滤器
  • DefaultLoginPageGeneratingFilter
  • DefaultLogoutPageGeneratingFilter
  • 并发会话过滤器
  • 摘要认证过滤器
  • BearerTokenAuthenticationFilter
  • 基本认证过滤器
  • 请求缓存感知过滤器
  • SecurityContextHolderAwareRequestFilter
  • JaasApi集成过滤器
  • 记住我身份验证过滤器
  • 匿名身份验证过滤器
  • 会话管理过滤器
  • 异常翻译过滤器
  • 过滤安全拦截器
  • 切换用户过滤器

You can also refer
most common way to authenticate a modern web app?
difference between authentication and authorization in context of Spring Security?

您还可以参考
对现代 Web 应用程序进行身份验证的最常用方法吗?
Spring Security 上下文中的身份验证和授权之间的区别?