当前位置: 代码网 > it编程>编程语言>Java > 详解HttpSecurity是如何组装过滤器链的

详解HttpSecurity是如何组装过滤器链的

2024年06月12日 Java 我要评论
一 securityfilterchain首先大伙都知道,spring security 里边的一堆功能都是通过 filter 来实现的,无论是认证、rememberme login、会话管理、csr

一 securityfilterchain

首先大伙都知道,spring security 里边的一堆功能都是通过 filter 来实现的,无论是认证、rememberme login、会话管理、csrf 处理等等,各种功能都是通过 filter 来实现的。

所以,我们配置 spring security,说白了其实就是配置这些 filter。

以前旧版继承自 websecurityconfigureradapter 类,然后重写 configure 方法,利用 httpsecurity 去配置过滤器链,这种写法其实不太好理解,特别对于新手来说,可能半天整不明白到底配置了啥。

现在新版写法我觉得更加合理,因为直接就是让开发者自己去配置过滤器链,类似下面这样:

@bean
securityfilterchain securityfilterchain(httpsecurity http) throws exception {
    return http.build();
}

这样开发者更容易理解,自己是在配置配置 securityfilter 过滤器链,因为就是要配置这样一个 bean。

securityfilterchain 是一个接口,这个接口只有一个实现类 defaultsecurityfilterchain。

defaultsecurityfilterchain 中有一个 requestmatcher,通过 requestmatcher 可以识别出来哪些请求需要拦截,拦截下来之后,经由该类的另外一个属性 filters 进行处理,这个 filters 中保存了我们配置的所有 filter。

public final class defaultsecurityfilterchain implements securityfilterchain {

	private final requestmatcher requestmatcher;

	private final list<filter> filters;

}

因此,在配置 securityfilterchain 这个 bean 的时候,我们甚至可以按照如下方式来写:

@bean
securityfilterchain securityfilterchain(httpsecurity http) throws exception {
    return new defaultsecurityfilterchain(new antpathrequestmatcher("/**"));
}

这个配置就表示拦截所有请求,但是拦截下来之后,这些请求所经过的过滤器为空,即拦截所有请求但是不做任何处理。

我们也可以为 defaultsecurityfilterchain 对象去配置过滤器链,大家看到,它的构造器可以传入 filter:

但是,spring security 过滤器数量有 30+,我们平日开发使用的也在 15 个左右,这样一个一个去配置太麻烦了,每个过滤器并非 new 出来就能用了,还要配置各种属性,所以我们一般不会自己一个一个去配置这些过滤器。

二 securityconfigurer

为了简化 spring security 中各个组件的配置,官方推出了 securityconfigurer,securityconfigurer 在 spring security 中扮演着非常重要的角色,其主要作用可以归纳为以下几点:

  • 配置过滤器:在 spring security 中,过滤器链中的每一个过滤器都是通过 xxxconfigurer 来进行配置的,而这些 xxxconfigurer 实际上都是 securityconfigurer 的实现。这意味着 securityconfigurer 负责配置和管理安全过滤器链中的各个组件。
  • 初始化和配置安全构建器:securityconfigurer 接口中定义了两个主要方法:init 和 configure。init 方法用于初始化安全构建器(securitybuilder),而 configure 方法则用于配置这个构建器。这两个方法共同确保了安全组件的正确设置和初始化。
  • 提供扩展点:securityconfigurer 的三个主要实现类(securityconfigureradapter、globalauthenticationconfigureradapter、websecurityconfigurer)为开发者提供了扩展 spring security 功能的点。特别是,大部分的 xxxconfigurer 都是 securityconfigureradapter 的子类,这使得开发者能够轻松地定制和扩展安全配置。
  • 构建安全上下文:通过配置和组合不同的 securityconfigurer 实现,可以构建一个完整的安全上下文,包括身份验证、授权、加密等各个方面,从而确保应用程序的安全性。

换句话说,spring security 中的各种 filter,其实都有各自对应的 xxxconfigurer,通过这些 xxxconfigurer 完成了对 filter 的配置。

举几个简答的例子,如:

  • corsconfigurer 负责配置 corsfilter
  • remembermeconfigurer 负责配置 remembermeauthenticationfilter
  • formloginconfigurer 负责配置 usernamepasswordauthenticationfilter

以上是前置知识。

三 securitybuilder

securitybuilder 是 spring security 框架中的一个核心接口,它体现了建造者设计模式,用于构建和配置安全组件。这个接口并不直接与安全功能如认证或授权的具体实现绑定,而是提供了一种灵活的方式来组织和装配这些功能组件,特别是在构建安全上下文和过滤器链过程中。

核心特点

  • 灵活性与模块化:通过 securitybuilder,开发者可以以模块化的方式添加、移除或替换安全配置中的各个部分,而不需要了解整个安全架构的细节。这促进了代码的复用性和可维护性。
  • 层次结构:在 spring security 中,securitybuilder及其子类形成了一个层次结构,允许配置像嵌套娃娃一样层层深入,每一个层级都可以贡献自己的配置逻辑,最终形成一个完整的安全配置。
  • 安全上下文构建:在认证过程中,securitybuilder 用于构造 securitycontext,该上下文中包含了当前认证主体(通常是用户)的详细信息,以及主体所关联的权限和角色。
  • 过滤器链构建:在 web 应用中,securitybuilder 还用于构建 filterchainproxy,这是一个关键组件,负责组织和执行一系列的过滤器,这些过滤器负责处理 http 请求的安全性,比如检查用户是否已经登录、是否有权限访问某个资源等。

简而言之,在 securitybuilder 的子类 abstractconfiguredsecuritybuilder 中,有一个名为 configurers 的集合,这个集合中保存的就是我们前面所说的各种 xxxconfigure 对象。

abstractconfiguredsecuritybuilder 收集到所有的 xxxconfigure 之后,将来会调用每个 xxxconfigure 的 configure 方法完成过滤器的构建。

另外很重要的一点,httpsecurity 也是一个 securitybuilder。因此,httpsecurity 其实就是帮我们收集各种各样的 xxxconfigure,并存入到 configurers 集合中,以备将来构建过滤器使用。

四 httpsecurity

根据前面的介绍,我们知道,无论我们怎么配置,最终拿到手的一定是一个 defaultsecurityfilterchain 对象,因为这是 securityfilterchain 的唯一实现类。

所以,httpsecurity 其实就是在帮我们配置 defaultsecurityfilterchain,我们看到 httpsecurity 里边就有两个非常关键的属性:

private list<orderedfilter> filters = new arraylist<>();
private requestmatcher requestmatcher = anyrequestmatcher.instance;

这两个恰恰就是构建 defaultsecurityfilterchain 所需的关键参数。

事实确实如此,我们看到,在 httpsecurity#performbuild 方法中,就是利用这两个参数构建出来了 defaultsecurityfilterchain:

@override
protected defaultsecurityfilterchain performbuild() {
	expressionurlauthorizationconfigurer<?> expressionconfigurer = getconfigurer(
			expressionurlauthorizationconfigurer.class);
	authorizehttprequestsconfigurer<?> httpconfigurer = getconfigurer(authorizehttprequestsconfigurer.class);
	boolean oneconfigurerpresent = expressionconfigurer == null ^ httpconfigurer == null;
	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);
}

这里先对 filters 进行排序,然后据此创建出来 defaultsecurityfilterchain 对象。

那么问题来了,filters 中的过滤器从何而来呢?

前面我们提到,httpsecurity 本质上也是一个 securitybuilder,我们平时在 httpsecurity 配置的各种东西,本质上其实就是一个 xxxconfigure,这些 xxxconfigure 被 httpsecurity 收集起来,最后会遍历收集起来的 xxxconfigure,调用其 configure 方法,最终获取过滤器,并将获取到的过滤器存入到 filters 集合中。

这里松哥以我们最为常见的登录配置为例来和大家捋一捋这个流程。

新版的登录配置我们一般按照如下方式来配置:

http.formlogin(f -> f.permitall());

这个方法如下:

public httpsecurity formlogin(customizer<formloginconfigurer<httpsecurity>> formlogincustomizer) throws exception {
	formlogincustomizer.customize(getorapply(new formloginconfigurer<>()));
	return httpsecurity.this;
}

从这段源码可以看到,我们配置里边写的 lambda,其实就是在配置 formloginconfigurer 对象。

getorapply 方法主要主要有两方面的作用:

  • 确保这个 bean 不会重复配置。
  • 如果该 bean 是第一次配置,那么将该对象添加到 securitybuilder 中(其实就是 httpsecurity 对象自身),这个 securitybuilder 里边保存了所有配置好的 xxxconfigure 对象,将来在过滤器链构建的时候,会去遍历这些 xxxconfigure 对象并调用其 configure 方法,完成过滤器的构建。

在 formloginconfigurer#init 方法中,完成了和登录相关过滤器的配置,如:

  • 登录请求处理过滤器(构造方法中完成的)
  • 注销过滤器的配置
  • 登录失败端点配置
  • 登录页面的配置

这里涉及到的属性都会在对应的 xxxconfigurer 中完成配置。

当过滤器链开始构建的时候,会调用到所有 xxxconfigurer 的 configure 方法,在这个方法中,最终完成相关过滤器的创建,并将之添加到 httpsecurity 的 filters 属性这个集合中。

formloginconfigurer 的 configure 方法在其父类中,如下:

@override
public void configure(b http) throws exception {
	portmapper portmapper = http.getsharedobject(portmapper.class);
	if (portmapper != null) {
		this.authenticationentrypoint.setportmapper(portmapper);
	}
	requestcache requestcache = http.getsharedobject(requestcache.class);
	if (requestcache != null) {
		this.defaultsuccesshandler.setrequestcache(requestcache);
	}
	this.authfilter.setauthenticationmanager(http.getsharedobject(authenticationmanager.class));
	this.authfilter.setauthenticationsuccesshandler(this.successhandler);
	this.authfilter.setauthenticationfailurehandler(this.failurehandler);
	if (this.authenticationdetailssource != null) {
		this.authfilter.setauthenticationdetailssource(this.authenticationdetailssource);
	}
	sessionauthenticationstrategy sessionauthenticationstrategy = http
		.getsharedobject(sessionauthenticationstrategy.class);
	if (sessionauthenticationstrategy != null) {
		this.authfilter.setsessionauthenticationstrategy(sessionauthenticationstrategy);
	}
	remembermeservices remembermeservices = http.getsharedobject(remembermeservices.class);
	if (remembermeservices != null) {
		this.authfilter.setremembermeservices(remembermeservices);
	}
	securitycontextconfigurer securitycontextconfigurer = http.getconfigurer(securitycontextconfigurer.class);
	if (securitycontextconfigurer != null && securitycontextconfigurer.isrequireexplicitsave()) {
		securitycontextrepository securitycontextrepository = securitycontextconfigurer
			.getsecuritycontextrepository();
		this.authfilter.setsecuritycontextrepository(securitycontextrepository);
	}
	this.authfilter.setsecuritycontextholderstrategy(getsecuritycontextholderstrategy());
	f filter = postprocess(this.authfilter);
	http.addfilter(filter);
}

关键在最后这一句 http.addfilter(filter);,这句代码将配置好的过滤器放到了 httpsecurity 的 filters 集合中。

其他的也都类似,例如配置 corsfilter 的 corsconfigurer#configure 方法,如下:

@override
public void configure(h http) {
	applicationcontext context = http.getsharedobject(applicationcontext.class);
	corsfilter corsfilter = getcorsfilter(context);
	http.addfilter(corsfilter);
}

这些被添加到 httpsecurity 的 filters 属性上的 filter,最终就成为了创建 defaultsecurityfilterchain 的原材料。

类似的道理,我们也可以分析出 disable 方法的原理,例如我们要关闭 csrf,一般配置如下:

.csrf(c -> c.disable());

那么很明显,这段代码其实就是调用了 csrfconfigurer 的 disable 方法:

public b disable() {
	getbuilder().removeconfigurer(getclass());
	return getbuilder();
}

该方法直接从 securitybuilder 的 configurers 集合中移除了 csrfconfigurer,所以导致最终调用各个 xxxconfigure 的 configure 方法的时候,没有 csrfconfigurer#configure 了,就导致 csrf 过滤器没有配置上,进而 csrf filter 失效。

以上就是详解httpsecurity是如何组装过滤器链的的详细内容,更多关于httpsecurity组装过滤器链的资料请关注代码网其它相关文章!

(0)

相关文章:

版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。

发表评论

验证码:
Copyright © 2017-2025  代码网 保留所有权利. 粤ICP备2024248653号
站长QQ:2386932994 | 联系邮箱:2386932994@qq.com