Spring Security
青阳半雪 人气:01 简述
Spring Security 本质上就是通过一系列的过滤器,进行业务的处理。
Spring Security
在 Servlet
的过滤链(filter chain
)中注册了一个过滤器 FilterChainProxy
,它会把请求代理到 Spring Security
自己维护的多个过滤链,每个过滤链会匹配一些 URL
,如果匹配则执行对应的过滤器。过滤链是有顺序的,一个请求只会执行第一条匹配的过滤链。Spring Security
的配置本质上就是新增、删除、修改过滤器
但是万物终归有源头,过滤器是如何注册进来的,通过过程了解注册的骨架。
注明 这里只是使用了 spring web + spring security,同时使用 web.xml 的风格进行配置,如果你使用 SPI 机制(没有使用 web.xml),殊途同归。
2 注册过程
2.1 web.xml 配置
<filter> <filter-name>springSecurityFilterChain</filter-name> <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class> </filter> <filter-mapping> <filter-name>springSecurityFilterChain</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
其中 DelegatingFilterProxy
类图如下:
根据类图看出,DelegatingFilterProxy
继承 GenericFilterBean
,其间接实现了 Filter
接口,所以从类型上看,其也是一个过滤器。
从 Spring 容器中寻找 targetBeanName=springSecurityFilterChain 的 Bean, 从 DelegatingFilterProxy 的名字上看,知道它其实是一个代理类,真正执行业务的,是 filter-name 指定的 springSecurityFilterChain 这个 bean。接下来,问题来了,springSecurityFilterChain 这个 bean 又是在什么时候注册的呢
2.2 EnableWebSecurity 注解
我们一般在使用 Spring Security
的时候,都会自定义继承 WebSecurityConfigurerAdapter
,同时结合使用 EnableWebSecurity
注解,然后在自定义的类中进行各种各样满足业务的工作。
@EnableWebSecurity public class SecurityConfiguration extends WebSecurityConfigurerAdapter { ... }
这里我们重点关注的是
EnableWebSecurity
注解,查看下其源码,做了两个非常重要的点
- 1 导入
WebSecurityConfiguration
配置。 - 2 通过
@EnableGlobalAuthentication
注解引入全局配置
@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) @Documented @Import({ WebSecurityConfiguration.class, SpringWebMvcImportSelector.class, OAuth2ImportSelector.class, HttpSecurityConfiguration.class }) @EnableGlobalAuthentication @Configuration public @interface EnableWebSecurity { /** * Controls debugging support for Spring Security. Default is false. * @return if true, enables debug support with Spring Security */ boolean debug() default false; }
此注解中由使用了
Import
注解引入了其他的bean
,然后查看其源码,重点关注WebSecurityConfiguration
。
2.3 WebSecurityConfiguration 类
这个类里面比较重要的就两个方法:
1 springSecurityFilterChain
springSecurityFilterChain
方法上添加了 @Bean
注解,可以知道是创建了springSecurityFilterChain
bean
2 setFilterChainProxySecurityConfigurer
这个方式设置了对应的配置,注意,这个方法优先上面的方法执行。
分析其重点代码
private WebSecurity webSecurity; // 注入 bean @Bean(name = AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME) public Filter springSecurityFilterChain() throws Exception { boolean hasConfigurers = this.webSecurityConfigurers != null && !this.webSecurityConfigurers.isEmpty(); boolean hasFilterChain = !this.securityFilterChains.isEmpty(); ... if (!hasConfigurers && !hasFilterChain) { WebSecurityConfigurerAdapter adapter = this.objectObjectPostProcessor .postProcess(new WebSecurityConfigurerAdapter() { }); this.webSecurity.apply(adapter); } for (SecurityFilterChain securityFilterChain : this.securityFilterChains) { this.webSecurity.addSecurityFilterChainBuilder(() -> securityFilterChain); for (Filter filter : securityFilterChain.getFilters()) { if (filter instanceof FilterSecurityInterceptor) { this.webSecurity.securityInterceptor((FilterSecurityInterceptor) filter); break; } } } for (WebSecurityCustomizer customizer : this.webSecurityCustomizers) { customizer.customize(this.webSecurity); } // 重点关注 return this.webSecurity.build(); } @Autowired(required = false) public void setFilterChainProxySecurityConfigurer(ObjectPostProcessor<Object> objectPostProcessor, @Value("#{@autowiredWebSecurityConfigurersIgnoreParents.getWebSecurityConfigurers()}") List<SecurityConfigurer<Filter, WebSecurity>> webSecurityConfigurers) throws Exception { this.webSecurity = objectPostProcessor.postProcess(new WebSecurity(objectPostProcessor)); if (this.debugEnabled != null) { this.webSecurity.debug(this.debugEnabled); } webSecurityConfigurers.sort(AnnotationAwareOrderComparator.INSTANCE); Integer previousOrder = null; Object previousConfig = null; for (SecurityConfigurer<Filter, WebSecurity> config : webSecurityConfigurers) { Integer order = AnnotationAwareOrderComparator.lookupOrder(config); if (previousOrder != null && previousOrder.equals(order)) { throw new IllegalStateException("@Order on WebSecurityConfigurers must be unique. Order of " + order + " was already used on " + previousConfig + ", so it cannot be used on " + config + " too."); } previousOrder = order; previousConfig = config; } for (SecurityConfigurer<Filter, WebSecurity> webSecurityConfigurer : webSecurityConfigurers) { this.webSecurity.apply(webSecurityConfigurer); } this.webSecurityConfigurers = webSecurityConfigurers; }
我们先看执行的 setFilterChainProxySecurityConfigurer
方法,其中参数 webSecurityConfigurers
是一个 List
,它实际上是所有 WebSecurityConfigurerAdapter
的子类,那如果我们定义了自定义的配置类,也意味着读取了我们自定义的类。
接着看到 springSecurityFilterChain
方法注册了一个名字为 AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME 的 bean
,翻看源码,其实名字就是在 web.xml
中配置的 filter-name
,即 springSecurityFilterChain
,到这里困惑解开了一部分,原来查询的 bean 是在这里注入的。同时也要求了在使用 spring-security
时,在 web.xml
中配置 filter
时,不能是其他名字。
根据
this.webSecurity.build
这行代码,发现真正构建过滤器的是WebSecurity
类
2.4 WebSecurity 类
接上一步中的代码
// 重点关注 return this.webSecurity.build();
知道起作用的是 WebSecurity 类,其类图结构如下:
根据源码,定位到 WebSecurity
类中的 performBuild
方法
@Override protected Filter performBuild() throws Exception { ... int chainSize = this.ignoredRequests.size() + this.securityFilterChainBuilders.size(); List<SecurityFilterChain> securityFilterChains = new ArrayList<>(chainSize); for (RequestMatcher ignoredRequest : this.ignoredRequests) { securityFilterChains.add(new DefaultSecurityFilterChain(ignoredRequest)); } // ① 调用 securityFilterChainBuilder 的 build() 方法 for (SecurityBuilder<? extends SecurityFilterChain> securityFilterChainBuilder : this.securityFilterChainBuilders) { securityFilterChains.add(securityFilterChainBuilder.build()); } FilterChainProxy filterChainProxy = new FilterChainProxy(securityFilterChains); if (this.httpFirewall != null) { filterChainProxy.setFirewall(this.httpFirewall); } if (this.requestRejectedHandler != null) { filterChainProxy.setRequestRejectedHandler(this.requestRejectedHandler); } filterChainProxy.afterPropertiesSet(); Filter result = filterChainProxy; ... this.postBuildAction.run(); return result; }
代码分析:
- 1 代码中的
①
处,重点关注,通过断点调试,发现其实这里的securityFilterChainBuilder
就是HttpSecurity
,到这里也很清楚了,默认以及自定义过滤器是通过HttpSecurity
加载进来的。 - 2 创建好过滤器链之后,然后实例化
filterChainProxy
进行返回
根据源码知道
performBuild
方法最终返回的其实是一个filterChainProxy
实例,接下来我们再关注下filterChainProxy
类。
2.5 FilterChainProxy 类
其类图结构如下
查看其关键代码:
public class FilterChainProxy extends GenericFilterBean { // 维护的 spring security 过滤器链列表 private List<SecurityFilterChain> filterChains; public FilterChainProxy(SecurityFilterChain chain) { this(Arrays.asList(chain)); } public FilterChainProxy(List<SecurityFilterChain> filterChains) { this.filterChains = filterChains; } @Override public void doFilter( ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { boolean clearContext = request.getAttribute(FILTER_APPLIED) == null; if (!clearContext) { doFilterInternal(request, response, chain); return; } try { request.setAttribute(FILTER_APPLIED, Boolean.TRUE); // 真正起作用的函数 doFilterInternal(request, response, chain); } catch (RequestRejectedException ex) { this.requestRejectedHandler.handle((HttpServletRequest) request, (HttpServletResponse) response, ex); } ... } private void doFilterInternal( ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { FirewalledRequest firewallRequest = this.firewall.getFirewalledRequest( (HttpServletRequest) request); HttpServletResponse firewallResponse = this.firewall.getFirewalledResponse( (HttpServletResponse) response); List<Filter> filters = getFilters(firewallRequest); if (filters == null || filters.size() == 0) { ... chain.doFilter(firewallRequest, firewallResponse); return; } VirtualFilterChain virtualFilterChain = new VirtualFilterChain( firewallRequest, chain, filters); virtualFilterChain.doFilter(firewallRequest, firewallResponse); } }
通过代码分析,FilterChainProxy
这个类维护了真正的过滤器链列表,即 SecurityFilterChain
类型的过滤器链,注意,SecurityFilterChain
是过滤器链,而不是一个个的过滤器,过滤器会有其他的操作塞到过滤器链中,即 2.4
中的代码块,代码如下。
for (RequestMatcher ignoredRequest : this.ignoredRequests) { securityFilterChains.add(new DefaultSecurityFilterChain(ignoredRequest)); } // ① 调用 securityFilterChainBuilder 的 build() 方法 for (SecurityBuilder<? extends SecurityFilterChain> securityFilterChainBuilder : this.securityFilterChainBuilders) { securityFilterChains.add(securityFilterChainBuilder.build()); }
这段代码其实执行的业务就是把过滤器塞入到过滤器链中。
这里也更加清楚了, Spring Security Filter
并不是直接嵌入到 Web Filter
中的,而是通过 FilterChainProxy
来统一管理 Spring Security Filter
,FilterChainProxy
本身则通过 Spring
提供的 DelegatingFilterProxy
代理过滤器嵌入到 Web Filter
之中。
3 小结
根据趟源码,大概了解了过滤器注册的流程:
- 1
web.xml
中DelegatingFilterProxy
配置 - 2
EnableWebSecurity
注解引入配置初始化 - 3
WebSecurityConfiguration
类注入springSecurityFilterChain
的bean
并开始构建过滤器 - 4
WebSecurity
类中的performBuild
方法把过滤器都处理好,放到过滤器链中,接着实例化filterChainProxy
,这里实例化返回的对象就是第三步的过滤器
加载全部内容