过滤器链(一)
一.简介
参考:
深入浅出Spring Security(二):FilterChainProxy的创建过程
Spring Security 源码分析(一):过滤器链
Spring Security源码解析(二.创建FilterChainProxy)
Spring Security(六)—SpringSecurityFilterChain 加载流程深度解析
文中源码均为伪代码,删除了一些的细节
在SpringSecurity框架中,用户想要访问资源需要经过
FilterChainProxy
(本质上是个Filter,名字叫springSecurityFilterChain)来过滤。
里面包含一组SecuriyFilterChains,每个uri都对应一个SecurityFilterChain
,SecurityFilterChain包含多个SecurityFilter
二.FilterChainProxy的创建
虚线继承/实现,如有错乱以你为准。
从图中可以看出
WebSecurity
和HttpSecurity
都是Builder,
WebSecurity
是SecurityBuilder< Filter >为了创建FilterChainProxy
,
HttpSecurity
是SecurityBuilder< DefaultSecurityFilterChain >为了创建FilterChainProxy
的filterChains。WebSecurityConfigurerAdapter
是WebSecurity的Configurer
在WebSecurityConfiguration
创建WebSecurity
后,赋值给WebSecurity
的配置configurers,
并在WebSecurity
要创建FilterChainProxy
时,初始化,将HttpSecurity
赋值给WebSecurity
的securityFilterChainBuilders用于创建过滤器。
1.WebSecurityConfiguration
1.1 setFilterChainProxySecurityConfigurer()
public void setFilterChainProxySecurityConfigurer(ObjectPostProcessor<Object> objectPostProcessor, @Value("#{@autowiredWebSecurityConfigurersIgnoreParents.getWebSecurityConfigurers()}") List<SecurityConfigurer<Filter, WebSecurity>> webSecurityConfigurers) throws Exception {
this.webSecurity = (WebSecurity)objectPostProcessor.postProcess(new WebSecurity(objectPostProcessor));
webSecurityConfigurers.sort(WebSecurityConfiguration.AnnotationAwareOrderComparator.INSTANCE);
Iterator var5;
SecurityConfigurer config;
for(var5 = webSecurityConfigurers.iterator(); var5.hasNext(); previousConfig = config) {
config = (SecurityConfigurer)var5.next();
Integer order = WebSecurityConfiguration.AnnotationAwareOrderComparator.lookupOrder(config);
previousOrder = order;
}
var5 = webSecurityConfigurers.iterator();
while(var5.hasNext()) {
config = (SecurityConfigurer)var5.next();
this.webSecurity.apply(config);
}
this.webSecurityConfigurers = webSecurityConfigurers;
}
该方法负责收集配置类对象列表
webSecurityConfigurers
,并创建WebSecurity
,用于后面建造FilterChainProxy
过滤器
@Value("#{@autowiredWebSecurityConfigurersIgnoreParents. getWebSecurityConfigurers()}")
List<SecurityConfigurer<Filter, WebSecurity>> webSecurityConfigurers
@Value("#{}") 是SpEl表达式通常用来获取bean的属性或者调用bean的某个方法。
方法执行时,会先得到webSecurityConfigurers并排序(所有实现了WebSecurityConfigurerAdapter的配置类实例)
public List<SecurityConfigurer<Filter, WebSecurity>> getWebSecurityConfigurers() {
List<SecurityConfigurer<Filter, WebSecurity>> webSecurityConfigurers = new ArrayList();
Map<String, WebSecurityConfigurer> beansOfType = this.beanFactory.getBeansOfType(WebSecurityConfigurer.class);
Iterator var3 = beansOfType.entrySet().iterator();
while(var3.hasNext()) {
Entry<String, WebSecurityConfigurer> entry = (Entry)var3.next();
webSecurityConfigurers.add(entry.getValue());
}
return webSecurityConfigurers;
}
-
获取类型为
WebSecurityConfigurer
的bean -
this.webSecurity.apply(config)将
WebSecurityConfigurerAdapter
赋值给AbstractConfiguredSecurityBuilder
(webSecurity的父类)的configurersAddedInInitializing 和 configurers 【2.1】。
1.2 springSecurityFilterChain()
@Bean( name = {"springSecurityFilterChain"})
public Filter springSecurityFilterChain() throws Exception {
boolean hasConfigurers = this.webSecurityConfigurers != null && !this.webSecurityConfigurers.isEmpty();
if (!hasConfigurers) {
WebSecurityConfigurerAdapter adapter = (WebSecurityConfigurerAdapter)this.objectObjectPostProcessor.postProcess(new WebSecurityConfigurerAdapter() {
});
this.webSecurity.apply(adapter);
}
return (Filter)this.webSecurity.build();
}
调用
webSecurity
.build(),建造出FilterChainProxy
过滤器对象
并将其命名为"springSecurityFilterChain"
webSecurity
.build()–>AbstractSecurityBuilder.build()–>
AbstractConfiguredSecurityBuilder
.doBuild()【2.2】
- init(),configure()
将HttpSecurity
添加到webSecurity
的属性securityFilterChainBuilders集合里。 WebSecurity
.performBuild()【3.1】
securityFilterChainBuilders创建filterchains
创建FilterChainProxy
;
2.AbstractConfiguredSecurityBuilder
WebSecurity
继承AbstractConfiguredSecurityBuilder
2.1 apply(C configurer),add(C configurer)
public <C extends SecurityConfigurer<O, B>> C apply(C configurer) throws Exception {
this.add(configurer);
return configurer;
}
private <C extends SecurityConfigurer<O, B>> void add(C configurer) {
Class<? extends SecurityConfigurer<O, B>> clazz = configurer.getClass();
LinkedHashMap var3 = this.configurers;
synchronized(this.configurers) {
List<SecurityConfigurer<O, B>> configs = this.allowConfigurersOfSameType ? (List)this.configurers.get(clazz) : null;
((List)configs).add(configurer);
this.configurers.put(clazz, configs);
this.configurersAddedInInitializing.add(configurer);
}
}
2.2 doBuild()
protected final O doBuild() throws Exception {
LinkedHashMap var1 = this.configurers;
synchronized(this.configurers) {
this.buildState = AbstractConfiguredSecurityBuilder.BuildState.INITIALIZING;
this.beforeInit();
this.init();
this.buildState = AbstractConfiguredSecurityBuilder.BuildState.CONFIGURING;
this.beforeConfigure();
this.configure();
this.buildState = AbstractConfiguredSecurityBuilder.BuildState.BUILDING;
O result = this.performBuild();
this.buildState = AbstractConfiguredSecurityBuilder.BuildState.BUILT;
return result;
}
}
2.3 init(),configure()
- this.init() - >configurer.init(this) - >
WebSecurityConfigurerAdapter
.init(WebSecurity web) 【4.1】 - this.configure(this)->
WebSecurityConfigurerAdapter
.configure(WebSecurity web) 【4.2.2】;
private void init() throws Exception {
Collection<SecurityConfigurer<O, B>> configurers = this.getConfigurers();
Iterator var2 = configurers.iterator();
SecurityConfigurer configurer;
while(var2.hasNext()) {
configurer = (SecurityConfigurer)var2.next();
configurer.init(this);
}
var2 = this.configurersAddedInInitializing.iterator();
while(var2.hasNext()) {
configurer = (SecurityConfigurer)var2.next();
configurer.init(this);
}
}
private void configure() throws Exception {
Collection<SecurityConfigurer<O, B>> configurers = this.getConfigurers();
Iterator var2 = configurers.iterator();
while(var2.hasNext()) {
SecurityConfigurer<O, B> configurer = (SecurityConfigurer)var2.next();
configurer.configure(this);
}
}
3. WebSecurity
3.1 performBuild()
protected Filter performBuild() throws Exception {
// ...
int chainSize = ignoredRequests.size() + securityFilterChainBuilders.size();
List<SecurityFilterChain> securityFilterChains = new ArrayList<>(chainSize);
// 上文配置的需要忽略的请求
for (RequestMatcher ignoredRequest : ignoredRequests) {
securityFilterChains.add(new DefaultSecurityFilterChain(ignoredRequest));
}
// 上文配置的 HttpSecurity 实例
for (SecurityBuilder<? extends SecurityFilterChain> securityFilterChainBuilder : securityFilterChainBuilders) {
// 调用 build 方法生成 DefaultSecurityFilterChain
securityFilterChains.add(securityFilterChainBuilder.build());
}
// 过滤器链代理
FilterChainProxy filterChainProxy = new FilterChainProxy(securityFilterChains);
// ...
}
创建了FilterChainProxy
并用securityFilterChainBuilder配置了FilterChainProxy
的过滤器链securityFilterChains
4.WebSecurityConfigurerAdapter
4.1 init(),getHttp()
public void init(WebSecurity web) throws Exception {
HttpSecurity http = this.getHttp();
web.addSecurityFilterChainBuilder(http).postBuildAction(() -> {
FilterSecurityInterceptor securityInterceptor = (FilterSecurityInterceptor)http.getSharedObject(FilterSecurityInterceptor.class);
web.securityInterceptor(securityInterceptor);
});
}
protected final HttpSecurity getHttp() throws Exception {
AuthenticationManager authenticationManager = this.authenticationManager();
this.authenticationBuilder.parentAuthenticationManager(authenticationManager);
this.http = new HttpSecurity(this.objectPostProcessor, this.authenticationBuilder, sharedObjects);
}
WebSecurityConfigurerApdater
.init()方法,通过getHttp()方法获得配置好的 HttpSecurity
。
然后 web.addSecurityFilterChainBuilder(http)将 HttpSecurity
添加到webSecurity
的属性securityFilterChainBuilders集合里。
public WebSecurity addSecurityFilterChainBuilder(SecurityBuilder<? extends SecurityFilterChain> securityFilterChainBuilder) {
this.securityFilterChainBuilders.add(securityFilterChainBuilder);
return this;
}
4.2 配置WebSecurityConfigurerAdapter
如果需要干预 spring security 配置,创建MyWebSecurityConfig 继承WebSecurityConfigurerAdapter
并装配到 Spring 容器中。比如
@Configuration
@EnableWebSecurity
@Slf4j
public class MyWebSecurityConfig extends WebSecurityConfigurerAdapter {
}
这里我们可能会重写WebSecurityConfigurerAdapter 中的绿框中的三个configure方法。
4.2.1 configure(HttpSecurity http)
原方法
protected void configure(HttpSecurity http) throws Exception {
this.logger.debug("Using default configure(HttpSecurity). If subclassed this will potentially override subclass configure(HttpSecurity).");
((HttpSecurity)((HttpSecurity)((AuthorizedUrl)http.authorizeRequests().anyRequest()).authenticated().and()).formLogin().and()).httpBasic();
}
例子:修改HttpSecurity,重新设置登录,添加filter,修改拦截的路径等
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.formLogin()
.loginPage("/login")
.loginProcessingUrl("/login/submit")
.failureUrl("/login_error").permitAll()
.and()
.authorizeRequests()
.antMatchers(HttpMethod.GET, "/login").permitAll()
.anyRequest().authenticated()
.and()
.csrf().disable();
http.addFilterBefore(authenticationTokenFilter, UsernamePasswordAuthenticationFilter.class);
}
4.2.2 configure(WebSecurity web)
原方法
public void configure(WebSecurity web) throws Exception {
}
例子:修改WebSecurity 忽略静态资源
@Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers(HttpMethod.GET, "/*.html","/**/favicon.ico",
"/**/*.html", "/**/*.css", "/**/*.*.woff", "/**/*.*.woff2", "/**/*.*.ttf","/**/*.js", "/**/*.png",
"/**/*.jpg", "/**/*.ttf", "/**/*.map","/**/*.tpl","/**/*.map","/**/*.mp4", "/*.txt", "/**/*.txt",
"/webjars/**","/csrf","/v2/api-docs",
"/swagger-ui.html", "/swagger-resources/**");
}
4.2.3 configure(AuthenticationManagerBuilder auth)
原方法
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
this.disableLocalConfigureAuthenticationBldr = true;
}
例子: 使用customUserService进行用户信息的查询。
@Override
protected void configure (AuthenticationManagerBuilder auth) throws Exception{
auth.userDetailsService(customUserService()).passwordEncoder(passwordEncoder());
}
5. HttpSecurity 16.2
参考
Spring Security : 配置 HttpSecurity 的 SecurityConfigurer
Spring Security原理篇(三) HttpSecurity
用于配置 web 请求的安全配置,默认会应用到所有请求
在上面的configure(HttpSecurity http)中。我们可以修改HttpSecurity 的一些设置。
5.1 默认的过滤器链
Spring Security中HttpSecurity
是一个安全构建器SecurityBuilder
,目的是构建一个对HTTP请求进行安全控制的DefaultSecurityFilterChain
。
对HttpSecurity进行配置,是通过一组安全配置器来完成的。这组安全配置器有一个共同的抽象基类AbstractHttpConfigurer
。如下:
名称 | 简介 |
---|---|
AnonymousConfigurer |
配置匿名认证。 具体来讲,是向HttpSecurity 配置一对协调工作的AnonymousAuthenticationFilter 和AnonymousAuthenticationProvider ,所有属性采用合理的缺省值。这样请求处理时,如果SecurityContextHolder 中认证对象未设置(null 的情况),则向其设置一个匿名认证对象AnonymousAuthenticationToken 。 |
ChannelSecurityConfigurer |
配置ChannelProcessingFilter |
CorsConfigurer |
配置CorsFilter |
CsrfConfigurer |
配置CsrfFilter |
DefaultLoginPageConfigurer |
配置DefaultLoginPageGeneratingFilter /DefaultLogoutPageGeneratingFilter |
ExceptionHandlingConfigurer |
配置ExceptionTranslationFilter |
ExpressionUrlAuthorizationConfigurer | 配置FilterSecurityInterceptor |
FormLoginConfigurer | 配置UsernamePasswordAuthenticationFilter /DefaultLoginPageGeneratingFilter |
HeadersConfigurer |
配置HeaderWriterFilter |
HttpBasicConfigurer | 配置BasicAuthenticationFilter |
JeeConfigurer |
配置J2eePreAuthenticatedProcessingFilter |
LogoutConfigurer |
配置LogoutFilter |
PortMapperConfigurer |
配置共享对象PortMapper ,用于HTTP /HTTPS 两种协议之间跳转时的端口检测。 |
RememberMeConfigurer |
配置RememberMeAuthenticationFilter |
RequestCacheConfigurer |
配置RequestCacheAwareFilter |
SecurityContextConfigurer |
配置SecurityContextPersistenceFilter |
ServletApiConfigurer |
配置SecurityContextHolderAwareRequestFilter |
SessionManagementConfigurer |
配置SessionManagementFilter /ConcurrentSessionFilter |
X509Configurer |
配置X509AuthenticationFilter |
OAuth2ClientConfigurer |
配置OAuth2AuthorizationRequestRedirectFilter /OAuth2AuthorizationCodeGrantFilter ;配置共享对象: ClientRegistrationRepository / OAuth2AuthorizedClientRepository |
OAuth2LoginConfigurer |
配置OAuth2AuthorizationRequestRedirectFilter /OAuth2LoginAuthenticationFilter ;配置共享对象: ClientRegistrationRepository /OAuth2AuthorizedClientRepository / GrantedAuthoritiesMapper |
OAuth2ResourceServerConfigurer |
配置BearerTokenAuthenticationFilter ;配置共享对象: SessionCreationPolicy |
OpenIDLoginConfigurer |
配置OpenIDAuthenticationFilter ;配置共享对象: AuthenticationEntryPoint / OpenIDAuthenticationProvider |
比如 http.authorizeRequests()、http.formLogin()、http.httpBasic()分别创建了
ExpressionUrlAuthorizationConfigurer
,FormLoginConfigurer
,HttpBasicConfigurer
。在三个类从父级一直往上找,会发现它们都是SecurityConfigurer建造器的子类。
SecurityConfigurer中又有configure()方法。该方法被子类实现就用于创建各个过滤器,并将过滤器添加进HttpSecurity中维护的装有Filter的List中
5.2 addFilter(Filter) 添加自定义的过滤器
http.addFilterBefore(authenticationTokenFilter, UsernamePasswordAuthenticationFilter.class);
在过滤器链中UsernamePasswordAuthenticationFilter
前插入我们自定义的authenticationTokenFilter
。