Spring Security權限控制系列(四)
環境:Springboot2.4.12 + Spring Security 5.4.9
本篇主要內容:
- 核心過濾器創建原理
- 自定義過濾器
上一篇:《??Spring Security權限控制系列(三)??》
核心過濾器創建原理
Spring Security核心是通過Filter過濾器鏈來完成一系列邏輯處理的,比如CSRF,認證,授權驗證,Session管理等功能,這些過濾器都封裝在DefaultSecurityFilterChain中,最終過濾器鏈會被添加到FilterChainProxy(該過濾器的Bean名稱為springSecurityFilterChain)實際的過濾器中。
回顧過濾器FilterChainProxy與過濾器鏈DefaultSecurityFilterChain的創建過程:
- FilterChainProxy創建
// 1.創建FilterChainProxy過程
public class WebSecurityConfiguration {
(name = AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME)
public Filter springSecurityFilterChain() throws Exception {
// 當前環境中是否有自定義SecurityConfigurer類(WebSecurityConfigurerAdapter);這里是自動注入的
boolean hasConfigurers = this.webSecurityConfigurers != null && !this.webSecurityConfigurers.isEmpty();
// 當前環境中是否有自定義的SecurityFilterChain過濾器鏈Bean(這里是自動注入的)
boolean hasFilterChain = !this.securityFilterChains.isEmpty();
// 如果當前環境上面的兩種情況都存在則會拋出異常。只能有一個成立即可
Assert.state(!(hasConfigurers && hasFilterChain), "Found WebSecurityConfigurerAdapter as well as SecurityFilterChain. Please select just one.");
// 如果都不存在,則會創建一個默認的SecurityConfigurer
// 當我們項目中只是引入Spring Security包時就是該中情況
if (!hasConfigurers && !hasFilterChain) {
WebSecurityConfigurerAdapter adapter = this.objectObjectPostProcessor.postProcess(new WebSecurityConfigurerAdapter() {});
this.webSecurity.apply(adapter);
}
// ...
// 查找當前環境中是否有自定義的WebSecurityCustomizer類型的Bean
// 我們可以自定義WebSecurityCustomizer然后設置WebSecurity忽略的請求URI
for (WebSecurityCustomizer customizer : this.webSecurityCustomizers) {
customizer.customize(this.webSecurity);
}
// 通過WebSecurity#build方法來構建FilterChainProxy
return this.webSecurity.build();
}
}
WebSecurity#build構建FilterChainProxy過濾器。
public final class WebSecurity {
private final List<RequestMatcher> ignoredRequests = new ArrayList<>();
protected Filter performBuild() throws Exception {
// 計算過濾器鏈的大小,這兩個集合是如何被設置的?
// ignoredRequests 可以通過自定義WebSecurityCustomizer
// 通過web.ignoring().antMatchers("/demos/home") ;方法添加
// securityFilterChainBuilders 就是通過我們自定義WebSecurityConfigurerAdapter#init中構建的HttpSecurity
int chainSize = this.ignoredRequests.size() + this.securityFilterChainBuilders.size();
List<SecurityFilterChain> securityFilterChains = new ArrayList<>(chainSize);
// 為每一個忽略的uri配置一個過濾器鏈(注意:該中過濾器鏈中是沒有過濾器的)
for (RequestMatcher ignoredRequest : this.ignoredRequests) {
securityFilterChains.add(new DefaultSecurityFilterChain(ignoredRequest));
}
// securityFilterChainBuilders上面已經說過基本就是我們自定義的WebSecurityConfigurerAdapter
// 而該類在執行build方法的時候其實就是為HttpSecurity構建過濾器鏈
for (SecurityBuilder<? extends SecurityFilterChain> securityFilterChainBuilder : this.securityFilterChainBuilders) {
securityFilterChains.add(securityFilterChainBuilder.build());
}
FilterChainProxy filterChainProxy = new FilterChainProxy(securityFilterChains);
// 執行Bean的初始化過程
filterChainProxy.afterPropertiesSet();
Filter result = filterChainProxy;
return result;
}
}
以上就是創建核心過濾器FilterChainProxy的底層實現原理。
- DefaultSecurityFilterChain創建
過濾器鏈的創建在上面其實已經提到了是如何被創建的主要就是三種方式:
自定義SecurityFilterChain類型的Bean
使用了該種方式我們就不能再自定義WebSecurityConfigurerAdapter?。
public class CustomSecurityFilterChain implements SecurityFilterChain {
public boolean matches(HttpServletRequest request) {
return false;
}
public List<Filter> getFilters() {
return new ArrayList<>() ;
}
}
自定義WebSecurityCustomizer類型的Bean
@Component
public class CustomWebSecurity implements WebSecurityCustomizer {
@Override
public void customize(WebSecurity web) {
web.ignoring().antMatchers("/demos/home") ;
}
}
這種方式就是為每一個定義的URI創建一個沒有過濾器的過濾器鏈。
自定義WebSecurityConfigurerAdapter類型的Bean
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
// ...
}
該種方式在上面的源碼展示中已經看到了,上面的代碼片段。
for (SecurityBuilder<? extends SecurityFilterChain> securityFilterChainBuilder : this.securityFilterChainBuilders) {
// HttpSecurity#build構建Filter過濾器練
securityFilterChains.add(securityFilterChainBuilder.build());
}
自定義WebSecurityConfigurerAdapter子類將HttpSecurity添加到WebSecurity.securityFilterChainBuilders集合中。
public abstract class WebSecurityConfigurerAdapter {
public void init(WebSecurity web) throws Exception {
// 構建HttpSecurity對象
HttpSecurity http = getHttp();
web.addSecurityFilterChainBuilder(http).postBuildAction(() -> {
FilterSecurityInterceptor securityInterceptor = http.getSharedObject(FilterSecurityInterceptor.class);
web.securityInterceptor(securityInterceptor);
});
}
}
HttpSecurity構建過濾器鏈。
public final class HttpSecurity {
protected DefaultSecurityFilterChain performBuild() {
this.filters.sort(OrderComparator.INSTANCE);
List<Filter> sortedFilters = new ArrayList<>(this.filters.size());
for (Filter filter : this.filters) {
sortedFilters.add(((OrderedFilter) filter).filter);
}
return new DefaultSecurityFilterChain(this.requestMatcher, sortedFilters);
}
}
到此應該非常清楚底層創建核心過濾器FilterChainProxy及該過濾器與SecurityFilterChain過濾器鏈的關系及過濾器鏈創建的幾種方式。
自定義過濾器
過濾器鏈中的每一個過濾器都是有系統提供的,每種過濾器都處理不同方面的事,如果我們希望在現有的過濾器鏈中加入我們的一些處理過濾該如何操作?Spring Security為我們提供了往過濾器鏈中添加過濾器的接口,接下來通過實例來看如何向過濾器鏈中添加我們自定義的過濾器,以此實現我們自己的邏輯。
- 自定義過濾器
public class AutoAuthenticationFilter extends OncePerRequestFilter {
private AuthenticationManager authenticationManager ;
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
UsernamePasswordAuthenticationToken authenication = new UsernamePasswordAuthenticationToken("admin", "123123") ;
authenication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request)) ;
Authentication auth = authenticationManager.authenticate(authenication) ;
if (auth != null) {
SecurityContextHolder.getContext().setAuthentication(auth) ;
request.getSession().setAttribute(HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY, SecurityContextHolder.getContext()) ;
}
System.out.println("--------------------------Auto Authenticaton Filter...") ;
filterChain.doFilter(request, response) ;
}
}
- 添加到過濾器鏈
通過自定義的WebSecurityConfigurerAdapter#configure(HttpSecurity http)注冊自定義的過濾器。下面4個方法來添加過濾器。
public class SecurityConfig extends WebSecurityConfigurerAdapter {
private AutoAuthenticationFilter authFilter;
protected void configure(HttpSecurity http) throws Exception {
// 將自定義的過濾器添加到UsernamePasswordAuthenticationFilter過濾器的前面
http.addFilterBefore(authFilter, UsernamePasswordAuthenticationFilter.class) ;
}
}
總結:
- 過濾器創建的實現原理。
- 自定義過濾器的應用。