前篇:Spring Security源码学习——建造者基础架构
HttpSecurity 也是 Spring Security 中的重要一环。我们平时所做的大部分 Spring Security 配置也都是基于 HttpSecurity 来配置的。因此我们有必要从源码的角度来理解下 HttpSecurity 到底干了啥?
HttpSecurity的目标是构建一个 SecurityFilterChain 过滤器链实例,它 掌握着所有Filter的“生杀大权”,想要谁过滤就配置起来,不想要谁过滤就不配置或者禁用掉(因为有些会被默认配置)。
大致可以将其内方法分为两大类型,一类是框架默认给开发者提供的认证配置方法,可供开发者选择性调用;另一种是可供开发者配置自己的过滤器
public final class HttpSecurity //忽略代码..... {
private final RequestMatcherConfigurer requestMatcherConfigurer;
private List<Filter> filters = new ArrayList<>();//过滤器池
private RequestMatcher requestMatcher = AnyRequestMatcher.INSTANCE;
private FilterComparator comparator = new FilterComparator();
@Override
//先给过滤器排序,然后构造 DefaultSecurityFilterChain 对象
protected DefaultSecurityFilterChain performBuild() {
filters.sort(comparator);
return new DefaultSecurityFilterChain(requestMatcher, filters);
}
//如果当前 xxxConfigurer 已经配置过了,则直接返回,否则调用 apply 方法,这个 apply 方法最终会调用到 AbstractConfiguredSecurityBuilder#add 方法,将当前配置 configurer 收集起来
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);
}
@Override
protected void beforeConfigure() throws Exception {
setSharedObject(AuthenticationManager.class, getAuthenticationRegistry().build());
}
private AuthenticationManagerBuilder getAuthenticationRegistry() {
return getSharedObject(AuthenticationManagerBuilder.class);
}
}
指定支持 基于表单 的身份验证
public final class HttpSecurity ...//忽略代码...{
private final LinkedHashMap<Class<? extends SecurityConfigurer<O, B>>, List<SecurityConfigurer<O, B>>> configurers = new LinkedHashMap<>();
private final List<SecurityConfigurer<O, B>> configurersAddedInInitializing = new ArrayList<>();
public FormLoginConfigurer<HttpSecurity> formLogin() throws Exception {
//将FormLoginConfigurer放到configurers或者configurersAddedInInitializing中
//FormLoginConfigurer底层是会生成一个UsernamePasswordAuthenticationFilter
return getOrApply(new FormLoginConfigurer<>());
}
}
示例:最基本的配置是默认生成登录地址是 /login 的登录页,如果验证失败的话会重定向到 /login?error 页面。
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/**").hasRole("USER")
.and().formLogin();
}
配置基于 OpenID 的认证
public final class HttpSecurity ...//忽略代码...{
private final LinkedHashMap<Class<? extends SecurityConfigurer<O, B>>, List<SecurityConfigurer<O, B>>> configurers = new LinkedHashMap<>();
private final List<SecurityConfigurer<O, B>> configurersAddedInInitializing = new ArrayList<>();
public OpenIDLoginConfigurer<HttpSecurity> openidLogin() throws Exception {
return getOrApply(new OpenIDLoginConfigurer<>());
}
}
示例:启用 OpenID 认证
@Configuration
@EnableWebSecurity
public class OpenIDLoginConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().antMatchers("/**").hasRole("USER").and()
.openidLogin()
.permitAll();
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication()
// 用户名必须与登录用户的OpenID匹配
.withUser(
"https://www.google.com/accounts/o8/id?id=lmkCn9xzPdsxVwG7pjYMuDgNNdASFmobNkcRPaWU")
.password("password").roles("USER");
}
}
向响应添加 请求安全头 。当使用 WebSecurityConfigurerAdapter的默认构造函数时,它会被默认激活
public final class HttpSecurity ...//忽略代码...{
private final LinkedHashMap<Class<? extends SecurityConfigurer<O, B>>, List<SecurityConfigurer<O, B>>> configurers = new LinkedHashMap<>();
private final List<SecurityConfigurer<O, B>> configurersAddedInInitializing = new ArrayList<>();
public HeadersConfigurer<HttpSecurity> headers() throws Exception {
return getOrApply(new HeadersConfigurer<>());
}
}
示例1:只调用headers,其实相当于调用了以下的所有方法
@Override
protected void configure(HttpSecurity http) throws Exception {
http.headers()
.contentTypeOptions()
.and()
.xssProtection()
.and()
.cacheControl()
.and()
.httpStrictTransportSecurity()
.and()
.frameOptions()
.and()
// ...
;
}
示例2:当然你也可以禁用 headers()
@Override
protected void configure(HttpSecurity http) throws Exception {
http.headers()
.disable()
;
}
示例3:你也可以使用部分请求头,前提你需要调用 {@link HeadersConfigurer#defaultsDisabled()} 先关闭所有,然后打开你想要的请求头
@Override
protected void configure(HttpSecurity http) throws Exception {
http.headers()
.defaultsDisabled()
.cacheControl()
.and()
.frameOptions()
.and()
// ...
;
}
示例4:同样你可以选择默认值,而关闭某些特定的请求头
@Override
protected void configure(HttpSecurity http) throws Exception {
http.headers()
.frameOptions()
.disable()
.and()
// ...
;
}
添加要使用的CorsFilter。如果提供了一个名为 corsFilter 的bean,则使用CorsFilter添加该Filter。
public final class HttpSecurity ...//忽略代码...{
private final LinkedHashMap<Class<? extends SecurityConfigurer<O, B>>, List<SecurityConfigurer<O, B>>> configurers = new LinkedHashMap<>();
private final List<SecurityConfigurer<O, B>> configurersAddedInInitializing = new ArrayList<>();
public CorsConfigurer<HttpSecurity> cors() throws Exception {
return getOrApply(new CorsConfigurer<>());
}
}
允许配置 Session 会话管理
public final class HttpSecurity ...//忽略代码...{
private final LinkedHashMap<Class<? extends SecurityConfigurer<O, B>>, List<SecurityConfigurer<O, B>>> configurers = new LinkedHashMap<>();
private final List<SecurityConfigurer<O, B>> configurersAddedInInitializing = new ArrayList<>();
public SessionManagementConfigurer<HttpSecurity> sessionManagement() throws Exception {
return getOrApply(new SessionManagementConfigurer<>());
}
}
下面的配置演示如何强制一次只对用户的单个实例进行身份验证。如果用户使用用户名 user 进行身份验证而没有注销,并且尝试再次使用 user 进行身份验证,第一个会话将被强制终止并发送到 /login?expired URL。
当使用 SessionManagementConfigurer方法maximumSessions()时,不要忘记为应用程序配置HttpSessionEventPublisher ,以确保过期的会话被清除。
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.anyRequest().hasRole("USER")
.and()
.formLogin().permitAll()
.and()
.sessionManagement().maximumSessions(1)
.expiredUrl("/login?expired");
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication()
.withUser("user").password("password")
.roles("USER");
}
端口映射
public final class HttpSecurity ...//忽略代码...{
private final LinkedHashMap<Class<? extends SecurityConfigurer<O, B>>, List<SecurityConfigurer<O, B>>> configurers = new LinkedHashMap<>();
private final List<SecurityConfigurer<O, B>> configurersAddedInInitializing = new ArrayList<>();
public PortMapperConfigurer<HttpSecurity> portMapper() throws Exception {
return getOrApply(new PortMapperConfigurer<>());
}
}
示例:提供的SecurityConfigurer对象在从HTTP重定向到HTTPS,或从HTTPS重定向到HTTP时,使用这个配置作为默认的PortMapper。默认情况下,Spring Security使用PortMapperImpl将HTTP端口8080映射到HTTPS端口8443,将HTTP端口80映射到HTTPS端口443。
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/**").hasRole("USER")
.and()
.formLogin().permitAll()
.and()
.portMapper().http(9090).mapsTo(9443).http(80).mapsTo(443);
}
配置 基于容器 的预认证。在本例中,身份验证由Servlet容器管理。
public final class HttpSecurity ...//忽略代码...{
private final LinkedHashMap<Class<? extends SecurityConfigurer<O, B>>, List<SecurityConfigurer<O, B>>> configurers = new LinkedHashMap<>();
private final List<SecurityConfigurer<O, B>> configurersAddedInInitializing = new ArrayList<>();
public JeeConfigurer<HttpSecurity> jee() throws Exception {
return getOrApply(new JeeConfigurer<>());
}
}
示例:这个示例将使用 {@link HttpServletRequest} 上找到的用户,如果用户是角色 ROLE_USER 或 ROLE_ADMIN ,则将其添加到生成的 {@link Authentication} 中。
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/**").hasRole("USER")
.and()
.jee().mappableRoles("ROLE_USER", "ROLE_ADMIN");
}
配置基于 X509 的预认证。
public final class HttpSecurity ...//忽略代码...{
private final LinkedHashMap<Class<? extends SecurityConfigurer<O, B>>, List<SecurityConfigurer<O, B>>> configurers = new LinkedHashMap<>();
private final List<SecurityConfigurer<O, B>> configurersAddedInInitializing = new ArrayList<>();
public X509Configurer<HttpSecurity> x509() throws Exception {
return getOrApply(new X509Configurer<>());
}
}
示例:这个示例将尝试从 X509 证书中提取用户名。请记住,需要配置Servlet容器来请求客户端证书,这样才能工作
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/**").hasRole("USER")
.and()
.x509();
}
配置 Remember Me 认证。
public final class HttpSecurity ...//忽略代码...{
private final LinkedHashMap<Class<? extends SecurityConfigurer<O, B>>, List<SecurityConfigurer<O, B>>> configurers = new LinkedHashMap<>();
private final List<SecurityConfigurer<O, B>> configurersAddedInInitializing = new ArrayList<>();
public RememberMeConfigurer<HttpSecurity> rememberMe() throws Exception {
return getOrApply(new RememberMeConfigurer<>());
}
}
示例:下面的配置演示了如何允许基于令牌的 remember me 身份验证。在进行身份验证时,如果名为 remember-me 的HTTP参数存在,那么即使在他们的 {@link javax.servlet.http.HttpSession} 过期失效之后,用户也会被记住。
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/**").hasRole("USER")
.and().formLogin()
.permitAll().and()
.rememberMe();
}
配置基于 {@link HttpServletRequest} 使用限制访问
public final class HttpSecurity ...//忽略代码...{
private final LinkedHashMap<Class<? extends SecurityConfigurer<O, B>>, List<SecurityConfigurer<O, B>>> configurers = new LinkedHashMap<>();
private final List<SecurityConfigurer<O, B>> configurersAddedInInitializing = new ArrayList<>();
public ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry authorizeRequests()
throws Exception {
ApplicationContext context = getContext();
return getOrApply(new ExpressionUrlAuthorizationConfigurer<>(context))
.getRegistry();
}
}
示例1:最基本的例子是配置所有 url 都需要有 ROLE_USER 角色。下面这个配置示例要求对每个URL进行身份验证,并将授予用户 admin 和 user 不同的访问权限。
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/**").hasRole("USER")
.and()
.formLogin();
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication()
.withUser("user").password("password").roles("USER")
.and()
.withUser("admin").password("password").roles("ADMIN", "USER");
}
示例2:也可以配置多个url。下面的配置要求对每个URL进行身份验证,并且只允许 admin 用户访问以 /admin/ 开头的URL。任何一个用户都可以访问所有其他url。
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/admin/**").hasRole("ADMIN")
.antMatchers("/**").hasRole("USER")
.and()
.formLogin();
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication()
.withUser("user").password("password").roles("USER")
.and()
.withUser("admin").password("password").roles("ADMIN", "USER");
}
示例3:注意匹配器是按顺序匹配的。因此以下是无效的,因为第一个匹配器匹配每个请求,并且永远不会到达第二个匹配器
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/**").hasRole("USER")
.antMatchers("/admin/**").hasRole("ADMIN")
.and()
.formLogin();
}
允许配置请求缓存
public final class HttpSecurity ...//忽略代码...{
public RequestCacheConfigurer<HttpSecurity> requestCache() throws Exception {
return getOrApply(new RequestCacheConfigurer<>());
}
}
允许配置异常处理
public final class HttpSecurity ...//忽略代码...{
public ExceptionHandlingConfigurer<HttpSecurity> exceptionHandling() throws Exception {
return getOrApply(new ExceptionHandlingConfigurer<>());
}
}
public final class HttpSecurity ...//忽略代码...{
public SecurityContextConfigurer<HttpSecurity> securityContext() throws Exception {
return getOrApply(new SecurityContextConfigurer<>());
}
}
public final class HttpSecurity ...//忽略代码...{
public ServletApiConfigurer<HttpSecurity> servletApi() throws Exception {
return getOrApply(new ServletApiConfigurer<>());
}
}
添加 CSRF 支持
public final class HttpSecurity ...//忽略代码...{
public CsrfConfigurer<HttpSecurity> csrf() throws Exception {
ApplicationContext context = getContext();
return getOrApply(new CsrfConfigurer<>(context));
}
}
示例:当使用 {@link WebSecurityConfigurerAdapter} 时,会被默认激活。你可以禁用它
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable();
}
说明:提供 注销 的支持。当使用 {@link WebSecurityConfigurerAdapter} 时,会被默认激活。默认情况下,访问URL /logout 将使HTTP会话失效,清除配置的所有 {@link HttpSecurity#rememberMe()} 身份验证,清除 {@link SecurityContextHolder} ,然后重定向到 /login?success,从而使用户退出。
public final class HttpSecurity ...//忽略代码...{
public LogoutConfigurer<HttpSecurity> logout() throws Exception {
return getOrApply(new LogoutConfigurer<>());
}
}
示例:下面的配置,当 /custom-logout 接口被调用时,会走注销流程。注销将删除名为 remove 的cookie,清除 SecurityContexHolder,但是不会使 HttpSession 失效,在完成上面动作之后重定向到 /logout-success。
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().antMatchers("/**").hasRole("USER")
.and().formLogin()
.and()
.logout().deleteCookies("remove").invalidateHttpSession(false)
.logoutUrl("/custom-logout")
.logoutSuccessUrl("/logout-success");
}
说明:允许配置 匿名用户 的表示方式。当使用 {@link WebSecurityConfigurerAdapter} 时,会被默认激活。默认情况下,匿名用户将用 {@link org.springframework.security.authentication.AnonymousAuthenticationToken} ,包含角色 ROLE_ANONYMOUS。
public final class HttpSecurity ...//忽略代码...{
public AnonymousConfigurer<HttpSecurity> anonymous() throws Exception {
return getOrApply(new AnonymousConfigurer<>());
}
}
示例1:下面的配置演示了如何指定匿名用户应该包含角色 ROLE_ANON。
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/**").hasRole("USER")
.and().formLogin()
.and()
.anonymous().authorities("ROLE_ANON");
}
示例2:下面演示了如何将匿名用户表示为空。注意,假设启用了匿名身份验证可能会导致代码中出现空指针异常。
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/**").hasRole("USER")
.and().formLogin()
.and()
.anonymous().disable();
}
说明:配置 通道安全(HTTPS访问)。为了使该配置有用,至少必须提供一个到所需通道的映射。
public final class HttpSecurity ...//忽略代码...{
public ChannelSecurityConfigurer<HttpSecurity>.ChannelRequestMatcherRegistry requiresChannel()
throws Exception {
ApplicationContext context = getContext();
return getOrApply(new ChannelSecurityConfigurer<>(context))
.getRegistry();
}
}
示例:下面的例子演示了如何为每个请求要求HTTPS。不建议只支持某些请求需要HTTPS,因为允许HTTP的应用程序会引入许多安全漏洞。
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/**").hasRole("USER")
.and().formLogin()
.and().requiresChannel().anyRequest().requiresSecure();
}
说明:配置 HTTP基本认证 。
public final class HttpSecurity ...//忽略代码...{
public HttpBasicConfigurer<HttpSecurity> httpBasic() throws Exception {
return getOrApply(new HttpBasicConfigurer<>());
}
}
示例:下面的示例演示如何为应用程序配置HTTP基本身份验证。默认的领域是 Spring Security Application ,但是可以使用 {@link HttpBasicConfigurer#realmName()} 自定义。
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/**").hasRole("USER")
.and()
.httpBasic();
}
说明:
// TODO … 。如果只需要一个 {@link RequestMatcher},可以考虑使用 {@link #mvcMatcher()} 、{@link #antMatcher()}、 {@link #regexMatcher()}、或{@link #requestMatcher()}。
调用 {@link #requestMatchers()} 不会覆盖之前对 {@link #mvcMatcher()}、{@link #requestMatchers()}、{@link #antMatcher()}、{@link #regexMatcher()} 和 {@link #requestMatcher()} 的调用。
public final class HttpSecurity ...//忽略代码...{
public RequestMatcherConfigurer requestMatchers() {
return requestMatcherConfigurer;
}
}
示例1:下面配置了以 /api/ 和 /oauth/ 开头的URL,无需认证。
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.requestMatchers()
.antMatchers("/api/**", "/oauth/**")
.and()
.authorizeRequests()
.antMatchers("/**").hasRole("USER")
.and()
.httpBasic();
}
示例2:下面的配置与前面的配置相同
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.requestMatchers()
.antMatchers("/api/**")
.antMatchers("/oauth/**")
.and()
.authorizeRequests()
.antMatchers("/**").hasRole("USER")
.and()
.httpBasic();
}
示例3:下面的配置也与上面的配置相同
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.requestMatchers()
.antMatchers("/api/**")
.and()
.requestMatchers()
.antMatchers("/oauth/**")
.and()
.authorizeRequests()
.antMatchers("/**").hasRole("USER")
.and()
.httpBasic();
}
使用这三个方法添加的过滤器其必须是FilterComparator过滤器排序池的中对象,否则会报错.
public final class HttpSecurity ...//忽略代码...{
public HttpSecurity addFilter(Filter filter) { Class<? extends Filter> filterClass = filter.getClass();
if (!comparator.isRegistered(filterClass)) {
throw new IllegalArgumentException("");
}
this.filters.add(filter);
return this;
}
}
afterFilter必须在FilterComparator过滤器排序池中。
使用addFilterAfter方法后,filter的排序号会比afterFilter的排序号大1,序号大的排在后面
public final class HttpSecurity ...//忽略代码...{
public HttpSecurity addFilterAfter(Filter filter, Class<? extends Filter> afterFilter) {
comparator.registerAfter(filter.getClass(), afterFilter);
return addFilter(filter);
}
}
beforeFilter必须在FilterComparator过滤器排序池中。
使用addFilterBefore方法后,filter的排序号会比afterFilter的排序号小1,序号小的排在前面
public final class HttpSecurity ...//忽略代码...{
public HttpSecurity addFilterBefore(Filter filter, Class<? extends Filter> beforeFilter) {
comparator.registerBefore(filter.getClass(), beforeFilter);
return addFilter(filter);
}
}
atFilter必须在FilterComparator过滤器排序池中。
使用addFilterAt方法后,filter的排序号会afterFilter的排序号一样。
如果HttpSecurity过滤器池中也存在atFilter,那么filter会排在atFilter前面
public final class HttpSecurity ...//忽略代码...{
public HttpSecurity addFilterAt(Filter filter, Class<? extends Filter> atFilter) {
this.comparator.registerAt(filter.getClass(), atFilter);
return addFilter(filter);
}
}
配置数据源接口
public final class HttpSecurity ...//忽略代码...{
public HttpSecurity userDetailsService(UserDetailsService userDetailsService)throws Exception {
getAuthenticationRegistry().userDetailsService(userDetailsService);
return this;
}
}
配置验证器
public final class HttpSecurity ...//忽略代码...{
public HttpSecurity authenticationProvider(AuthenticationProvider authenticationProvider) {
getAuthenticationRegistry().authenticationProvider(authenticationProvider);
return this;
}
}
配置为只在匹配所提供的 ant 模式时被调用
public final class HttpSecurity ...//忽略代码...{
public HttpSecurity antMatcher(String antPattern) {
return requestMatcher(new AntPathRequestMatcher(antPattern));
}
}
配置为只在匹配所提供的 Spring MVC 模式时被调用
public final class HttpSecurity ...//忽略代码...{
public HttpSecurity mvcMatcher(String mvcPattern) {
HandlerMappingIntrospector introspector = new HandlerMappingIntrospector(getContext());
return requestMatcher(new MvcRequestMatcher(introspector, mvcPattern));
}
}
配置为只在匹配所提供的 正则表达式 模式时被调用
public final class HttpSecurity ...//忽略代码...{
public HttpSecurity regexMatcher(String pattern) {
return requestMatcher(new RegexRequestMatcher(pattern, null));
}
}
HttpSecurity的初始化发生在FilterChainProxy创建的过程中,初始代码如下
@Configuration(proxyBeanMethods = false)
public class WebSecurityConfiguration implements ImportAware, BeanClassLoaderAware {
@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);
}
//HttpSecurity的初始化发生在这里
return webSecurity.build();
}
}
HttpSecurity的初始化有2个过程:装配配置器和过滤器
真正的初始化发生在WebSecurityConfigurerAdapter的init方法中
public abstract class WebSecurityConfigurerAdapter implements WebSecurityConfigurer<WebSecurity> {
public void init(final WebSecurity web) throws Exception {
final HttpSecurity http = getHttp();
web.addSecurityFilterChainBuilder(http).postBuildAction(() -> {
FilterSecurityInterceptor securityInterceptor = http.getSharedObject(FilterSecurityInterceptor.class);
web.securityInterceptor(securityInterceptor);
});
}
protected final HttpSecurity getHttp() throws Exception {
if (http != null) {return http;}
DefaultAuthenticationEventPublisher eventPublisher = objectPostProcessor
.postProcess(new DefaultAuthenticationEventPublisher());
localConfigureAuthenticationBldr.authenticationEventPublisher(eventPublisher);
AuthenticationManager authenticationManager = authenticationManager();
authenticationBuilder.parentAuthenticationManager(authenticationManager);
authenticationBuilder.authenticationEventPublisher(eventPublisher);
Map<Class<?>, Object> sharedObjects = createSharedObjects();
http = new HttpSecurity(objectPostProcessor, authenticationBuilder,sharedObjects);
if (!disableDefaults) {
http
.csrf().and() //配置池中添加CsrfConfigurer
.addFilter(new WebAsyncManagerIntegrationFilter())//过滤器池中添加WebAsyncManagerIntegrationFilter
.exceptionHandling().and()//配置池中添加ExceptionHandlingConfigurer
.headers().and()//配置池中添加HeadersConfigurer
.sessionManagement().and()//配置池中添加SessionManagementConfigurer
.securityContext().and()//配置池中添加SecurityContextConfigurer
.requestCache().and()//配置池中添加RequestCacheConfigurer
.anonymous().and()//配置池中添加AnonymousConfigurer
.servletApi().and()//配置池中添加ServletApiConfigurer
.apply(new DefaultLoginPageConfigurer<>()).and()//配置池中添加DefaultLoginPageConfigurer
.logout();//配置池中添加LogoutConfigurer
// @formatter:on
ClassLoader classLoader = this.context.getClassLoader();
List<AbstractHttpConfigurer> defaultHttpConfigurers =
SpringFactoriesLoader.loadFactories(AbstractHttpConfigurer.class, classLoader);
for (AbstractHttpConfigurer configurer : defaultHttpConfigurers) {
http.apply(configurer);
}
}
configure(http);
return http;
}
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()//配置池中添加ExpressionUrlAuthorizationConfigurer
.anyRequest().authenticated().and()
.formLogin().and()//配置池中添加FormLoginConfigurer
.httpBasic();//配置池中添加HttpBasicConfigurer
}
}
过滤器的装配发在其父类AbstractConfiguredSecurityBuilder的configure方法中,循环调用其注册的配置器,调用它们的configure方法,来注册过滤器
public abstract class AbstractConfiguredSecurityBuilder<O, B extends SecurityBuilder<O>>
extends AbstractSecurityBuilder<O> {
private final LinkedHashMap<Class<? extends SecurityConfigurer<O, B>>, List<SecurityConfigurer<O, B>>> configurers = new LinkedHashMap<>();
private void configure() throws Exception {
Collection<SecurityConfigurer<O, B>> configurers = getConfigurers();
for (SecurityConfigurer<O, B> configurer : configurers) {
configurer.configure((B) this);
}
}
}
public final class HttpSecurity //忽略代码。。。。。 {
private final RequestMatcherConfigurer requestMatcherConfigurer;
private List<Filter> filters = new ArrayList<>();
private RequestMatcher requestMatcher = AnyRequestMatcher.INSTANCE;
private FilterComparator comparator = new FilterComparator();
@Override
protected DefaultSecurityFilterChain performBuild() {
filters.sort(comparator);
return new DefaultSecurityFilterChain(requestMatcher, filters);
}
}