Spring Security 是如何起作用的

我们知道, Spring Security配置成功后, 可以拦截制定的http请求来进行检证和授权.
拦截http请求, 通常都是Filter做的事情, 那Spring Security的Filter是哪个类, 又是怎么生效的呢?

@EnableWebSecurity简析

@EnableWebSecurity是开启Spring Security的注解. 主要代码有:

@Import({ WebSecurityConfiguration.class,
		SpringWebMvcImportSelector.class,
		OAuth2ImportSelector.class })

首先分析:WebSecurityConfiguration
有一个创建Filter的方法:

	@Bean(name = AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME)
	public Filter springSecurityFilterChain() throws Exception {
		boolean hasConfigurers = webSecurityConfigurers != null
				&& !webSecurityConfigurers.isEmpty();
		if (!hasConfigurers) {
			WebSecurityConfigurerAdapter adapter = objectObjectPostProcessor
					.postProcess(new WebSecurityConfigurerAdapter() {
					});
			webSecurity.apply(adapter);
		}
		return webSecurity.build();
	}

 

 

这个Bean的name是: springSecurityFilterChain

这里引起了2个问题:

  1. 一般的Filter需要通过Servlet的标准注解, 才能让Servlet容器识别并生效, 并且还要配置匹配的url等信息.通过Spring 搞出的这个Filter只是一个受Spring 容器管理的一个Bean而已, 并不能被Servlet容器识别, 响应http请求.
  2. 这个Filter的真正实现类是什么? webSecurity.build()只是一个构建者模式的表达.

我们先分析问题2.
build()方法的定义实际上是在AbstractSecurityBuilder中定义:

 

真正的逻辑委托给抽象方法doBuild实现, 显然doBuild需要子类去实现:

也就是在AbstractConfiguredSecurityBuilder(WebSecurity的父类)中实现的:

 

可见核心是委托给了抽象方法:performBuild, 这个performBuild的实现是在WebSecurity中, 所以现在把焦点移回到:WebSecurity的performBuild

 

这里终于知道这个Filter的真正身份了:
FilterChainProxy filterChainProxy = new FilterChainProxy(securityFilterChains);
就是这个:FilterChainProxy

看看这个类的构造子:

 

里面包含了一个SecurityFilterChain的集合.
再看下SecurityFilterChain

里面包含了一组真正的Filter.
下面我们分析下这个: securityFilterChains
我们再看下performBuild()的前面几行代码:

		int chainSize = ignoredRequests.size() + securityFilterChainBuilders.size();
		List<SecurityFilterChain> securityFilterChains = new ArrayList<>(
				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);

可以看到这个securityFilterChains的集合的值来源于2部分:ignoredRequestssecurityFilterChainBuilders

ignoredRequests就是我们不想应用过滤器的url的集合, 就是通常配置在Spring Security的配置类中的, 比如有个自定义的配置类:

public class WebSecurityConfig extends WebSecurityConfigurerAdapter implements InitializingBean {
}

里面关于有些不要经过Filter chain的url的定义方式如下:

    @Override
    public void configure(WebSecurity web) throws Exception {

        List<String> antUrlList = authProperties().getAntUrl();
        antUrlList.add("/404");
        antUrlList.add("/403");
        antUrlList.add("/500");
        antUrlList.add("/error");
        String[] antUrls = authProperties().getAntUrl().toArray(new String[antUrlList.size()]);
        web.ignoring().antMatchers(antUrls);
        super.configure(web);
    }

下面主要看看:securityFilterChainBuilders是什么
默认情况下securityFilterChainBuilders里面只有一个对象,那就是HttpSecurity
具体原理, 我们后面分析.

到这里, 我们知道了FilterChainProxy这个Filter 表示的就是:springSecurityFilterChain

那它是怎么起作用的呢, 这就引入了:DelegatingFilterProxy
AbstractSecurityWebApplicationInitializer实现了WebApplicationInitializer接口. 这个接口会被Servlet识别并调用onStartup()方法.
也就是说, DelegatingFilterProxy可以被Servlet容器识别.
在AbstractSecurityWebApplicationInitializer的有:

可见:在onStartup()方法里调用了:insertSpringSecurityFilterChain, 这里创建了
DelegatingFilterProxy, 它是一个真正的filter

默认拦截所有的请求. 拦下来后做什么呢?

我们再看DelegatingFilterProxy

其父类GenericFilterBean继承了Filter, 所以本质上DelegatingFilterProxy也是一个Filter, 它的doFilter方法:

	protected void invokeDelegate(
			Filter delegate, ServletRequest request, ServletResponse response, FilterChain filterChain)
			throws ServletException, IOException {

		delegate.doFilter(request, response, filterChain);
	}

也就是说delegate才是真正的doFilter的宿主.
那:delegate是什么呢?

那doFilter()之前会执行init()方法, 看代码发现最终会执行到:

	@Override
	protected void initFilterBean() throws ServletException {
		synchronized (this.delegateMonitor) {
			if (this.delegate == null) {
				// If no target bean name specified, use filter name.
				if (this.targetBeanName == null) {
					this.targetBeanName = getFilterName();
				}
				// Fetch Spring root application context and initialize the delegate early,
				// if possible. If the root application context will be started after this
				// filter proxy, we'll have to resort to lazy initialization.
				WebApplicationContext wac = findWebApplicationContext();
				if (wac != null) {
					this.delegate = initDelegate(wac);
				}
			}
		}
	}

这里最关键的就变成了:targetBeanName

再看看DelegatingFilterProxy的构造子:

	public DelegatingFilterProxy(String targetBeanName, @Nullable WebApplicationContext wac) {
		Assert.hasText(targetBeanName, "Target Filter bean name must not be null or empty");
		this.setTargetBeanName(targetBeanName);
		this.webApplicationContext = wac;
		if (wac != null) {
			this.setEnvironment(wac.getEnvironment());
		}
	}

那这个构造函数又是怎么调用的呢?

让我们接着抽丝剥茧, 离真相已经非常接近了!
利用IDE的find usage功能, 查看这个构造谁调用了:

只有一处类的调用,没错,就是它:
DelegatingFilterProxyRegistrationBean

	@Override
	public DelegatingFilterProxy getFilter() {
		return new DelegatingFilterProxy(this.targetBeanName,
				getWebApplicationContext()) {

			@Override
			protected void initFilterBean() throws ServletException {
				// Don't initialize filter bean on init()
			}

		};
	}

我cha, 这个吊玩意, 又是:this.targetBeanName, this.targetBeanName,是啥?

我cha, 居然又是它的构造的参数, 那这个玩意又是谁跟谁生成来的呢? 真相真的不远了.
再次祭出神器, 看看这个鸟玩意是谁生的?

终于发现是:SecurityFilterAutoConfiguration

没错 ,就是它:private static final String DEFAULT_FILTER_NAME = AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME;
他:

cha, 蹂躏它一百遍!!!!

就是它:springSecurityFilterChain, 它真正对应的是谁, 还记得吗???, 往前翻:FilterChainProxy
好, 那最后一个问题: SecurityFilterAutoConfiguration是咋执行的?
让我们再次关注这个文件:

发现org.springframework.boot.autoconfigure.EnableAutoConfiguration的值里面是有SecurityFilterAutoConfiguration的

OK, 那一切都明了!

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值