他总是自动弹出来 在那么回事(它会自动弹出来)
917
2022-05-30
本篇摘自胖哥最新的基于Spring Security 5.6.x的《Spring Security干货》教程。旧版的教程将在2022年1月1日下线,请需要的同学尽快通过本公众号回复“2021开工福利”下载。原创不易,请多多关注、、转发。
Spring Security最难的地方就是HttpSecurity的顶层设计。不信你看看HttpSecurity的定义。
public final class HttpSecurity extends AbstractConfiguredSecurityBuilder
感觉不到的话,再给你看看UML图:
为什么要这么复杂?我第一次看到HttpSecurity的结构时我怀疑我自己是不是Java开发。多年以后,当我深入学习了之后才理解了这种设计。作为一个框架,尤其是安全框架,配置必须足够灵活才能适用于更多的业务场景。Spring Security采取了配置与构建分离的架构设计来保证这一点。
配置与构建分离
配置只需要去收集配置项,构建只需要把所有的配置构建成目标对象。各干各的,分离职责,这种做法能够提高代码的可维护性和可读写性。Spring Security利用接口隔离把配置和构建进行高度抽象,提高灵活度,降低复杂度。不过这个体系依然非常庞大。为了降低学习难度需要把大问题拆解成小问题,各个击破,这种学习方法在学习一些复杂的抽象理论时很凑效。
SecurityBuilder
SecurityBuilder就是对构建的抽象。你看上面的类图过于复杂,而看SecurityBuilder就非常的简单了。
public interface SecurityBuilder
就一个动作,构建泛化的目标对象O。通过下面这一组抽象和具体的定义我想你应该明白SecurityBuilder了吧。
// 抽象 SecurityBuilder -> O // 具体 HttpSecurity->DefaultSecurityFilterChain
一句话,构建的活都是我来干。
AbstractSecurityBuilder
AbstractSecurityBuilder是对SecurityBuilder的实现。源码如下:
public abstract class AbstractSecurityBuilder
它通过原子类AtomicBoolean对构建方法build()进行了调用限制:每个目标对象只能被构建一次,避免安全策略发生不一致的情况。构建方法还加了final关键字,不可覆写!构建的核心逻辑通过预留的钩子方法doBuild()来扩展,钩子方法是很常见的一种继承策略。另外AbstractSecurityBuilder还提供了获取已构建目标对象的方法getObject。
一句话,构建的活我只干一次。
HttpSecurityBuilder
public interface HttpSecurityBuilder
HttpSecurityBuilder对DefaultSecurityFilterChain的构建进行了增强,为其构建器增加了一些额外的获取配置或管理配置的入口,参见上面的注释。补充一点这个接口最大的功能就是打通了构建和配置的关系,可以操作下面要讲的SecurityConfigurer。
一句话,我只构建DefaultSecurityFilterChain。
SecurityConfigurer是对配置的抽象。配置只是手段,构建才是目的。因此配置是对构建的配置。
public interface SecurityConfigurer
SecurityConfigurer有两个方法,都非常重要。一个是init方法,这个方法你可以认为是SecurityBuilder构造函数的逻辑。如果你想在SecurityBuilder初始化的时候执行一些逻辑或者在后续配置中共享一些变量的话就可以在init方法中去实现;第二个方法是configure,为SecurityBuilder配置一些必要的属性。到这里还没完?这两个方法有着明确的先后执行顺序。在一次构建内可能有多个SecurityConfigurer,只有全部的init逐个执行完毕后才会逐个执行configure方法。相关的源码在AbstractConfiguredSecurityBuilder中的标记部分:
@Override protected final O doBuild() throws Exception { synchronized (this.configurers) { this.buildState = BuildState.INITIALIZING; beforeInit(); // ① 执行所有的初始化方法 init(); this.buildState = BuildState.CONFIGURING; beforeConfigure(); // ② 执行所有的configure方法 configure(); this.buildState = BuildState.BUILDING; O result = performBuild(); this.buildState = BuildState.BUILT; return result; } }
一句话,配置SecurityBuilder的事都是我来干。
SecurityConfigurerAdapter
SecurityConfigurer在某些场景下是有局限性的,它不能获取正在配置的SecurityBuilder,因此你无法进一步操作SecurityBuilder,配置的扩展性将大打折扣。因此引入了SecurityConfigurerAdapter来扩展SecurityConfigurer。
public abstract class SecurityConfigurerAdapter
这样可以指定SecurityBuilder,而且可以把SecurityBuilder暴露出来,随时随地去调整SecurityBuilder,灵活性大大提高。具体说的话,你可以通过and()方法获取SecurityBuilder并对SecurityBuilder的其它配置项进行操作,比如上图中SecurityConfigurerAdapter之间的切换。除此之外还引入了ObjectPostProcessor来后置操作一些并不开放的内置对象。关于ObjectPostProcessor会找个合适的场景去讲解它。
一句话,配置SecurityBuilder不算什么,灵活适配才是花活。
AbstractHttpConfigurer
不是所有的配置都是有用的,有些配置我们希望有个关闭的入口功能。比如csrf功能,控制着csrf的配置的是CsrfConfigurer,如果CsrfConfigurer有一个关闭功能就好了。因此从SecurityConfigurerAdapter衍生出AbstractHttpConfigurer来满足这个需求。
public abstract class AbstractHttpConfigurer
AbstractHttpConfigurer的实现类非常多,日常的配置项大都由AbstractHttpConfigurer的实现类来控制。这个类是做定制化配置的一个重要入口之一,如果你想精通Spring Security,这个类一定要掌握。
一句话,我能“杀”我自己。
AbstractConfiguredSecurityBuilder
我们希望有多个SecurityConfigurer配置SecurityBuilder,表单登录的、会话管理、csrf等等。用到什么配置什么,让配置基于策略。因此引入了AbstractConfiguredSecurityBuilder。
public
通过上面两个apply方法就可以把所有的SecurityConfigurer适配进来,然后通过doBuilder进行精细化构建生命周期。你可以在各个生命周期阶段进行一些必要的操作。
一句话,所有的配置都由我来进行适配。
总结
我们把Spring Security整个配置构建体系拆分了来看会简单的多一些。即使这样想理解这个体系也绝非靠一篇两篇文章也是不现实的。不过从中也可以看得出一个道理,如果你的代码想高度灵活,就必须把各个生命周期分层地高度抽象才行。
2021版Spring Security实战干货教程即将下线,2022版即将上线!
2021-12-15
更快的Maven来了
2021-12-23
能进这个Java组织的都是大神,现在只有三个中国人
2021-12-22
如果你是头条用户,一定要加入这个程序员组织
2021-12-22
前瞻|Spring 6.0将停止支持Freemarker和JSP
2021-12-20
Spring
版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。