SpringSecurity最难的地方就是HttpSecurity的顶层设计。如果您不相信我,请查看HttpSecurity的定义。publicfinalclassHttpSecurityextendsAbstractConfiguredSecurityBuilderimplementsSecurityBuilder,HttpSecurityBuilder{//Omitting}没感觉的话我给你看UML图时间?我自己不是Java开发人员。多年以后,当我深入研究它时,我明白了这个设计。作为框架,尤其是安全框架,配置必须足够灵活,才能适用于更多的业务场景。SpringSecurity采用配置和构造分离的架构设计来保证这一点。配置与构建分离Configuration只需要收集配置项,而Construction只需要将所有配置构建成目标对象。各司其职,分工负责。这种做法可以提高代码的可维护性和可读性。SpringSecurity使用接口隔离,对配置和构造进行高度抽象,提高灵活性,降低复杂度。但是,系统仍然非常庞大。为了降低学习难度,需要把大问题拆成小问题,一个一个分解。这种学习方法在学习一些复杂的抽象理论时非常有效。SecurityBuilderSecurityBuilder是构造的抽象。可以看到上面的类图太复杂了,而SecurityBuilder却很简单。publicinterfaceSecurityBuilder{//BuildObuild()throwsException;}通过一个操作构建一个广义目标对象O。通过下面这组抽象和具体的定义,我想你应该了解了SecurityBuilder。//AbstractSecurityBuilder->O//ConcreteHttpSecurity->DefaultSecurityFilterChain总之一句话,我来做所有的构建工作。publicabstractclassAbstractSecurityBuilderimplementsSecurityBuilder{privateAtomicBooleanbuilding=newAtomicBoolean();privateOobject;@OverridepublicfinalObuild()throwsException{if(this.building.compareAndSet(false,true)){//构造的核心逻辑由hook提供methodthis.object=doBuild();returnthis.object;}thrownewAlreadyBuiltException("Thisobjecthasalreadybeenbuilt");}//获取构建目标对象publicfinalOgetObject(){if(!this.building.get()){thrownewIllegalStateException("Thisobjecthasnot已建成");}returnthis。object;}/***hookmethod*/protectedabstractOdoBuild()throwsException;}它使用原子类AtomicBoolean来限制对构建方法build()的调用:每个目标对象只能构建一次,以避免安全策略不一致。build方法中也加入了final关键字,不可覆盖!构建的核心逻辑是通过保留的钩子方法doBuild()来扩展的,这是一种很常见的继承策略。另外,AbstractSecurityBuilder还提供了getObject方法来获取构造的目标对象。总之,我只做一次建筑工作。HttpSecurityBuilderpublicinterfaceHttpSecurityBuilder>extendsSecurityBuilder{//根据类名获取配置>CgetConfigurer(Classclazz);//根据类名移除配置>CremoveConfigurer(Classclazz);//设置一个对象为共享对象,这样就可以在多个SecurityConfigurer中使用。voidsetSharedObject(ClasssharedType,Cobject);//获取一个共享对象;//在过滤器链中已有的afterFilter类之后注册一个过滤器HaddFilterAfter(Filterfilter,ClassafterFilter);//在过滤器链中已有的beforeFilter类之前注册一个过滤器HaddFilterBefore(Filterfilter,ClassbeforeFilter);//在过滤器中链注册一个过滤器,必须在内置的RegistryFilterOrderRegistrationHaddFilter(Filterfilter);}HttpSecurityBuilder增强DefaultSecurityFilterChain的构造,为其builder添加一些额外的获取配置或管理配置的入口,见上文注意补充的是这个接口最大的作用就是打通了构造和配置的关系,可以操作下面要讲的SecurityConfigurer。一句话,我只搭建DefaultSecurityFilterChain。SecurityConfigurerSecurityConfigurer是配置的抽象。配置只是手段,建设才是目的。所以一个配置就是一个构建的配置。publicinterfaceSecurityConfigurer>{//builder初始化后续信息共享需要注入的配置voidinit(Bbuilder)throwsException;//其他一些必要的配置voidconfigure(Bbuilder)throwsException;}SecurityConfigurer有两个方法是很重要。一个是init方法,您可以将其视为SecurityBuilder构造函数的逻辑。如果想在SecurityBuilder初始化的时候执行一些逻辑或者在后续的配置中共享一些变量,可以在init方法中进行;第二个方法是configure,它为SecurityBuilder配置一些必要的属性。还没结束?这两个方法有明确的执行顺序。一个构建中可能有多个SecurityConfigurer,只有所有的init都一个一个执行完后,configure方法才会一个一个执行。相关源码在AbstractConfiguredSecurityBuilder标注部分:@OverrideprotectedfinalOdoBuild()throwsException{synchronized(this.configurers){this.buildState=BuildState.INITIALIZING;beforeInit();//①执行所有初始化方法init();this.buildState=BuildState.CONFIGURING;beforeConfigure();//②执行所有configure方法configure();this.buildState=BuildState.BUILDING;Oresult=performBuild();this.buildState=BuildState.BUILT;returnresult;}}一句话,我将完成SecurityBuilder的所有配置。SecurityConfigurerAdapterSecurityConfigurer在某些场景下有局限性。它无法获取到正在配置的SecurityBuilder,也就无法进一步操作SecurityBuilder,配置的可扩展性会大大降低。因此引入SecurityConfigurerAdapter来扩展SecurityConfigurer。publicabstractclassSecurityConfigurerAdapter>implementsSecurityConfigurer{privateBsecurityBuilder;privateCompositeObjectPostProcessorobjectPostProcessor=newCompositeObjectPostProcessor();@Overridepublicvoidinit(Bbuilder)throwsException{}@Overridepublicvoidconfigure(Bbuilder)throwsException{}//获取正在配置的构建器,以apipublicBand(){returngetBuilder();}protectedfinalBgetBuilder(){Assert.state(this.securityBuilder!=null,"securityBuildercannotbenull");returnthis.securityBuilder;}//使用复合对象后处理器处理对象,改变一些对象的属性@SuppressWarnings("unchecked")protectedTpostProcess(Tobject){return(T)this.objectPostProcessor.postProcess(object);}//添加一个ObjectPostProcessor以符合builderpublicvoidaddObjectPostProcessor(ObjectPostProcessor>objectPostProcessor){this.objectPostProcessor.addObjectPostProcessor(objectPostProcessor);}//设置需要配置的builder,让多个SecurityConfigurerAdapters可以配置一个SecurityBuilderpublicvoidsetBuilder(Bbuilder){this.securityBuilder=builder;}//其他省略}这样既可以指定SecurityBuilder,又可以暴露SecurityBuilder,随时随地调整SecurityBuilder,大大提高了灵活性。具体可以使用and()方法获取SecurityBuilder,操作SecurityBuilder的其他配置项,比如上图中SecurityConfigurerAdapter之间的切换。另外还引入了ObjectPostProcessor,对一些没有打开的内置对象进行后处理。关于ObjectPostProcessor,我会找一个合适的场景来说明。总之,配置SecurityBuilder没什么,灵活适配才是诀窍。AbstractHttpConfigurer并不是所有的配置都有用,有些配置我们希望有一个封闭入口的功能。比如csrf函数,控制csrf配置的就是CsrfConfigurer,如果CsrfConfigurer有一个shutdown函数就好了。因此,从SecurityConfigurerAdapter派生出AbstractHttpConfigurer来满足这个需求。publicabstractclassAbstractHttpConfigurer,BextendsHttpSecurityBuilder>extendsSecurityConfigurerAdapter{//关闭当前配置@SuppressWarnings("unturnchecked")publicgetreBuilderClasser()();{getveBuilderReconfiger()););}//增强了父类新的ObjectPostProcessor方法@SuppressWarnings("unchecked")publicTwithObjectPostProcessor(ObjectPostProcessor>objectPostProcessor){addObjectPostProcessor(objectPostProcessor);return(T)this;}}AbstractHttpConfigurer有很多实现类,大部分日常的配置项由AbstractHttpConfigurer的实现类控制。该类是自定义配置的重要入口之一。想要精通SpringSecurity,必须掌握这门课。一句话,我可以“杀死”自己。AbstractConfiguredSecurityBuilder我们希望有多个SecurityConfigurer来配置SecurityBuilder、表单登录、session管理、csrf等。使用什么配置,让配置以策略为准。因此引入了AbstractConfiguredSecurityBuilder。public>Capply(Cconfigurer)throwsException{//将objectPostProcessor注入configurerconfigurer.addObjectPostProcessor(this.objectPostProcessor);//为SecurityConfigurerAdapter设置Builder,这样就可以获取了//注意和其他的不一样SecurityConfigurer配置器。((B)this);add(configurer);returnconfigurer;}public>Capply(Cconfigurer)throwsException{add(configurer);returnconfigurer;}通过以上两个apply方法,将所有的SecurityConfigurer适配进去,然后使用doBuilder细化构造生命周期。您可以在各个生命周期阶段执行一些必要的操作。总而言之,所有配置都是我改编的。总结我们将SpringSecurity的整个配置构建体系拆分开来,看会更简单。即使你想这样理解这个系统,依靠一两篇文章也绝不是不现实的。但是,从这里也可以看出,如果你的代码想要高度灵活,就必须对每个生命周期进行分层和高度抽象。本文转载自微信公众号“码农小胖哥”,可通过以下二维码关注。转载本文请联系码农小胖公众号。