過濾器鏈(一)
一.簡介
參考:
深入淺出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
。