Spring 安全过滤器(Security Filter)是如何串联起来生效的

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

串联中的主要过滤器如下(按顺序)

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

我不明白这些过滤器是如何使用的。是否对于spring提供的form-login, UsernamePasswordAuthenticationFilter只用于/登录,而后面的过滤器不是?登录名称空间元素是否自动配置这些过滤器?是否每个请求(验证与否)到达FilterSecurityInterceptor非登录url?

如果我想用JWT-token来保护我的REST API,这是从登录中检索的?我必须配置两个命名空间配置http标签,权利?一个用于带有UsernamePasswordAuthenticationFilter/登录,另一个用于带有自定义JwtAuthenticationFilter的REST url。

配置两个http元素是否会创建两个springSecurityFitlerChains?在我声明form-login之前,UsernamePasswordAuthenticationFilter是否默认关闭?我如何用一个过滤器替换SecurityContextPersistenceFilter,它将从现有的JWT-token而不是JSESSIONID中获得Authentication ?

192736 次浏览

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

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

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

关于核心fitlers的更多细节

表单登录名称空间元素是否自动配置这些过滤器?

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

是否每个请求(验证与否)达到FilterSecurityInterceptor非登录url?

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

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

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

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

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

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

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

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

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

13.3过滤器排序

过滤器在链中定义的顺序非常重要。 不管您实际使用的是哪种过滤器,顺序应该是

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

  2. < em > SecurityContextPersistenceFilter < / em >,因此SecurityContext可以在web请求开始时的SecurityContextHolder中设置,并且 任何对SecurityContext的更改都可以复制到HttpSession 当web请求结束(准备与下一个web请求一起使用)

  3. ConcurrentSessionFilter,因为它使用SecurityContextHolder功能,需要更新SessionRegistry以反映来自主体的持续请求

  4. 认证处理机制- < em > UsernamePasswordAuthenticationFilter < / em > < em > CasAuthenticationFilter < / em >, < em > BasicAuthenticationFilter < / em >等-这样SecurityContextHolder可以 修改为包含有效的Authentication request token

  5. < em > SecurityContextHolderAwareRequestFilter < / em >,如果你用它来安装一个Spring Security感知HttpServletRequestWrapper到你的 李servlet容器< / p > < / > < em > JaasApiIntegrationFilter < / em >,如果一个JaasAuthenticationToken在SecurityContextHolder中,这将处理FilterChain作为 JaasAuthenticationToken中的Subject

  6. < em > RememberMeAuthenticationFilter < / em >,这样如果没有早期的身份验证处理机制更新SecurityContextHolder, 该请求提供了一个cookie,使“记住我”服务能够 发生时,一个合适的记住的身份验证对象将被放置 李有< / p > < / >

  7. < em > AnonymousAuthenticationFilter < / em >,这样如果没有早期的身份验证处理机制更新SecurityContextHolder, 一个匿名身份验证对象将放在

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

  9. FilterSecurityInterceptor,用于保护web uri,并在访问被拒绝时引发异常

下面,我将逐一回答大家的问题:

我很困惑这些过滤器是如何使用的。是春天用的吗 提供form-login, UsernamePasswordAuthenticationFilter只使用 对于/login,和后面的过滤器不是?表单登录名称空间 元素自动配置这些过滤器?是否每个请求 (验证与否)达到FilterSecurityInterceptor的非登录 网址吗?< / p >

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

这是最小有效的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"
}

注意:我通过创建一个简单的RestController来获得它们,@Autowires the 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;
}

在这里,我们可以看到,只要用一个最小配置声明<security:http>元素,就包括了所有默认过滤器,但没有一个是Authentication类型的(13.3过滤器排序部分的第4组)。因此,这实际上意味着只要声明security:http元素,SecurityContextPersistenceFilter、ExceptionTranslationFilter和FilterSecurityInterceptor就会自动配置。

事实上,应该配置一种身份验证处理机制,甚至安全名称空间bean处理声明也会在启动时抛出错误,但可以在<http:security>中添加入口点-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"
}

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

现在,问题来了:

对于spring提供的form-login, UsernamePasswordAuthenticationFilter仅用于/login,和 后面的过滤器不是吗?< / p >

是的,它用于尝试完成登录处理机制,以防请求匹配UsernamePasswordAuthenticationFilter url。这个url可以配置,甚至改变它的行为来匹配每个请求。

你也可以在同一个FilterchainProxy中配置多个身份验证处理机制(如HttpBasic、CAS等)。

表单登录名称空间元素是否自动配置这些过滤器?

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

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

是否每个请求(验证与否)达到FilterSecurityInterceptor非登录url?

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

如果我想用从登录中检索的jwt -令牌来保护我的REST API,该怎么办?我必须配置两个名称空间配置http标签,权利?另一个用于/login,使用UsernamePasswordAuthenticationFilter,另一个用于REST url,使用自定义JwtAuthenticationFilter

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

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

是的,那是真的

是UsernamePasswordAuthenticationFilter默认关闭,直到我声明窗体登录?

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

我如何将SecurityContextPersistenceFilter替换为一个,它将从现有的JWT-token而不是JSESSIONID获得身份验证?

你可以避免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中配置多个身份验证处理机制”的问题。如果声明多个(Spring实现)身份验证过滤器,后者是否会覆盖第一个所执行的身份验证?这与拥有多个身份验证提供者有什么关系?

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

但这并不一定会发生。我在安全REST服务中有一些生产案例,其中我使用了一种授权令牌,它可以作为Http报头提供,也可以在请求体中提供。所以我配置了两个过滤器来恢复那个令牌,一个是从Http Header中恢复,另一个是从rest请求的请求体中恢复。事实上,如果一个http请求同时作为http报头和请求体内部提供身份验证令牌,两个过滤器都将尝试执行将其委托给管理器的身份验证机制,但它可以很容易地避免,只需在每个过滤器的doFilter()方法的开头检查请求是否已经经过身份验证。

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

与此相反,我也有一个场景,我只发布一个UsernamePasswordAuthenticationFilter,但用户凭据都可以包含在DB或LDAP中,因此我有两个UsernamePasswordAuthenticationToken支持提供者,AuthenticationManager将过滤器的任何身份验证尝试安全地委托给提供者以验证凭据。

因此,我认为很明显,身份验证过滤器的数量并不决定身份验证提供者的数量,提供者的数量也不决定过滤器的数量。

此外,文档声明SecurityContextPersistenceFilter负责清理SecurityContext,这对于线程池来说很重要。如果我省略它或提供自定义实现,我必须手动实现清洗,对吗?在定制链时是否有更多类似的陷阱?

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

SecurityContextPersistenceFilterSecurityContextRepository实现中委托对SecurityContext的搜索。默认情况下,使用HttpSessionSecurityContextRepository,但这可以使用过滤器的一个构造函数来更改。因此,最好是编写一个符合您需求的SecurityContextPersistenceFilter并在SecurityContextPersistenceFilter中配置它,相信它已被证明的行为,而不是从头开始。

Spring安全是一个基于过滤器的框架,它在你的应用程序之前以代理过滤器或Spring托管bean的方式种植了一个WALL(HttpFireWall)。请求必须通过多个过滤器才能到达API。

Spring Security的执行顺序

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

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

  3. HeaderWriterFilter为当前响应添加报头的过滤器实现。

  4. LogoutFilter如果请求url是/logout(默认配置),或者如果请求url匹配LogoutConfigurer中配置的RequestMatcher,则

  • 清除安全上下文。
  • 使会话失效
  • 删除在LogoutConfigurer中配置的cookie名称的所有cookie
  • 重定向到默认注销成功url /或配置的注销成功url或调用配置的logoutSuccessHandler。
  1. UsernamePasswordAuthenticationFilter
  • 对于loginProcessingUrl以外的任何请求url,过滤器将不会进一步处理,但过滤器链只是继续。
  • 如果请求的URL匹配(必须是HTTP POST)默认的/login或匹配FormLoginConfigurer中配置的.loginProcessingUrl(),则UsernamePasswordAuthenticationFilter尝试身份验证。
  • 默认的登录表单参数是用户名和密码,可以被usernameParameter(String)passwordParameter(String)覆盖。
  • 设置.loginPage() 覆盖默认值
  • 尝试验证时
    • 创建一个Authentication对象(在自定义认证过滤器的情况下,UsernamePasswordAuthenticationTokenAuthentication的任何实现)。
    • authenticationManager.authenticate(authToken)将被调用
    • 注意,我们可以配置任意数量的AuthenticationProvider身份验证方法,尝试所有的身份验证提供程序并检查任意的身份验证提供程序< >强supports < / >强 authToken/authentication对象,支持的身份验证提供程序将用于身份验证。,如果身份验证成功,则返回Authentication对象,否则抛出AuthenticationException
  • 如果将创建身份验证成功会话,并且将调用authenticationSuccessHandler,它将重定向到配置的目标url(默认为/)
  • 如果认证失败,用户将成为未认证的用户,链将继续。
  1. SecurityContextHolderAwareRequestFilter,如果你用它来安装一个Spring安全感知HttpServletRequestWrapper到你的servlet容器

  2. AnonymousAuthenticationFilter检测SecurityContextHolder中是否没有认证对象,如果没有发现认证对象,则创建具有授予权限ROLE_ANONYMOUSAuthentication对象(AnonymousAuthenticationToken)。这里AnonymousAuthenticationToken便于识别未经身份验证的用户后续请求

调试日志

.
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,捕获任何Spring Security异常,以便可以返回HTTP错误响应或启动适当的AuthenticationEntryPoint

  2. <李> < p > FilterSecurityInterceptor < br > 将会有FilterSecurityInterceptor,它几乎位于过滤器链的最后,它从SecurityContext获取身份验证对象,并获得授予的权限列表(授予的角色),它将决定是否允许该请求到达所请求的资源,该决定是通过匹配HttpSecurityConfiguration中配置的允许的AntMatchers来做出的

考虑例外情况401-UnAuthorized和403-Forbidden。这些决定将在过滤器链的最后一个
执行

  • 未验证的用户试图访问公共资源- 允许
  • 未验证的用户试图访问安全资源- 401 -未经授权
  • 通过身份验证的用户试图访问受限资源(对其角色受限)- 403年被禁止的

注意:用户请求流不仅在上述过滤器中,还有其他过滤器,这里没有显示。(ConcurrentSessionFilterRequestCacheAwareFilterSessionManagementFilter…)
当你使用自定义认证过滤器而不是UsernamePasswordAuthenticationFilter时,情况会有所不同 如果你配置了JWT认证过滤器并省略.formLogin() i.e, UsernamePasswordAuthenticationFilter,情况就会完全不同
仅供参考。spring-web和spring-security中的过滤器
注意:**引用pic**中的包名,因为还有一些来自orm和我的自定义实现的过滤器。

enter image description here

从文档过滤器的排序为

  • 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

你也可以参考
最常见的验证现代web应用程序的方法是什么? < br > Spring Security上下文中的身份验证和授权之间的区别? < / p >