1. 前言
上一文我们使用
Spring Security
实现了各种登录聚合的场面。其中我们是通过在
UsernamePasswordAuthenticationFilter
之前一个自定义的过滤器实现的。我怎么知道自定义过滤器要加在
UsernamePasswordAuthenticationFilter
之前。我在这个系列开篇说了
Spring Security
权限控制的一个核心关键就是
过滤器链
,这些过滤器如下图进行过滤传递,甚至比这个更复杂!这只是一个最小单元。
Spring Security 内置了一些过滤器,他们各有各的本事。如果你掌握了这些过滤器,很多实际开发中的需求和问题都很容易解决。今天我们来见识一下这些内置的过滤器。
2. 内置过滤器初始化
在
Spring Security
初始化核心过滤器时
HttpSecurity
会通过将
Spring Security
内置的一些过滤器以
FilterComparator
提供的规则进行比较按照比较结果进行排序注册。
2.1 排序规则
FilterComparator
维护了一个顺序的注册表
filterToOrder
。
FilterComparator() {
Step order = new Step(INITIAL_ORDER, ORDER_STEP);
put(ChannelProcessingFilter.class, order.next());
put(ConcurrentSessionFilter.class, order.next());
put(WebAsyncManagerIntegrationFilter.class, order.next());
put(SecurityContextPersistenceFilter.class, order.next());
put(HeaderWriterFilter.class, order.next());
put(CorsFilter.class, order.next());
put(CsrfFilter.class, order.next());
put(LogoutFilter.class, order.next());
filterToOrder.put(
"org.springframework.security.oauth2.client.web.OAuth2AuthorizationRequestRedirectFilter",
order.next());
filterToOrder.put(
"org.springframework.security.saml2.provider.service.servlet.filter.Saml2WebSsoAuthenticationRequestFilter",
order.next());
put(X509AuthenticationFilter.class, order.next());
put(AbstractPreAuthenticatedProcessingFilter.class, order.next());
filterToOrder.put("org.springframework.security.cas.web.CasAuthenticationFilter",
order.next());
filterToOrder.put(
"org.springframework.security.oauth2.client.web.OAuth2LoginAuthenticationFilter",
order.next());
filterToOrder.put(
"org.springframework.security.saml2.provider.service.servlet.filter.Saml2WebSsoAuthenticationFilter",
order.next());
put(UsernamePasswordAuthenticationFilter.class, order.next());
put(ConcurrentSessionFilter.class, order.next());
filterToOrder.put(
"org.springframework.security.openid.OpenIDAuthenticationFilter", order.next());
put(DefaultLoginPageGeneratingFilter.class, order.next());
put(DefaultLogoutPageGeneratingFilter.class, order.next());
put(ConcurrentSessionFilter.class, order.next());
put(DigestAuthenticationFilter.class, order.next());
filterToOrder.put(
"org.springframework.security.oauth2.server.resource.web.BearerTokenAuthenticationFilter", order.next());
put(BasicAuthenticationFilter.class, order.next());
put(RequestCacheAwareFilter.class, order.next());
put(SecurityContextHolderAwareRequestFilter.class, order.next());
put(JaasApiIntegrationFilter.class, order.next());
put(RememberMeAuthenticationFilter.class, order.next());
put(AnonymousAuthenticationFilter.class, order.next());
filterToOrder.put(
"org.springframework.security.oauth2.client.web.OAuth2AuthorizationCodeGrantFilter",
order.next());
put(SessionManagementFilter.class, order.next());
put(ExceptionTranslationFilter.class, order.next());
put(FilterSecurityInterceptor.class, order.next());
put(SwitchUserFilter.class, order.next());
}
复制代码
这些就是所有内置的过滤器。 他们是通过下面的方法获取自己的序号:
private Integer getOrder(Class<?> clazz) {
while (clazz != null) {
Integer result = filterToOrder.get(clazz.getName());
if (result != null) {
return result;
}
clazz = clazz.getSuperclass();
}
return null;
}
复制代码
通过过滤器的类全限定名从注册表
filterToOrder
中获取自己的序号,如果没有直接获取到序号通过递归获取父类在注册表中的序号作为自己的序号,序号越小优先级越高。上面的过滤器并非全部会被初始化。有的需要额外引入一些功能包,有的看
HttpSecurity
的配置情况。
在上一篇
文章
中。我们禁用了
CSRF
功能,就意味着
CsrfFilter
不会被注册。
3. 内置过滤器讲解
接下来我们就对这些内置过滤器进行一个系统的认识。 我们将按照默认顺序进行讲解 。
3.1 ChannelProcessingFilter
ChannelProcessingFilter
通常是用来过滤哪些请求必须用
https
协议, 哪些请求必须用
http
协议, 哪些请求随便用哪个协议都行。它主要有两个属性:
-
ChannelDecisionManager
用来判断请求是否符合既定的协议规则。它维护了一个ChannelProcessor
列表 这些ChannelProcessor
是具体用来执行ANY_CHANNEL
策略 (任何通道都可以),REQUIRES_SECURE_CHANNEL
策略 (只能通过https
通道),REQUIRES_INSECURE_CHANNEL
策略 (只能通过http
通道)。 -
FilterInvocationSecurityMetadataSource
用来存储 url 与 对应的ANY_CHANNEL
、REQUIRES_SECURE_CHANNEL
、REQUIRES_INSECURE_CHANNEL
的映射关系。
ChannelProcessingFilter
通过
HttpScurity#requiresChannel()
等相关方法引入其配置对象
ChannelSecurityConfigurer
来进行配置。
3.2 ConcurrentSessionFilter
ConcurrentSessionFilter
主要用来判断
session
是否过期以及更新最新的访问时间。其流程为:
-
session
检测,如果不存在直接放行去执行下一个过滤器。存在则进行下一步。 -
根据
sessionid
从SessionRegistry
中获取SessionInformation
,从SessionInformation
中获取session
是否过期;没有过期则更新SessionInformation
中的访问日期; 如果过期,则执行doLogout()
方法,这个方法会将session
无效,并将SecurityContext
中的Authentication
中的权限置空,同时在SecurityContenxtHoloder
中清除SecurityContext
然后查看是否有跳转的expiredUrl
,如果有就跳转,没有就输出提示信息。
ConcurrentSessionFilter
通过
SessionManagementConfigurer
来进行配置。
3.3 WebAsyncManagerIntegrationFilter
WebAsyncManagerIntegrationFilter
用于集成SecurityContext到Spring异步执行机制中的WebAsyncManager。用来处理异步请求的安全上下文。具体逻辑为:
-
从请求属性上获取所绑定的
WebAsyncManager
,如果尚未绑定,先做绑定。 -
从
asyncManager
中获取key
为CALLABLE_INTERCEPTOR_KEY
的安全上下文多线程处理器SecurityContextCallableProcessingInterceptor
, 如果获取到的为null
, 新建一个SecurityContextCallableProcessingInterceptor
并绑定CALLABLE_INTERCEPTOR_KEY
注册到asyncManager
中。
这里简单说一下
SecurityContextCallableProcessingInterceptor
。它实现了接口
CallableProcessingInterceptor
,
当它被应用于一次异步执行时,
beforeConcurrentHandling()
方法会在调用者线程执行,该方法会相应地从当前线程获取
SecurityContext
,然后被调用者线程中执行逻辑时,会使用这个
SecurityContext
,从而实现安全上下文从调用者线程到被调用者线程的传输。
WebAsyncManagerIntegrationFilter
通过
WebSecurityConfigurerAdapter#getHttp()
方法添加到
HttpSecurity
中成为
DefaultSecurityFilterChain
的一个链节。
3.4 SecurityContextPersistenceFilter
SecurityContextPersistenceFilter
主要控制
SecurityContext
的在一次请求中的生命周期 。 请求来临时,创建
SecurityContext
安全上下文信息,请求结束时清空
SecurityContextHolder
。