Spring Security權(quán)限控制系列(四)
環(huán)境:Springboot2.4.12 + Spring Security 5.4.9
本篇主要內(nèi)容:
- 核心過濾器創(chuàng)建原理
- 自定義過濾器
上一篇:《??Spring Security權(quán)限控制系列(三)??》
核心過濾器創(chuàng)建原理
Spring Security核心是通過Filter過濾器鏈來完成一系列邏輯處理的,比如CSRF,認(rèn)證,授權(quán)驗(yàn)證,Session管理等功能,這些過濾器都封裝在DefaultSecurityFilterChain中,最終過濾器鏈會(huì)被添加到FilterChainProxy(該過濾器的Bean名稱為springSecurityFilterChain)實(shí)際的過濾器中。
回顧過濾器FilterChainProxy與過濾器鏈DefaultSecurityFilterChain的創(chuàng)建過程:
- FilterChainProxy創(chuàng)建
// 1.創(chuàng)建FilterChainProxy過程
public class WebSecurityConfiguration {
(name = AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME)
public Filter springSecurityFilterChain() throws Exception {
// 當(dāng)前環(huán)境中是否有自定義SecurityConfigurer類(WebSecurityConfigurerAdapter);這里是自動(dòng)注入的
boolean hasConfigurers = this.webSecurityConfigurers != null && !this.webSecurityConfigurers.isEmpty();
// 當(dāng)前環(huán)境中是否有自定義的SecurityFilterChain過濾器鏈Bean(這里是自動(dòng)注入的)
boolean hasFilterChain = !this.securityFilterChains.isEmpty();
// 如果當(dāng)前環(huán)境上面的兩種情況都存在則會(huì)拋出異常。只能有一個(gè)成立即可
Assert.state(!(hasConfigurers && hasFilterChain), "Found WebSecurityConfigurerAdapter as well as SecurityFilterChain. Please select just one.");
// 如果都不存在,則會(huì)創(chuàng)建一個(gè)默認(rèn)的SecurityConfigurer
// 當(dāng)我們項(xiàng)目中只是引入Spring Security包時(shí)就是該中情況
if (!hasConfigurers && !hasFilterChain) {
WebSecurityConfigurerAdapter adapter = this.objectObjectPostProcessor.postProcess(new WebSecurityConfigurerAdapter() {});
this.webSecurity.apply(adapter);
}
// ...
// 查找當(dāng)前環(huán)境中是否有自定義的WebSecurityCustomizer類型的Bean
// 我們可以自定義WebSecurityCustomizer然后設(shè)置WebSecurity忽略的請求URI
for (WebSecurityCustomizer customizer : this.webSecurityCustomizers) {
customizer.customize(this.webSecurity);
}
// 通過WebSecurity#build方法來構(gòu)建FilterChainProxy
return this.webSecurity.build();
}
}
WebSecurity#build構(gòu)建FilterChainProxy過濾器。
public final class WebSecurity {
private final List<RequestMatcher> ignoredRequests = new ArrayList<>();
protected Filter performBuild() throws Exception {
// 計(jì)算過濾器鏈的大小,這兩個(gè)集合是如何被設(shè)置的?
// ignoredRequests 可以通過自定義WebSecurityCustomizer
// 通過web.ignoring().antMatchers("/demos/home") ;方法添加
// securityFilterChainBuilders 就是通過我們自定義WebSecurityConfigurerAdapter#init中構(gòu)建的HttpSecurity
int chainSize = this.ignoredRequests.size() + this.securityFilterChainBuilders.size();
List<SecurityFilterChain> securityFilterChains = new ArrayList<>(chainSize);
// 為每一個(gè)忽略的uri配置一個(gè)過濾器鏈(注意:該中過濾器鏈中是沒有過濾器的)
for (RequestMatcher ignoredRequest : this.ignoredRequests) {
securityFilterChains.add(new DefaultSecurityFilterChain(ignoredRequest));
}
// securityFilterChainBuilders上面已經(jīng)說過基本就是我們自定義的WebSecurityConfigurerAdapter
// 而該類在執(zhí)行build方法的時(shí)候其實(shí)就是為HttpSecurity構(gòu)建過濾器鏈
for (SecurityBuilder<? extends SecurityFilterChain> securityFilterChainBuilder : this.securityFilterChainBuilders) {
securityFilterChains.add(securityFilterChainBuilder.build());
}
FilterChainProxy filterChainProxy = new FilterChainProxy(securityFilterChains);
// 執(zhí)行Bean的初始化過程
filterChainProxy.afterPropertiesSet();
Filter result = filterChainProxy;
return result;
}
}
以上就是創(chuàng)建核心過濾器FilterChainProxy的底層實(shí)現(xiàn)原理。
- DefaultSecurityFilterChain創(chuàng)建
過濾器鏈的創(chuàng)建在上面其實(shí)已經(jīng)提到了是如何被創(chuàng)建的主要就是三種方式:
自定義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") ;
}
}
這種方式就是為每一個(gè)定義的URI創(chuàng)建一個(gè)沒有過濾器的過濾器鏈。
自定義WebSecurityConfigurerAdapter類型的Bean
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
// ...
}
該種方式在上面的源碼展示中已經(jīng)看到了,上面的代碼片段。
for (SecurityBuilder<? extends SecurityFilterChain> securityFilterChainBuilder : this.securityFilterChainBuilders) {
// HttpSecurity#build構(gòu)建Filter過濾器練
securityFilterChains.add(securityFilterChainBuilder.build());
}
自定義WebSecurityConfigurerAdapter子類將HttpSecurity添加到WebSecurity.securityFilterChainBuilders集合中。
public abstract class WebSecurityConfigurerAdapter {
public void init(WebSecurity web) throws Exception {
// 構(gòu)建HttpSecurity對象
HttpSecurity http = getHttp();
web.addSecurityFilterChainBuilder(http).postBuildAction(() -> {
FilterSecurityInterceptor securityInterceptor = http.getSharedObject(FilterSecurityInterceptor.class);
web.securityInterceptor(securityInterceptor);
});
}
}
HttpSecurity構(gòu)建過濾器鏈。
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);
}
}
到此應(yīng)該非常清楚底層創(chuàng)建核心過濾器FilterChainProxy及該過濾器與SecurityFilterChain過濾器鏈的關(guān)系及過濾器鏈創(chuàng)建的幾種方式。
自定義過濾器
過濾器鏈中的每一個(gè)過濾器都是有系統(tǒng)提供的,每種過濾器都處理不同方面的事,如果我們希望在現(xiàn)有的過濾器鏈中加入我們的一些處理過濾該如何操作?Spring Security為我們提供了往過濾器鏈中添加過濾器的接口,接下來通過實(shí)例來看如何向過濾器鏈中添加我們自定義的過濾器,以此實(shí)現(xiàn)我們自己的邏輯。
- 自定義過濾器
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個(gè)方法來添加過濾器。
public class SecurityConfig extends WebSecurityConfigurerAdapter {
private AutoAuthenticationFilter authFilter;
protected void configure(HttpSecurity http) throws Exception {
// 將自定義的過濾器添加到UsernamePasswordAuthenticationFilter過濾器的前面
http.addFilterBefore(authFilter, UsernamePasswordAuthenticationFilter.class) ;
}
}
總結(jié):
- 過濾器創(chuàng)建的實(shí)現(xiàn)原理。
- 自定義過濾器的應(yīng)用。