一 前言
整个框架的核心就是构建一个名字为 springSecurityFilterChain
的过滤器Bean,它的类型是FilterChainProxy
。底层通过FilterChainProxy代理去调用各种Filter(Filter链),Filter通过调用AuthenticationManager
完成认证 ,通过调用AccessDecisionManager
完成授权。
二 springSecurityFilterChain的加载
springSecurityFilterChain这个Bean是核实加载到spring容器里面?
我们在引入springsecurity的自定义配置是加了@EnableWebSecurity
这个注解,这个注解又什么用呢,我们翻开他的源码看看
@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引入springsecurity的框架的响应的配置,并加载到spring容器中,完成了响应的配置,核心是WebSecurityConfiguration
这个类
2.1 springSecurityFilterChain的创建
在WebSecurityConfiguration我们发现会创建一个springSecurityFilterChain的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();
Assert.state(!(hasConfigurers && hasFilterChain),
"Found WebSecurityConfigurerAdapter as well as SecurityFilterChain. Please select just one.");
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();
}
建造者 webSecurity
调用 build()
方法,开始构建 springSecurityFilterChain
那么webSecurity在理创建的呢?
在这个类中有一个setFilterChainProxySecurityConfigurer()
通过他完成webSecurity的创建
@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;
}
在build方法里面干了啥呢
AbstractSecurityBuilder调用doBuild
调用org.springframework.security.config.annotation.AbstractConfiguredSecurityBuilder中的doBuild方法,通过performBuild()
完成springSecurityFilterChain的创建
在org.springframework.security.config.annotation.web.builders.WebSecurity完成核心创建
public final class WebSecurity extends
AbstractConfiguredSecurityBuilder<Filter, WebSecurity> implements
SecurityBuilder<Filter>, ApplicationContextAware {
private final List<RequestMatcher> ignoredRequests = new ArrayList<RequestMatcher>();
private final List<SecurityBuilder<? extends SecurityFilterChain>> securityFilterChainBuilders = new ArrayList<SecurityBuilder<? extends SecurityFilterChain>>();
...
@Override
protected Filter performBuild() throws Exception {
Assert.state(
!securityFilterChainBuilders.isEmpty(),
"At least one SecurityBuilder<? extends SecurityFilterChain> needs to be specified. Typically this done by adding a @Configuration that extends WebSecurityConfigurerAdapter. More advanced users can invoke "
+ WebSecurity.class.getSimpleName()
+ ".addSecurityFilterChainBuilder directly");
int chainSize = ignoredRequests.size() + securityFilterChainBuilders.size();
List<SecurityFilterChain> securityFilterChains = new ArrayList<SecurityFilterChain>(
chainSize);
for (RequestMatcher ignoredRequest : ignoredRequests) {
securityFilterChains.add(new DefaultSecurityFilterChain(ignoredRequest));
}
for (SecurityBuilder<? extends SecurityFilterChain> securityFilterChainBuilder : securityFilterChainBuilders) {
securityFilterChains.add(securityFilterChainBuilder.build());
}
FilterChainProxy filterChainProxy = new FilterChainProxy(securityFilterChains);
if (httpFirewall != null) {
filterChainProxy.setFirewall(httpFirewall);
}
filterChainProxy.afterPropertiesSet();
Filter result = filterChainProxy;
if (debugEnabled) {
logger.warn("\n\n"
+ "********************************************************************\n"
+ "********** Security debugging is enabled. *************\n"
+ "********** This may include sensitive information. *************\n"
+ "********** Do not use in a production system! *************\n"
+ "********************************************************************\n\n");
result = new DebugFilter(filterChainProxy);
}
postBuildAction.run();
return result;
}
...
}
- 主要关注由
AbstractConfiguredSecurityBuilder
继承下来的方法实现performBuild()
- 方法中首先创建了一个
securityFilterChains
- 然后第一个for循环将那些忽略拦截的URL封装成一堆
DefaultSecurityFilterChain
添加进securityFilterChains
- 第二个for循环调用的是 build() 方法,其实它最终调用的是
HttpSecurity
实现的 performBuild() 方法,返回值也是DefaultSecurityFilterChain
,随后添加进 securityFilterChains - 最后根据
securityFilterChains
创建出 FilterChainProxy 对象
注意创建过程可不是这么简单的由 webSecurity 一次性调用下来,就是说 doBuild() 可不只是被 webSecurity 对象调用,会被很多创建者重复调用,重复的去走创建流程,比如被一堆的 HttpSecurity 调用目的是创建单条过滤器链,可以自己Debug去走一走。
由此securityFilterChains就创建完成
2.2springSecurityFilterChain加载到Servlet 容器
通过上面我们知道springSecurityFilterChain创建完成了,那么2.2springSecurityFilterChain是如何加载到Servlet 容器中?
这里我们先了接下SpringBoot 中 Filter 加入 Servlet 容器的方式的几种方式
方式一 使用
@WebFilter
注解,@ServletComponentScan
所扫描的包路径必须包含该 Filter
@WebFilter(filterName = "myFilter",urlPatterns = "/*")
public class MyFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) {
}
@Override
public void destroy() {
}
}
@SpringBootApplication
@EnableAutoConfiguration
@EnableWebMvc
@ServletComponentScan(basePackages = "com.lktest.filter")
public class EghmApplication {
public static void main(String[] args) {
SpringApplication.run(EghmApplication.class, args);
}
}
方式二 通过FilterRegistrationBean 注入
@Configuration
public class FilterConfig {
@Bean
public FilterRegistrationBean filterRegistrationBean(){
FilterRegistrationBean bean = new FilterRegistrationBean();
bean.setFilter(new MyFilter2());
bean.addUrlPatterns("/*");
return bean;
}
}
方式三 通过
DelegatingFilterProxyRegistrationBean
注入
@Bean("proxyFilter")
public Filter filter (){
return new Filter() {
@Override
public void init(javax.servlet.FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) {
}
@Override
public void destroy() {
}
};
}
@Bean
public DelegatingFilterProxyRegistrationBean delegatingFilterProxyRegistrationBean(){
DelegatingFilterProxyRegistrationBean bean = new DelegatingFilterProxyRegistrationBean("proxyFilter");
bean.addUrlPatterns("/*");
return bean;
}
第二种和第三种类似,均实现了 AbstractFilterRegistrationBean 接口,而该接口间接实现了 ServletContextInitializer ,SpringBoot 在启动容器后会查找实现该接口的 Bean,并调用 onStartup() 方法添加自定义的 Filter,两者的区别:
- DelegatingFilterProxyRegistrationBean 通过传入的 proxyFilter 名字,在 WebApplicationContext 查找该 Fillter Bean,并通过DelegatingFilterProxy 生成基于该 Bean 的代理 Filter 对象。
- FilterRegistrationBean 则是直接设置一个Filter,因此该 Filter 可以有 Spring 容器管理,也可不用 Spring 管理
方式四 如果 Filter 声明为一个 Bean,则不需要定义为 FilterRegistrationBean,也会被 Spring 发现并添加,就是方法四,该方式无法定义拦截规则等,默认全局,慎用。
@Bean("myFilter")
public Filter filter() {
return new Filter() {
@Override
public void init(javax.servlet.FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) {
}
@Override
public void destroy() {
}
};
}
2.2.1 核心过滤器是如何加入 Sevlet 容器
其实核心过滤器的加入方式使用的是上面提到的方式三,SpringBoot 提供了这么一个类:
SecurityFilterAutoConfiguration
@Configuration(proxyBeanMethods = false)
// 仅在 Servlet 环境下生效
@ConditionalOnWebApplication(type = Type.SERVLET)
// 确保安全属性配置信息被加载并以 bean 形式被注册到容器
@EnableConfigurationProperties(SecurityProperties.class)
// 仅在特定类存在于 classpath 上时才生效
@ConditionalOnClass({
AbstractSecurityWebApplicationInitializer.class, SessionCreationPolicy.class })
// 指定该配置类在 SecurityAutoConfiguration 配置类应用之后应用
@AutoConfigureAfter(SecurityAutoConfiguration.class)
public class SecurityFilterAutoConfiguration {
// 要注册到 Servlet 容器的 DelegatingFilterProxy Filter的目标代理 Filter Bean 的名称:springSecurityFilterChain,
// 会发现实际上就是我们的核心过滤器的 Bean。
private static final String DEFAULT_FILTER_NAME = AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME;
// 定义一个 securityFilterChainRegistration 的 Bean,其目的是注册另外一个 Bean 到 Servlet 容器
// 实现类为 DelegatingFilterProxy 的一个 Servlet Filter,该 DelegatingFilterProxy Filter 其实是一个代理过滤器
// 它被 Servlet 容器用于匹配特定URL模式的请求,而它会将任务委托给名字为 springSecurityFilterChain 的 Filter
@Bean
@ConditionalOnBean(name = DEFAULT_FILTER_NAME)
public DelegatingFilterProxyRegistrationBean securityFilterChainRegistration(
SecurityProperties securityProperties) {
DelegatingFilterProxyRegistrationBean registration = new DelegatingFilterProxyRegistrationBean(
DEFAULT_FILTER_NAME);
registration.setOrder(securityProperties.getFilter().getOrder());
registration.setDispatcherTypes(getDispatcherTypes(securityProperties));
return registration;
}
private EnumSet<DispatcherType> getDispatcherTypes(SecurityProperties securityProperties) {
if (securityProperties.getFilter().getDispatcherTypes() == null) {
return null;
}
return securityProperties.getFilter().getDispatcherTypes().stream()
.map((type) -> DispatcherType.valueOf(type.name()))
.collect(Collectors.toCollection(() -> EnumSet.noneOf(DispatcherType.class)));
}
}
- 该类主要定义了一个
securityFilterChainRegistration
的 Bean,其目的是注册另外一个 Bean 到 Servlet 容器,实现类为 DelegatingFilterProxy 的一个 Servlet Filter - 这个 DelegatingFilterProxy Filter 其实是一个代理过滤器,它被 Servlet 容器用于匹配特定 URL 模式的请求,而它会将任务委托给名字为
springSecurityFilterChain
的 Filter - 正好,我们 Spring Security 的核心过滤器名字就是
springSecurityFilterChain
,其实现类是 FilterChainProxy
三 运行过程
public class FilterChainProxy extends GenericFilterBean {
//省略....
// Servlet 容器调用FilterChainProxy 的该方法
@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);
}
finally {
SecurityContextHolder.clearContext();
request.removeAttribute(FILTER_APPLIED);
}
}
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) {
if (logger.isTraceEnabled()) {
logger.trace(LogMessage.of(() -> "No security for " + requestLine(firewallRequest)));
}
firewallRequest.reset();
chain.doFilter(firewallRequest, firewallResponse);
return;
}
if (logger.isDebugEnabled()) {
logger.debug(LogMessage.of(() -> "Securing " + requestLine(firewallRequest)));
}
// 将过滤器链加载到VirtualFilterChain
VirtualFilterChain virtualFilterChain = new VirtualFilterChain(firewallRequest, chain, filters);
// 调用VirtualFilterChain doFilter()执行过滤器中方法
virtualFilterChain.doFilter(firewallRequest, firewallResponse);
}
//VirtualFilterChain 实现FilterChain
private static final class VirtualFilterChain implements FilterChain {
private final FilterChain originalChain;
private final List<Filter> additionalFilters;
private final FirewalledRequest firewalledRequest;
private final int size;
private int currentPosition = 0;
private VirtualFilterChain(FirewalledRequest firewalledRequest, FilterChain chain,
List<Filter> additionalFilters) {
this.originalChain = chain;
this.additionalFilters = additionalFilters;
this.size = additionalFilters.size();
this.firewalledRequest = firewalledRequest;
}
@Override
public void doFilter(ServletRequest request, ServletResponse response) throws IOException, ServletException {
if (this.currentPosition == this.size) {
if (logger.isDebugEnabled()) {
logger.debug(LogMessage.of(() -> "Secured " + requestLine(this.firewalledRequest)));
}
// Deactivate path stripping as we exit the security filter chain
this.firewalledRequest.reset();
this.originalChain.doFilter(request, response);
return;
}
// 通过责任链模式完成过滤器链的调用
this.currentPosition++;
Filter nextFilter = this.additionalFilters.get(this.currentPosition - 1);
if (logger.isTraceEnabled()) {
logger.trace(LogMessage.format("Invoking %s (%d/%d)", nextFilter.getClass().getSimpleName(),
this.currentPosition, this.size));
}
nextFilter.doFilter(request, response, this);
}
}
}
- 通过以上的分析我们知道了
FilterChainProxy
过滤器已经加入了ServletContext
,请求到达的时候会调用doFilter()
- doFilter() 里面进入方法
doFilterInternal()
,里面getFilters()
方法返回第一条匹配中请求的过滤器链,然后使用过滤器链(filters)和请求(fwRequest)以及原始过滤器链(chain)共同创建出类型为VirtualFilterChai
n 虚拟过滤器链对象 vfc,目的是为了在过滤器链上传递请求,这里便是典型的责任链模式 - 之后请求开始被所有 Filter 处理,所有 Filter 都处理过之后会使用原始过滤器调用 doFilter(request, response) 继续被其他的过滤器处理