持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第11天,点击查看活动详情
Spring Security过滤器链的构建
Spring Security过滤器链的构建主要角色是HttpSecurity,它的主要作用就是创建一个 SecurityFilterChain对象,再进一步其实就是DefaultSecurityFilterChain 对象,它包含一个路径匹配器以及众多的Spring Security过滤器。另外一个比较重要的角色是WebSecurity,WebSecurity 则是在一个更大的层面上去构建过滤器。可以这样理解,一个HttpSecurity管理一个过滤器链,而WebSecurity可以管理多个HttpSecurity对象还有一些其他的被忽略的请求等功能。
1.HttpSecurity
在HttpSecurity中,将各种收集起来的Configurer配置类保存到HttpSecurity的父类AbstractConfiguredSecurityBuilder的配置类集合configurers变量中。然后,在后续的过滤器链中,再通过这些Configurer配置类构建出具体的Spring Security过滤器,并把它们添加到HttpSecurity的filters对象中。在HtpSecurity中,已有的方法的功能大致比较类似,存在大量功能类似的方法,就举常见的form表单登陆的例子来说明一下HttpSecurity的配置原理,代码如下:
public FormLoginConfigurer<HttpSecurity> formLogin() throws Exception {
return getOrApply(new FormLoginConfigurer<>());
}
public HttpSecurity formLogin(Customizer<FormLoginConfigurer<HttpSecurity>> formLoginCustomizer) throws Exception {
formLoginCustomizer.customize(getOrApply(new FormLoginConfigurer<>()));
return HttpSecurity.this;
}
public HttpSecurity authenticationProvider (
AuthenticationProvider authenticationProvider){
getAuthenticationRegistry ()
.authenticationProvider(authenticationProvider);
return this;
}
private AuthenticationManagerBuilder getAuthenticationRegistry() {
return getSharedObject(AuthenticationManagerBuilder.class);
}
@Override
public HttpSecurity addFilterAfter(Filter filter, Class<? extends Filter> afterFilter) {
return addFilterAtOffsetOf(filter, 1, afterFilter);
}
@Override
public HttpSecurity addFilterBefore(Filter filter, Class<? extends Filter> beforeFilter) {
return addFilterAtOffsetOf(filter, -1, beforeFilter);
}
private HttpSecurity addFilterAtOffsetOf(Filter filter, int offset, Class<? extends Filter> registeredFilter) {
int order = this.filterOrders.getOrder(registeredFilter) + offset;
this.filters.add(new OrderedFilter(filter, order));
this.filterOrders.put(filter.getClass(), order);
return this;
}
public HttpSecurity addFilter(Filter filter) {
Integer order = this.filterOrders.getOrder(filter.getClass());
if (order == null) {
throw new IllegalArgumentException("The Filter class " + filter.getClass().getName()
+ " does not have a registered order and cannot be added without a specified order. Consider using addFilterBefore or addFilterAfter instead.");
}
this.filters.add(new OrderedFilter(filter, order));
return this;
}
public HttpSecurity addFilterAt(Filter filter, Class<? extends Filter> atFilter) {
return addFilterAtOffsetOf(filter, 0, atFilter);
}
- 以form表单登录配置为例,在HttpSecurity中有两个重载方法可以进行配置:
第一个是一个无参的formLogin 方法,该方法的返回值是一个FormLoginConfigurer对象,开发者可以在该对象的基础上继续完善对form表单的配置,我们在前面章节中配置的表单登录都是通过这种方式来进行配置的。
第二个是一个有参的formLogin方法,该方法的参数是一个FormLoginConfigurer 对象,返回值则是一个HtpSecurity 对象,也就是说开发者可以提前在外面配置好FormLoginConfigurer 对象,然后直接传进来进行配置即可,返回值HttpSecurity对象则可以在方法返回后直接进行其他过滤器的配置。
无论是有参还是无参,最终都会调用到getOrApply方法,该方法会调用父类的getConfigurer方法去查看是否已经有对应的配置类了,如果有,则直接返回;如果没有,则调用apply方法添加到父类的configurers变量中,所以,其他过滤器的配置都和form表单登录配置类似。 - 每一套过滤器链都会有对应的AuthenticationManager对象来进行认证操作(如果认证失败,则会调用AuthenticationManager的parent再次进行认证),主要是通过authenticationProvider方法配置执行认证的authenticationProvider 对象,通过userDetailService方法配置UserDetailsService,最后在beforeConfigure方法中触发AuthenticationManager对象的构建。
- performBuild 方法则是进行DefaultSecurityFilterChain 对象的构建,传入请求匹配器和过滤器集合filters, 在构建之前,Spring Security会先按照既定的顺序对filters 进行排序。
- 通过addFiterAfter、addFilterBefore 两个方法,我们可以在某一个过滤器之后或者之前添加一个自定义的过滤器。
- addFiter方法可以向过滤器链中添加一个过滤器 ,这个过滤器必须是Spring Security框架提供的过滤器的一个实例或者其扩展。实际上,在每个对应的Configurer配置类里的的configure方法中,都会调用addFilter方法将构建好的过滤器添加到HttpSecurity中的filters集合中。
- addFilterAt 方法可以替换掉原来过滤器链里指定的一个过滤器。需要注意的是,在同一个位置添加多个过滤器并不会覆盖现有的过滤器。
2.WebSecurity
一个HttpSecurity对象会构建一个过滤器链对象,而一个项目中可以存在多个过滤器链,WebSecurity负责将HttpSecurity所构建的DefaultSecurityFilterChain对象(可能有多个),连同其他某些需要忽略的请求,再次重新构建为一个FilterChainProxy对象,同时添加上HTTP防火墙等。
首先,在WebSecurity中维护了一个比较重要的集合---ignoredRequests ,这个集合中保存了所有被忽略的请求,因为在实际项目中,不是所有的请求都需要经过Spring Security过滤器链,因为有某些资源是不需要认证授权的,可以直接返回给客户端,ignoredRequests就将这些需要被忽略的请求保存起来。
然后,WebSecurity声明了一个securityFilterChainBuilders 集合,该集合用来保存所有的
HtpSecurity对象,每一个HttpSecurity对象创建成功之后,通过addSecurityFiterChainBuilder
方法将HtpSecurity对象添加到securityFilterChainBuilders集合中。
最重要的是performBuild方法,它是具体的构建方法,在该方法中,会统计出过滤器链的总个
数(包括被忽略的请求个数以及过滤器链个数),然后创建一个集合securityFiterChains,遍历被忽略的请求并分别构建成DefaultSecurityFiterChain 对象保存到securityFilterChains 集合中。
其中,对于被忽略的请求,在构建DefaultSecurityFilterChain对象时,只是传入了请求匹配器,而没有传入对应的过滤器链,这就意味着这些被忽略掉的请求,将来不必经过Spring Security 过滤器链;接下来再遍历securityFilterChainBuilders集合,调用每个对象的build 方法构建DefaultSecurityFilterChain并存入securityFilterChains集合中,然后传入securityFilterChains集合构建FilterChainProxy对象,最后再设置HTTP防火墙。所有设置完成之后,最后返回filterChainProxy对象。
FilterChainProxy则是最终构建出来的代理过滤器链,通过 Spring 提供的DelegatingFilterProxy将FilterChainProxy对象嵌入到原生的Filter中,这样才会对请求生效。