〇、上篇回顾
- 整个框架的核心就是构建一个名字为
springSecurityFilterChain
的过滤器,它的类型是FilterChainProxy
。 WebSecurity
和HttpSecurity
都是建造者
WebSecurity
的构建目标是FilterChainProxy
对象,即核心过滤器springSecurityFilterChain
HttpSecurity
的构建目标只是FilterChainProxy
对象中一组SecurityFilterChain
的一个
一、建造者接口架构
建造者整体UML图
在介绍
WebSecurity
和HttpSecurity
之前我们先来看看它们上层的类和接口。AuthenticationManagerBuilder
先放放,后面讲解如何认证授权时详细介绍。
上层UML类图
1. SecurityBuilder
- 说明:可以看上面UML类图,是建造者顶级接口,含有一个
build()
接口方法 - 源码:
public interface SecurityBuilder<O> {
O build() throws Exception;
}
2. AbstractSecurityBuilder
- 说明:SecurityBuilder的抽象子类,确保建造者只被构建一次,对父接口方法
build()
进行了原子判断,保证每次只构建一次,定义了一个抽象方法doBuild()
供子类扩展 - 源码:
public abstract class AbstractSecurityBuilder<O> implements SecurityBuilder<O> {
private AtomicBoolean building = new AtomicBoolean();
private O object;
public final O build() throws Exception {
if (this.building.compareAndSet(false, true)) {
this.object = doBuild();
return this.object;
}
throw new AlreadyBuiltException("This object has already been built");
}
public final O getObject() {
if (!this.building.get()) {
throw new IllegalStateException("This object has not been built");
}
return this.object;
}
protected abstract O doBuild() throws Exception;
}
3. AbstractConfiguredSecurityBuilder
-
说明:主要关注
add()
、doBuild()
、init()
、configure()
、performBuild()
add()
:允许配置器应用到建造者中,实际上就是把各种配置添加到该类定义的配置器列表 configurers、configurersAddedInInitializingdoBuild()
:实现的父类方法,真正执行构建的方法,方法中调用了 init()、configure()init()
:循环初始化所有的 configurers、configurersAddedInInitializing 配置configure()
:循环配置所有的 configurersperformBuild()
:配置完所有之后,进行实际的执行,该方法是抽象方法,留给下层子类(WebSecurity、HttpSecurity)
实现
-
源码:
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<Class<? extends SecurityConfigurer<O, B>>, List<SecurityConfigurer<O, B>>>();
private final List<SecurityConfigurer<O, B>> configurersAddedInInitializing = new ArrayList<SecurityConfigurer<O, B>>();
@SuppressWarnings("unchecked")
private <C extends SecurityConfigurer<O, B>> void add(C configurer) throws Exception {
Assert.notNull(configurer, "configurer cannot be null");
Class<? extends SecurityConfigurer<O, B>> clazz = (Class<? extends SecurityConfigurer<O, B>>) configurer
.getClass();
synchronized (configurers) {
if (buildState.isConfigured()) {
throw new IllegalStateException("Cannot apply " + configurer
+ " to already built object");
}
List<SecurityConfigurer<O, B>> configs = allowConfigurersOfSameType ? this.configurers
.get(clazz) : null;
if (configs == null) {
configs = new ArrayList<SecurityConfigurer<O, B>>(1);
}
configs.add(configurer);
this.configurers.put(clazz, configs);
if (buildState.isInitializing()) {
this.configurersAddedInInitializing.add(configurer);
}
}
}
@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;
}
}
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);
}
}
}
4. HttpSecurityBuilder
- 说明:该接口被
HttpSecurity
所实现,主要声明了一些操作Filter
的接口方法。 - 源码:
public interface HttpSecurityBuilder<H extends HttpSecurityBuilder<H>> extends
SecurityBuilder<DefaultSecurityFilterChain> {
<C extends SecurityConfigurer<DefaultSecurityFilterChain, H>> C getConfigurer(
Class<C> clazz);
<C extends SecurityConfigurer<DefaultSecurityFilterChain, H>> C removeConfigurer(
Class<C> clazz);
<C> void setSharedObject(Class<C> sharedType, C object);
<C> C getSharedObject(Class<C> sharedType);
H authenticationProvider(AuthenticationProvider authenticationProvider);
H userDetailsService(UserDetailsService userDetailsService) throws Exception;
H addFilterAfter(Filter filter, Class<? extends Filter> afterFilter);
H addFilterBefore(Filter filter, Class<? extends Filter> beforeFilter);
H addFilter(Filter filter);
}
二、FilterChainProxy
在介绍主要建造者之前先看看核心过滤器
springSecurityFilterChain
的类型FilterChainProxy
类介绍
- 继承了
GenericFilterBean
类,这个类实现了Filter
接口,所以FilterChainProxy
也是个过滤器。 - 它其实是过滤器链的一个代理,真正起作用的是它内部维护的一组
SecurityFilterChain
过滤器链,SecurityFilterChain
是一个接口,有个默认实现类DefaultSecurityFilterChain
,实现类里面主要是一组URL
和一组过滤器
。
UML类图
说明
- 类图中很清晰表示,
FilterChainProxy
一对多组合了SecurityFilterChain
,SecurityFilterChain
里面有一组Filter
和一组URL
。
三、WebSecurity
类介绍
WebSecurity
的目标是构建FilterChainProxy
对象,即构建核心过滤器springSecurityFilterChain
。
方法属性
说明
- 主要关注由
AbstractConfiguredSecurityBuilder
继承下来的方法实现performBuild()
- 方法中首先创建了一个
securityFilterChains
- 然后第一个for循环将那些忽略拦截的URL封装成一堆
DefaultSecurityFilterChain
添加进securityFilterChains
- 第二个for循环调用的是
build()
方法,其实它最终调用的是HttpSecurity
实现的performBuild()
方法,返回值也是DefaultSecurityFilterChain
,随后添加进securityFilterChains
- 最后根据
securityFilterChains
创建出FilterChainProxy
对象。
public final class WebSecurity extends
AbstractConfiguredSecurityBuilder<Filter, WebSecurity> implements
SecurityBuilder<Filter>, ApplicationContextAware {
private final List<RequestMatcher> ignoredRequests = new ArrayList<RequestMatcher>();
private final List<SecurityBuilder<? extends SecurityFilterChain>> securityFilterChainBuilders = new ArrayList<SecurityBuilder<? extends SecurityFilterChain>>();
...
@Override
protected Filter performBuild() throws Exception {
Assert.state(
!securityFilterChainBuilders.isEmpty(),
"At least one SecurityBuilder<? extends SecurityFilterChain> needs to be specified. Typically this done by adding a @Configuration that extends WebSecurityConfigurerAdapter. More advanced users can invoke "
+ WebSecurity.class.getSimpleName()
+ ".addSecurityFilterChainBuilder directly");
int chainSize = ignoredRequests.size() + securityFilterChainBuilders.size();
List<SecurityFilterChain> securityFilterChains = new ArrayList<SecurityFilterChain>(
chainSize);
for (RequestMatcher ignoredRequest : ignoredRequests) {
securityFilterChains.add(new DefaultSecurityFilterChain(ignoredRequest));
}
for (SecurityBuilder<? extends SecurityFilterChain> securityFilterChainBuilder : securityFilterChainBuilders) {
securityFilterChains.add(securityFilterChainBuilder.build());
}
FilterChainProxy filterChainProxy = new FilterChainProxy(securityFilterChains);
if (httpFirewall != null) {
filterChainProxy.setFirewall(httpFirewall);
}
filterChainProxy.afterPropertiesSet();
Filter result = filterChainProxy;
if (debugEnabled) {
logger.warn("\n\n"
+ "********************************************************************\n"
+ "********** Security debugging is enabled. *************\n"
+ "********** This may include sensitive information. *************\n"
+ "********** Do not use in a production system! *************\n"
+ "********************************************************************\n\n");
result = new DebugFilter(filterChainProxy);
}
postBuildAction.run();
return result;
}
...
}
四、HttpSecurity
要会用此框架,必须知道
HttpSecurity
!!!
- 它的目标是构建一个
SecurityFilterChain
过滤器链实例,它掌握着所有Filter的“生杀大权”
,想要谁过滤就配置起来,不想要谁过滤就不配置或者禁用掉(因为有些会被默认配置)。 - 大致可以将其内方法分为两大类型,一类是
框架默认给开发者提供的认证配置方法,可供开发者选择性调用
,比如:
formLogin()、openidLogin()、headers()、cors()、sessionManagement()、portMapper()、jee()、x509()、rememberMe()、authorizeRequests()、requestCache()、exceptionHandling()、securityContext()、servletApi()、csrf()、logout()、anonymous()、requiresChannel()、httpBasic()、requestMatchers()、addFilterAt()、requestMatcher()、antMatcher()、mvcMatcher()、regexMatcher()、getOrApply()
- 还有一类
可供开发者配置自己的过滤器
,比如:
setSharedObject()、beforeConfigure()、authenticationProvider()、userDetailsService()、getAuthenticationRegistry()、addFilterAfter()、addFilterBefore()、addFilter()
- 还有一个方法
performBuild()
,和WebSecurity
一样继承自AbstractConfiguredSecurityBuilder
,目的是创建SecurityFilterChain
实例。
由于该类功能强大、复杂又必须了解,故新开一章专门讲述
HttpSecurity
里面各个方法。
五、系列文章
Spring Security 系列
- 《手把手教你如何使用Spring Security(上):登录授权》
- 《手把手教你如何使用Spring Security(中):接口认证》
- 《手把手教你如何使用Spring Security(下):访问控制》
- 《Spring Security源码(一):整体框架设计》
- 《Spring Security源码(二):建造者详解》
- 《Spring Security源码(三):HttpSecurity详解》
- 《Spring Security源码(四):配置器详解》
- 《Spring Security源码(五):FilterChainProxy是如何创建的?》
- 《Spring Security源码(六):FilterChainProxy是如何运行的?》
- 《Spring Security源码(七):设计模式在框架中的应用》
- 《Spring Security源码(八):登录认证源码流程》
- 《Spring Security源码(九):过滤器链上的过滤器是如何排序的?》
- 《Spring Security源码(十):权限访问控制是如何做到的?》
Spring Security OAuth 系列
Java 面试宝典是大明哥全力打造的 Java 精品面试题,它是一份靠谱、强大、详细、经典的 Java 后端面试宝典。它不仅仅只是一道道面试题,而是一套完整的 Java 知识体系,一套你 Java 知识点的扫盲贴。
它的内容包括:
- 大厂真题:Java 面试宝典里面的题目都是最近几年的高频的大厂面试真题。
- 原创内容:Java 面试宝典内容全部都是大明哥原创,内容全面且通俗易懂,回答部分可以直接作为面试回答内容。
- 持续更新:一次购买,永久有效。大明哥会持续更新 3+ 年,累计更新 1000+,宝典会不断迭代更新,保证最新、最全面。
- 覆盖全面:本宝典累计更新 1000+,从 Java 入门到 Java 架构的高频面试题,实现 360° 全覆盖。
- 不止面试:内容包含面试题解析、内容详解、知识扩展,它不仅仅只是一份面试题,更是一套完整的 Java 知识体系。
- 宝典详情:https://www.yuque.com/chenssy/sike-java/xvlo920axlp7sf4k
- 宝典总览:https://www.yuque.com/chenssy/sike-java/yogsehzntzgp4ly1
- 宝典进展:https://www.yuque.com/chenssy/sike-java/en9ned7loo47z5aw
目前 Java 面试宝典累计更新 400+ 道,总字数 42w+。大明哥还在持续更新中,下图是大明哥在 2024-12 月份的更新情况:
想了解详情的小伙伴,扫描下面二维码加大明哥微信【daming091】咨询
同时,大明哥也整理一套目前市面最常见的热点面试题。微信搜[大明哥聊 Java]或扫描下方二维码关注大明哥的原创公众号[大明哥聊 Java] ,回复【面试题】 即可免费领取。