内容简介:本文是对 Spring Security Core 4.0.4 Release 进行源码分析的系列文章之一;本系列开始,将讲解有关 Spring Security 的配置相关的内容;本文为作者的原创作品,转载需注明出处;
本文是对 Spring Security Core 4.0.4 Release 进行源码分析的系列文章之一;
本系列开始,将讲解有关 Spring Security 的配置相关的内容;
本文为作者的原创作品,转载需注明出处;
简介
在《 Spring Security 源码分析九:Java config - WebSecurity & @EnableWebSecurity 》文中,笔者在HttpSecurity 小节中留了一个悬念,那就是通过 HttpSecurity 的 build() 方法将返回一个SecurityFilterChain 对象,SecurityFilterChain 对象由多个 Filter 所构成,组成了一条 Spring Security 的过滤链,但是并没有深入的去探讨 HttpSecurity 是如何去构建 SecurityFilterChain 对象的内部机制;本篇文章,笔者将试图去回答这个问题;
配置用例
先来看一些简单的用例,
单个 FilterChain 的配置
protected void configure(HttpSecurity http) throws Exception { http .authorizeRequests() .anyRequest().authenticated() .and() .formLogin() .and() .httpBasic(); }
等价于做了如下的配置
<http> <intercept-url pattern="/**" access="authenticated"/> <form-login /> <http-basic /> </http>
- 每一个 and() 操作符就相当于 XML 配置中的一次换行,XML 配置中的每一行代表一个完整的配置;
-
配置 .authorizeRequests().anyRequest().authenticated() 相当于
<intercept-url pattern="/**" access="authenticated"/>
authorizeRequests()表示开始一个 <intercept-url> 配置节点的开始;
anyRequest()表示配置属性 pattern=”/**“;
authenticated()表示配置属性 access=”authenticated”;
上述的配置等价于配置了一个 SecurityFilterChain 对象,既是一个 Spring Security 安全链,因为没有指定安全链的起始链接,所以该安全链默认作用在 /** 访问 request 上的,并且使用默认实现的 form login 和 http basic authentication 对象;
多个 FilterChain 的配置
在 Spring Security 源码分析八:Spring Security 过滤链二 - Demo 例子 的例子中,通过 Java Config 的方式配置了两条不同的 Security Filter Chain;
首先,通过如下的方式配置了一条 /web/** 的安全过滤链,
http.antMatcher("/web/**") // the filter chain defined for web request .authorizeRequests() .antMatchers("/web/report/**").hasRole("MANAGER") .anyRequest().authenticated() .and() .formLogin() // login 的相对路径必须与 security chain 的的相对路径吻合,这里是 /web/**;注意 login 分两步,一步是 Getter 会到 login.html,另外一步是从 login.html -> post -> 6 // 允许访问 .permitAll();
再次,通过如下的方式配置了另一条 /rest/** 的安全过滤链,
http.antMatcher("/rest/**") // the filter chain defined for web request; .csrf().disable() // rest 请求无需 CSRF Token; .authorizeRequests() .antMatchers("/rest/hello").hasRole("USER") .anyRequest().authenticated() .and() .httpBasic();
上述的配置等价如下的配置,
<bean id="filterChainProxy" class="org.springframework.security.web.FilterChainProxy"> <sec:filter-chain-map path-type="ant"> <sec:filter-chain pattern="/web/**" filters=" securityContextPersistenceFilter, logoutFilter, usernamePasswordAuthenticationFilter, exceptionTranslationFilter, filterSecurityInterceptor"> <sec:intercept-url pattern="/web/report/**" access="ROLE_MANAGER"/> </sec:filter-chain> <sec:filter-chain pattern="/rest/**" filters=" securityContextPersistenceFilter, logoutFilter, basicAuthenticationFilter, exceptionTranslationFilter, filterSecurityInterceptor" > <sec:intercept-url pattern="/rest/report/**" access="ROLE_USER"/> </sec:filter-chain> </sec:filter-chain-map> </bean> <!-- logout filter --> <bean id="logoutFilter" class="org.springframework.security.web.authentication.logout.LogoutFilter"> <!-- the post-logout destination --> <constructor-arg value="/"/> <constructor-arg> <array> <ref local="logoutHandler"/> <!--高级配置 添加 rememberMe--> <ref local="rememberMeServices"/> </array> </constructor-arg> <property name="filterProcessesUrl" value="/logout"/> </bean> <bean id="basicAuthenticationFilter" ....> ...... </bean> ......
相关配置说明和其它配置元素
还是以上述单个 FilterChain 的配置为例
http -> HttpSecurity
Java Config 中的配置元素http等价于 XML 配置元素中的<http>;该实例既表示 HttpSecurity 实例本身;
and() : HttpSecurity
Java Config 中的配置元素and()等价于 XML 配置元素中的 换行
操作,也就表示准备要开始配置另外一个 Security 元素了;其实在 Java Config 的过程中,每一次的 and() 操作以后,返回的都是当前 HttpSecuirty 实例;可以看到,每一次相关方法的调用,比如、、返回的都是一个 SecurityConfigurer 对象,而通过调用 SecurityConfigurer.and() 返回的永远都是 SecurityBuilder 对象,这里对应的既是 HttpSecurity;
该部分将会在后面的部分有更详细的介绍;
antMatcher(String antPattern) : HttpSecurity
Java Config 中的配置元素antMatcher(antPattern)类似于进行如下的 XML 配置,
<sec:filter-chain pattern="/web/**" ... > .... </sec:filter-chain>
开启了一个新的 Security Chain 的配置;
authorizeRequests() : ExpressionUrlAuthorizationConfigurer
Java Config 中的配置元素authorizeRequests()等价于 XML 配置元素中的<intercept-url>操作,表示对特定 URL 执行的特定操作;这个方法比较有意思,返回的是 ExpressionUrlAuthorizationConfigurer<HttpSecurity>. ExpressionInterceptUrlRegistry 一个内嵌对象;该部分将会在后面的部分有更详细的介绍;
formLogin() : FormLoginConfigurer
Java Config 中的配置元素formLogin()等价于 XML 配置元素中的<form-login>操作,表示对特定 URL 执行的特定操作;该方法返回的是 FormLoginConfigurer 对象,该部分将会在后面的部分有更详细的介绍;
httpBasic() : HttpBasicConfigurer
Java Config 中的配置元素httpBasic()等价于 XML 配置元素中的<http-basic>操作;该方法返回的是 HttpBasicConfigurer 对象,该部分将会在后面的部分有更详细的介绍;
sessionManagement() : SessionManagementConfigurer
Java Config 中的配置元素sessionManagement()类似于进行如下的 XML 配置,
<session-management> <concurrency-control max-sessions="1" error-if-maximum-exceeded="true" /> </session-management>
该方法返回的是 SessionManagementConfigurer 对象,该部分将会在后面的部分有更详细的介绍;
authenticationProvider( authenticationProvider ) : HttpSecurity
Java Config 中的配置元素authenticationProvider( authenticationProvider )类似于进行如下的 XML 配置,
<authentication-manager> <authentication-provider user-service-ref='myUserDetailsService'/> </authentication-manager>
该方法返回的是当前的 HttpSecurity 对象;
userDetailsService( userDetailsService ) : HttpSecurity
Java Config 中的配置元素userDetailsService()类似于进行如下的 XML 的配置,
<beans:bean id="myUserDetailsService" class="org.springframework.security.core.userdetails.jdbc.JdbcDaoImpl"> <beans:property name="dataSource" ref="dataSource"/> </beans:bean>
portMapper() : PortMapperConfigurer
Java Config 中的配置元素portMapper()类似于进行如下的 XML 配置,
<port-mappings> <port-mapping http="9080" https="9443"/> </port-mappings>
exceptionHandling() : ExceptionHandlingConfigurer
Java Config 中的配置元素exceptionHandling()类似于进行如下的 XML 的配置,
<bean id="exceptionTranslationFilter" class="org.springframework.security.web .access.ExceptionTranslationFilter"> <property name="authenticationEntryPoint" ref="authenticationEntryPoint"/> <property name="accessDeniedHandler" ref="accessDeniedHandler"/> </bean> <bean id="authenticationEntryPoint" class="org.springframework.security.web .authentication.LoginUrlAuthenticationEntryPoint"> <property name="loginFormUrl" value="/login.do"/> </bean> <bean id="accessDeniedHandler" class="org.springframework.security.web .access.AccessDeniedHandlerImpl"> <property name="errorPage" value="/accessDenied.do"/> </bean>
从 ExceptionHandlingConfigurer 的配置方法 configure() 中也可以看到这个逻辑,
@Override public void configure(H http) throws Exception { AuthenticationEntryPoint entryPoint = getAuthenticationEntryPoint(http); ExceptionTranslationFilter exceptionTranslationFilter = new ExceptionTranslationFilter( entryPoint, getRequestCache(http)); if (accessDeniedHandler != null) { exceptionTranslationFilter.setAccessDeniedHandler(accessDeniedHandler); } exceptionTranslationFilter = postProcess(exceptionTranslationFilter); http.addFilter(exceptionTranslationFilter); }
其它配置
如图,展示了 HttpSecuirty 所有相关的配置项,里面还包含 csrf() , logout() , anonymous() , requiresChannel() 等等非常重要的一系列的配置方法;
References
有关 XML 的配置参考 https://docs.spring.io/spring-security/site/docs/3.0.x/reference/ns-config.html
HttpSecurity
SecurityBuilder 的实体类总共有三个,HttpSecurity、WebSecurity 以及 AuthenticationManagerBuilder;下面笔者将逐个的分析每一个相关的类;
SecurityBuilder
SecurityBuilder 接口非常的简单,提供了一个 build() 方法,顾名思义,主要是用来执行构建操作的;
public interface SecurityBuilder<O> { /** * Builds the object and returns it or null. * * @return the Object to be built or null if the implementation allows it. * @throws Exception if an error occurred when building the Object */ O build() throws Exception; }
构建完成以后,将会返回对象 O;
AbstractSecurityBuilder
public abstract class AbstractSecurityBuilder<O> implements SecurityBuilder<O> { private AtomicBoolean building = new AtomicBoolean(); private O object; public final O build() throws Exception { if (building.compareAndSet(false, true)) { object = doBuild(); return object; } throw new AlreadyBuiltException("This object has already been built"); } /** * @return the Object that was built */ public final O getObject() { if (!building.get()) { throw new IllegalStateException("This object has not been built"); } return object; } protected abstract O doBuild() throws Exception; }
三个方法,
-
build()
执行构建动作,通过调用抽象方法 build() 完成构建;
-
getObject()
获得构建成功以后的对象;
-
doBuild()
抽象方法,供子类实现其构建方法;
AbstractConfiguredSecurityBuilder
该抽象类中的逻辑非常的关键了,将其核心关键代码摘要如下,
public abstract class AbstractConfiguredSecurityBuilder<O, B extends SecurityBuilder<O>> extends AbstractSecurityBuilder<O> { ...... @SuppressWarnings("unchecked") public <C extends SecurityConfigurerAdapter<O, B>> C apply(C configurer) throws Exception { configurer.addObjectPostProcessor(objectPostProcessor); configurer.setBuilder((B) this); add(configurer); return configurer; } public <C extends SecurityConfigurer<O, B>> C apply(C configurer) throws Exception { add(configurer); return configurer; } ...... /** * Sets an object that is shared by multiple {@link SecurityConfigurer}. * * @param sharedType the Class to key the shared object by. * @param object the Object to store */ @SuppressWarnings("unchecked") public <C> void setSharedObject(Class<C> sharedType, C object) { this.sharedObjects.put((Class<Object>) sharedType, object); } @SuppressWarnings("unchecked") public <C> C getSharedObject(Class<C> sharedType) { return (C) this.sharedObjects.get(sharedType); } ...... @SuppressWarnings("unchecked") private <C extends SecurityConfigurer<O, B>> void add(C configurer) throws Exception { Class<? extends SecurityConfigurer<O, B>> clazz = (Class<? extends SecurityConfigurer<O, B>>) configurer.getClass(); synchronized (configurers) { .... List<SecurityConfigurer<O, B>> configs = allowConfigurersOfSameType ? this.configurers.get(clazz) : null; .... configs.add(configurer); this.configurers.put(clazz, configs); .... } } ...... @Override protected final O doBuild() throws Exception { synchronized (configurers) { buildState = BuildState.INITIALIZING; beforeInit(); init(); buildState = BuildState.CONFIGURING; beforeConfigure(); configure(); buildState = BuildState.BUILDING; O result = performBuild(); buildState = BuildState.BUILT; return result; } } ...... /** * Subclasses must implement this method to build the object that is being returned. * * @return */ protected abstract O performBuild() throws Exception; @SuppressWarnings("unchecked") private void init() throws Exception { Collection<SecurityConfigurer<O, B>> configurers = getConfigurers(); for (SecurityConfigurer<O, B> configurer : configurers) { configurer.init((B) this); } for (SecurityConfigurer<O, B> configurer : configurersAddedInInitializing) { configurer.init((B) this); } } @SuppressWarnings("unchecked") private void configure() throws Exception { Collection<SecurityConfigurer<O, B>> configurers = getConfigurers(); for (SecurityConfigurer<O, B> configurer : configurers) { configurer.configure((B) this); } } }
好些个核心的方法,下面我们一个一个的来屡吧,
-
apply(C configurer)
这里笔者罗列了两个 apply(C configurer) 方法,目的是为了描述一个调试的 bug,HttpSecurity 的 getOrApply(configurer) 方法,
@SuppressWarnings("unchecked") private <C extends SecurityConfigurerAdapter<DefaultSecurityFilterChain, HttpSecurity>> C getOrApply( C configurer) throws Exception { C existingConfig = (C) getConfigurer(configurer.getClass()); if (existingConfig != null) { return existingConfig; } return apply(configurer); }
如果通过 COMMAND + 鼠标左键点击 apply(configurer) 跳转到的是上述的方法
public <C extends SecurityConfigurer<O, B>> C apply(C configurer) throws Exception { add(configurer); return configurer; }
如果是这样的话,就有一个疑问了,那就是如果通过 configurer.and() 方法获得 HttpSecurity 既是上述的 Build 对象 B ,比如通过 http.csrf().and() 就无法获取 HttpSecurity 对象了,自然也就没有办法继续进行 HttpSecurity 的相关配置了;不过,经过笔者的调试过程中发现,实际上,经过 HttpSecurity.getOrApply(configurer) 方法调用的其实是
@SuppressWarnings("unchecked") public <C extends SecurityConfigurerAdapter<O, B>> C apply(C configurer) throws Exception { configurer.addObjectPostProcessor(objectPostProcessor); configurer.setBuilder((B) this); add(configurer); return configurer; }
该方法中,通过
configurer.setBuilder((B) this)
的方式将 HttpSecurity 赋值给了当前的 configurer,所以自然也就可以通过该 configurer 获取到当前的 HttpSecurity 对象了;Debug 过程的截图如下,可见,调用的正式上述方法; -
add(C configurer)
该方法在 apply(C configurer) 方法中被调用,目的是将 configurer 添加到 builder 的 configurers 中;唯一需要注意的是,configurers 是一个以 configurer class 为
主键
的 Map,其值存储的就是按照 class 分类号的 configurers;为什么这样设计?难不成同一个类型的 configurer class 还会有多个 configurers?笔者暂时还没有想到相应的场景; -
setSharedObject(Class
sharedType, C object)
当需要覆盖掉 configurer 的一些 工具 累的时候,可以使用这个方法;
-
doBuild()
:O
该方法是核心中的核心,执行构建完以后,将返回构建好的对象;笔者在前面分析 WebSecurity 的构建过程 中比较详细的描述了 WebSecurity 的三步构造过程,init、configure 以及 perform build 步骤;现在,我们再从源码的层面,依次看下这三个方法所执行的逻辑,-
init build process
对应前三行代码,
buildState = BuildState.INITIALIZING; beforeInit(); init();
首先,执行 beforeInit() 方法,对应执行的是一个模板方法,子类需要继承并实现该方法来实现一些初始化之前的方法;如下所述,
/** * Invoked prior to invoking each {@link SecurityConfigurer#init(SecurityBuilder)} * method. Subclasses may override this method to hook into the lifecycle without * using a {@link SecurityConfigurer}. */ protected void beforeInit() throws Exception { }
再次,执行 init() 方法,
@SuppressWarnings("unchecked") private void init() throws Exception { Collection<SecurityConfigurer<O, B>> configurers = getConfigurers(); for (SecurityConfigurer<O, B> configurer : configurers) { configurer.init((B) this); } .... }
可以看到,在初始化构建过程当中,会首先依次执行各个 configurer 的 init() 方法;
-
configure build process
这里的逻辑与 init build process 大同小异,同样是先执行模板方法 beforeConfigurer() 方法,需要子类继承实现;然后再依次遍历 configurer 并执行其 configurer() 方法;
-
perform build process
这个过程执行抽象方法 performBuild(),该方法必须由子类实现;也就是说,三个子类 HttpSecurity、WebSecurity 以及 AuthenticationManagerBuilder 都有实现这个方法,这里,我们看看 HttpSecurity 的实现,
@Override protected DefaultSecurityFilterChain performBuild() throws Exception { Collections.sort(filters, comparitor); return new DefaultSecurityFilterChain(requestMatcher, filters); }
首先,根据 filters 的优先级排好序,然后通过构造好的 requestMatcher 和 filters 来初始化一个 DefaultSecurityFilterChain 对象并返回;
-
-
init()
由 doBuild() 方法的 init build process 所调用;
-
configure()
由 doBuild() 方法的 configure build process 所调用;
HttpSecurity
由可知,HttpSecurity 是一个 SecurityBuilder 实例,通过其父类AbstractConfiguredSecurityBuilderdoBuild() 方法的三步构建 init、configure 以及 perform build 最终返回一个 SecurityFilterChain 实例;可见,HttpSecurity 的职责就是通过一系列的 configurer 并且通过三步构建步骤最后生成一个 SecurityFilterChain 实例,该实例最终由 FilterChainProxy 加载;更多有关 HttpSecurity 构建的过程参考HttpSecurity.build 部分内容;
从HttpSecurity 相关配置元素和操作中,可以知道,HttpSecurity 通过各种配置方法注入了不同的 configurers,既对象,并且通过 getOrApply(configurer) 方法将 configurer 注入到父类的configurers队列中(注意是个 Map 对象),
@SuppressWarnings("unchecked") private <C extends SecurityConfigurerAdapter<DefaultSecurityFilterChain, HttpSecurity>> C getOrApply( C configurer) throws Exception { C existingConfig = (C) getConfigurer(configurer.getClass()); if (existingConfig != null) { return existingConfig; } return apply(configurer); }
更多有关此部分的内容参考AbstractConfiguredSecurityBuilder中 apply(C configurer) 小节;另外这里 apply(configurer) 调用的是父类的第一个 apply(C configurer); 注意
,正是因为在 apply 的过程中,将当前的 builder 既 HttpSecurity 对象注入到了 configurer 中,所以,可以通过任意的一个 configurer 对象通过调用其 and() 方法都可以获取到当前的 HttpSecurity 对象;就类似于在配置过程中通过 http.csrf().and() 既可以获取得到 HttpSecurity 对象;
Ok,通过上述分析,我们可以知道,HttpSecurity 通过若干 SecurityConfigurer 配置,通过其 configure() 方法,创建了若干个 Filters,正是这些 Filters 构成了 HttpSecurity 的核心,也正是这些 Filters 构建了 SecurityFilterChain 对象;那么,下面,我们就来一睹的风采;
SecurityConfigurer
由类图可知,SecurityConfigurer 主要分为两大类,WebSecurityConfigurer 和 SecurityConfigureAdapter;
注意,这里有一个关键的行为区别,那就是,WebSecurityConfigurerAdapter 类型的配置类,比如 WebConfigSecurity 和 RestConfigSecurity 不包含与之关联的 SecurityBuilder 实例,这里指的是 WebSecurity;而 SecurityConfigurerAdapter 类型的相关配置类则相反,均包含与其关联的 SecurityBuilder 类型实例,比如 AuthenticationManagerBuilder 以及 HttpSecurity,且是一对一的关系,这也就是为什么通过 SecurityConfigurerAdapter. and() 方法可以获取与之关联的 SecurityBuilder 的根本原因;
WebSecurityConfigurer
该部分参考 WebSecurity 章节的相关内容 WebSecurityConfigurer 的介绍;WebSecurityConfigurer 的主要职责就是扩展出用户自定义的有关 HttpSecurity 的配置;
SecurityConfigurerAdapter
由上述类图可知,包含三个子类,不过这里,笔者主要针对 UserDetailsAwareConfigurer 和 AbstractHttpConfigurer 来进行讲解;并且重要的是,该类提供了两个比较重要的方法;
-
and()
该方法将会始终返回 HttpSecurity 对象;
-
getBuilder()
and() 方法改回调用此方法来获取 Builder 对象,既 HttpSecurity 对象;
AbstractHttpConfigurer
由此类扩展出了多个与 HttpSecurity 相关联的 configurers,比如 HttpBasicConfigurer、FormLoginConfigurer 以及 ExpressionUrlAuthorizationConfigurer;
再来看一下,关注点聚焦到 HttpSecurity 上;
从HttpSecurity.build 的介绍可知 HttpSecurity 的构建的目的就是生成 Filters,然后将这些 Filters 作为构造参数初始化 DefaultSecurityFilterChain 对象并返回;而过程中,Filters 的生成就与 configurers 的方法 configure() 息息相关了;下面,笔者就相关的方法介绍如下,
HttpBasicConfigurer.configure(B http)
参数 B 指的是泛型 Builder,这里既是 HttpSecurity;
@Override public void configure(B http) throws Exception { AuthenticationManager authenticationManager = http .getSharedObject(AuthenticationManager.class); BasicAuthenticationFilter basicAuthenticationFilter = new BasicAuthenticationFilter( authenticationManager, authenticationEntryPoint); if (authenticationDetailsSource != null) { basicAuthenticationFilter .setAuthenticationDetailsSource(authenticationDetailsSource); } RememberMeServices rememberMeServices = http.getSharedObject(RememberMeServices.class); if(rememberMeServices != null) { basicAuthenticationFilter.setRememberMeServices(rememberMeServices); } basicAuthenticationFilter = postProcess(basicAuthenticationFilter); http.addFilter(basicAuthenticationFilter); }
下面笔者就 HttpBasicConfigurer 的配置的过程做进行详细的分析,
SharedObject Filter
ExpressionUrlAuthorizationConfigurer
该对象的 configure 方法在其父类 AbstractInterceptUrlConfigurer 中,
@Override public void configure(H http) throws Exception { FilterInvocationSecurityMetadataSource metadataSource = createMetadataSource(http); if (metadataSource == null) { return; } FilterSecurityInterceptor securityInterceptor = createFilterSecurityInterceptor( http, metadataSource, http.getSharedObject(AuthenticationManager.class)); if (filterSecurityInterceptorOncePerRequest != null) { securityInterceptor .setObserveOncePerRequest(filterSecurityInterceptorOncePerRequest); } securityInterceptor = postProcess(securityInterceptor); http.addFilter(securityInterceptor); http.setSharedObject(FilterSecurityInterceptor.class, securityInterceptor); }
可见,目的正是为了生成一个 FilterSecurityInterceptor 既 Filter 对象,并将其载入到 HttpSecurity 对象中;
FormLoginConfigurer
同样,该对象的 configure 方法也是在其父类 AbstractAuthenticationFilterConfigurer 中;
@Override public void configure(B http) throws Exception { PortMapper portMapper = http.getSharedObject(PortMapper.class); if (portMapper != null) { authenticationEntryPoint.setPortMapper(portMapper); } authFilter.setAuthenticationManager(http .getSharedObject(AuthenticationManager.class)); authFilter.setAuthenticationSuccessHandler(successHandler); authFilter.setAuthenticationFailureHandler(failureHandler); if (authenticationDetailsSource != null) { authFilter.setAuthenticationDetailsSource(authenticationDetailsSource); } SessionAuthenticationStrategy sessionAuthenticationStrategy = http .getSharedObject(SessionAuthenticationStrategy.class); if (sessionAuthenticationStrategy != null) { authFilter.setSessionAuthenticationStrategy(sessionAuthenticationStrategy); } RememberMeServices rememberMeServices = http .getSharedObject(RememberMeServices.class); if (rememberMeServices != null) { authFilter.setRememberMeServices(rememberMeServices); } F filter = postProcess(authFilter); http.addFilter(filter); }
上面代码的核心在 authFilter,而该 authFilter 正是 UsernamePasswordAuthenticationFilter,该 authFilter 的初始化过程在 FormLoginConfigurer 的构造函数中,
public FormLoginConfigurer() { super(new UsernamePasswordAuthenticationFilter(), null); usernameParameter("username"); passwordParameter("password"); }
UserDetailsAwareConfigurer
UserDetailsAwareConfigurer 主要涉及两个实体类 InMemoryUserDetailsManagerConfigurer 和 JdbcUserDetailsManagerConfigurer ;顾名思义,这两个类都是对用户身份信息进行验证的类;如类图中所示,这两个 configurers 将会作为 AuthenticationManagerBuilder 的 configures 来进行构建,最终构建的结果是返回一个 ProviderManager 对象;有关这部分内容笔者将会在下一章节对其进行描述;
总结
正如对 WebSecurity 的总结 那样,WebSecurity 与 HttpSecurity 的关系是一对多的关系,也就是说,HttpSecurity 正是为 Spring Security 构建多个安全链 SecurityFilterChain 的核心类;WebSecurity 将构建出 FilterChainProxy, FilterChainProxy 包含多个 SecurityFilterChain 既安全链;
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:- 以太坊源码分析(36)ethdb源码分析
- [源码分析] kubelet源码分析(一)之 NewKubeletCommand
- libmodbus源码分析(3)从机(服务端)功能源码分析
- [源码分析] nfs-client-provisioner源码分析
- [源码分析] kubelet源码分析(三)之 Pod的创建
- Spring事务源码分析专题(一)JdbcTemplate使用及源码分析
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
ACM国际大学生程序设计竞赛亚洲区预选赛真题题解
郭炜 / 电子工业 / 2011-7 / 49.00元
ACM国际大学生程序设计竞赛(ACM International Collegiate Programming Contest,简称ACM/ICPC)是世界上历史最悠久,规模最大、最具声望的程序设计竞赛,一直受到众多国际知名大学的重视,全球著名IT公司更是争相招募竞赛的优胜者。 该项赛事分为各大洲预选赛和全球总决赛两个阶段。北京大学多次在亚洲区预选赛中负责命题工作,是中国在ACM/ICPC命......一起来看看 《ACM国际大学生程序设计竞赛亚洲区预选赛真题题解》 这本书的介绍吧!