环境: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) ;
}
}
总结:
- 过滤器创建的实现原理。
- 自定义过滤器的应用。