官网地址 Spring Security Reference
版本:Version 5.5.0
HttpSecurity 的继承关系图
在前面了解过 WebSecurity、HttpSecurity、AuthenticationManagerBuilder 这三个重要构建者公共的部分:
|- SecurityBuilder
|- AbstractSecurityBuilder
|- AbstractConfiguredSecurityBuilder
公共的这部分对构建者做扩展,点击这里可以回顾一下,这里主要看看 HttpSecurity 类。
HttpSecurityBuilder
这是一个接口,定义了实现类的一些功能。这个接口继承了 SecurityBuilder ,也就是说这是一个构建器接口:
实现这个接口的类都将构建一个 DefaultSecurityFilterChain 类的对象。
接口方法说明
-
getConfigurer 获取一个配置对象。
按类名获取 SecurityConfigurer,如果未找到则为 null。 请注意,不考虑对象层次结构。在之前的文章中也提到过 SecurityConfigurer 配置类,它有 init 方法 和 configure 方法,用来对构造器进行配置,实现定制对象的能力。
-
removeConfigurer 移除一个配置对象。
按类名删除 SecurityConfigurer,如果未找到则为 null。 请注意,不考虑对象层次结构。 -
setSharedObject 和 getSharedObject
配置 和 获取由多个 SecurityConfigurer 共享的对象。 -
authenticationProvider 方法表示配置身份验证器
允许添加额外的 AuthenticationProvider 以供使用。 -
userDetailsService 配置userDetailsService (用户数据获取接口)。
允许添加要使用的额外 UserDetailsService -
addFilterBefore 和 addFilterAfter
在某一个已知过滤器之前或者之后添加过滤器。已知的过滤器实例要么是在 addFilter(Filter) 中列出的过滤器,要么是已经使用 addFilterAfter(Filter, Class) 或 addFilterBefore(Filter, Class) 添加的过滤器。 -
addFilter 添加一个过滤器。
添加一个过滤器,该过滤器必须是安全框架中提供的过滤器之一的实例或扩展其中的一个过滤器。
官方注解中给出了安全框架中提供的过滤器及它们的顺序:
- ChannelProcessingFilter
- SecurityContextPersistenceFilter
- LogoutFilter
- X509AuthenticationFilter
- AbstractPreAuthenticatedProcessingFilter
- CasAuthenticationFilter
- UsernamePasswordAuthenticationFilter
- OpenIDAuthenticationFilter
- DefaultLoginPageGeneratingFilter
- DefaultLogoutPageGeneratingFilter
- ConcurrentSessionFilter
- DigestAuthenticationFilter
- BearerTokenAuthenticationFilter
- BasicAuthenticationFilter
- RequestCacheAwareFilter
- SecurityContextHolderAwareRequestFilter
- JaasApiIntegrationFilter
- RememberMeAuthenticationFilter
- AnonymousAuthenticationFilter
- SessionManagementFilter
- ExceptionTranslationFilter
- FilterSecurityInterceptor
- SwitchUserFilter
HttpSecurityBuilder 接口只有一个实现类:HttpSecurity。上面描述的所用功能都将在 HttpSecurity 实现类中实现。
AbstractSecurityBuilder 和 AbstractConfiguredSecurityBuilder
这两个抽象类在之前的文章中有详细说明,这里回顾一下:
DefaultSecurityFilterChain 过滤器链
多次提及 DefaultSecurityFilterChain 安全过滤器链,这里提前看一下
SecurityFilterChain
源码注解:
- 定义能够与 HttpServletRequest 匹配的过滤器链。 以决定它是否适用于该请求。
- 用于配置过滤器链(FilterChainProxy)。
该接口定义了两个抽象方法,
- matches 方法用来匹配请求
- getFilters 方法返回一个
List<Filter>
集合
当接收到一个请求时,matches 方法判断请求是否和当前过滤器链匹配,如果匹配(true),就返回 getFilters 方法中的过滤器,这个请求会逐个经过集合中的过滤器。
该接口只有一个实现类:DefaultSecurityFilterChain
DefaultSecurityFilterChain
public final class DefaultSecurityFilterChain implements SecurityFilterChain {
private static final Log logger = LogFactory.getLog(DefaultSecurityFilterChain.class);
private final RequestMatcher requestMatcher;
private final List<Filter> filters;
public DefaultSecurityFilterChain(RequestMatcher requestMatcher, Filter... filters) {
this(requestMatcher, Arrays.asList(filters));
}
public DefaultSecurityFilterChain(RequestMatcher requestMatcher, List<Filter> filters) {
logger.info("Creating filter chain: " + requestMatcher + ", " + filters);
this.requestMatcher = requestMatcher;
this.filters = new ArrayList<>(filters);
}
public RequestMatcher getRequestMatcher() {
return requestMatcher;
}
public List<Filter> getFilters() {
return filters;
}
public boolean matches(HttpServletRequest request) {
return requestMatcher.matches(request);
}
@Override
public String toString() {
return "[ " + requestMatcher + ", " + filters + "]";
}
}
源码注解说:这个类时 SecurityFilterChain 接口的标准实现。
这里借助于 RequestMatcher 对请求做匹配,RequestMatcher是一个接口,是一个匹配 HttpServletRequest 请求的简单策略,它有很多实现类(有些类利用定义内部类实现该接口来提供请求匹配能力):
![](https://i-blog.csdnimg.cn/blog_migrate/a3db0e32258d15ba1a2bde892f83f156.png)
HttpSecurity
通过idea工具查看这个类中的详细内容,发现里面有很多方法,还有两个内部类。点开源码发现里面有很多注释,第一反应就是这个类好复杂
但是通过之前对这个类的父类以接接口的了解,这个类的大概功能是明确的:
构建过滤器链,并且可以将多个配置类应用到这个配置类
所以根据这个类的核心目的,按照之前看 WebSecurity、AuthenticationManagerBuilder 的步骤看这个类的源码:
先看一下类中的内容
源码官网类注释
文档地址
说明了这个类的作用:(类似于命名空间配置中 Spring Security 的 XML <http> 元素)
它允许为特定的 http 请求配置基于 Web 的安全性。 默认情况下,它将应用于所有请求,但可以使用 requestMatcher(RequestMatcher) 或其他类似方法进行限制。
源码注释中的例子含义:
-
定义了一个内存身份验证方案
在内存中存入一个用户:user,它的密码为:password,它的角色为:USER
-
配置将要求请求的任何 URL (所用请求都会被匹配上)都需要具有“ROLE_USER”角色的用户。
“ROLE_” 是一个前缀,在源码中是默认前缀,也可以自己配置。
内部类
成员变量
-
requestMatcherConfigurer 内部类 RequestMatcherConfigurer 的对象,用来配置请求匹配器的。
-
filters 存放过滤器的 List 集合
-
requestMatcher 请求匹配器,用来匹配接收到的请求
RequestMatcher requestMatcher = AnyRequestMatcher.INSTANCE;
默认是一个所有请求都会被匹配到的匹配器。 -
comparator 过滤器比较器,用来比较过滤器顺序
FilterComparator comparator = new FilterComparator();
比如在给过滤器排序是会使用这个比较器来比较过滤器的顺序
构造方法
需要三个参数:
-
objectPostProcessor 后置处理对象
-
authenticationBuilder 认证管理器构建器
用于创建AuthenticationManager。 允许轻松构建内存身份验证,LDAP身份验证,基于JDBC的身份验证,添加UserDetailsService以及添加AuthenticationProvider。【可以之前的文章】 -
sharedObjects 存放共享数据的对象
这是一个
Map<Class<? extends Object>, Object>
集合。在 WebSecurityConfigurerAdapter 适配器中,创建 HttpSecurity 的 getHttp() 方法中创建了共享对象,并在创建实例时传入这个构造函数,下面是 WebSecurityConfigurerAdapter 中部分代码的截图 :
构造方法中的内容:
public HttpSecurity(ObjectPostProcessor<Object> objectPostProcessor,
AuthenticationManagerBuilder authenticationBuilder,
Map<Class<? extends Object>, Object> sharedObjects) {
// 调用父类构造器,设置后置处理对象,并且使用默认配置:不允许添加同类型配置器
super(objectPostProcessor);
// 断言:authenticationBuilder 不能为空
Assert.notNull(authenticationBuilder, "authenticationBuilder cannot be null");
// 把 AuthenticationManagerBuilder 存入自己的共享变量集合中
setSharedObject(AuthenticationManagerBuilder.class, authenticationBuilder);
// 遍历调用方传入的共享变量,并把他们都存到自己的共享变量集合中
for (Map.Entry<Class<? extends Object>, Object> entry : sharedObjects
.entrySet()) {
setSharedObject((Class<Object>) entry.getKey(), entry.getValue());
}
// 从共享变量中获取 ApplicationContext
ApplicationContext context = (ApplicationContext) sharedObjects
.get(ApplicationContext.class);
// 创建请求匹配配置器,需要传入 ApplicationContext 对象
this.requestMatcherConfigurer = new RequestMatcherConfigurer(context);
}
共享变量中的 ApplicationContext 对象是在 WebSecurityConfigurerAdapter 中通过依赖注入获取的。
构造方法的作用就是创建一个新的实例。
performBuild 方法
这是构建这个构建器实际构建对象的方法
@Override
protected DefaultSecurityFilterChain performBuild() throws Exception {
Collections.sort(filters, comparator);
return new DefaultSecurityFilterChain(requestMatcher, filters);
}
方法逻辑简单,就是利用比较器 comparator
将 filters 集合中的过滤器进行排序,排完之后利用这个拍好序的 filters 集合和请求匹配器 requestMatcher 创建 DefaultSecurityFilterChain 实例。
实现 HttpSecurityBuilder 接口中的抽象方法
这些方法的实现可以分成以下几类来看:
-
由父类实现了的方法
getConfigurer 方法
removeConfigurer 方法
setSharedObject 方法
getSharedObject 方法
以上四个方法实际是在父类 AbstractConfiguredSecurityBuilder 中得到实现的。setSharedObject 方法虽然在 HttpSecurity 中有覆盖,但是方法体本质还是直接调用父类的方法。
-
添加过滤器的方法
public HttpSecurity addFilterAfter(Filter filter, Class<? extends Filter> afterFilter) { comparator.registerAfter(filter.getClass(), afterFilter); return addFilter(filter); } public HttpSecurity addFilterBefore(Filter filter, Class<? extends Filter> beforeFilter) { comparator.registerBefore(filter.getClass(), beforeFilter); return addFilter(filter); } public HttpSecurity addFilter(Filter filter) { Class<? extends Filter> filterClass = filter.getClass(); if (!comparator.isRegistered(filterClass)) { throw new IllegalArgumentException( "The Filter class " + filterClass.getName() + " does not have a registered order and cannot be added without a specified order. Consider using addFilterBefore or addFilterAfter instead."); } this.filters.add(filter); return this; } public HttpSecurity addFilterAt(Filter filter, Class<? extends Filter> atFilter) { this.comparator.registerAt(filter.getClass(), atFilter); return addFilter(filter); }
这些方法都是用来添加过滤器的,前三个是接口中规定的,最后一个是 HttpSecurity 中自己加的一个方法。添加功能都是通过比较器实现对 List 集合操作类实现的。
-
修改认证管理器构建器(AuthenticationManagerBuilder)的方法
authenticationProvider 方法和userDetailsService 方法和 AuthenticationManagerBuilder 中提到的方法是一样的,但是这里千万不能理解成覆盖啥的,它们没有关系。这里是通过共享变量实现对 AuthenticationManagerBuilder 构建器的修改的。public HttpSecurity authenticationProvider( AuthenticationProvider authenticationProvider) { getAuthenticationRegistry().authenticationProvider(authenticationProvider); return this; } public HttpSecurity userDetailsService(UserDetailsService userDetailsService) throws Exception { getAuthenticationRegistry().userDetailsService(userDetailsService); return this; } // 从共享变量中获取 AuthenticationManagerBuilder 实例(在构造方法中设置过这个值,并且断言它不为空) private AuthenticationManagerBuilder getAuthenticationRegistry() { return getSharedObject(AuthenticationManagerBuilder.class); }
获取或者应用配置器的方法
剩下很对方法都都是在添加配置器,它们都有类似的逻辑
- 都调用 getOrApply 方法将特定的配置器应用到构造器(即:HttpSecurity)
- 都立刻返回应用的配置器,便于调用方对配置器进行自定义的配置
// 添加 Basic 配置
public HttpBasicConfigurer<HttpSecurity> httpBasic() throws Exception {
return getOrApply(new HttpBasicConfigurer<>());
}
// 添加通道安全配置器
public ChannelSecurityConfigurer<HttpSecurity>.ChannelRequestMatcherRegistry requiresChannel()
throws Exception {
ApplicationContext context = getContext();
return getOrApply(new ChannelSecurityConfigurer<>(context))
.getRegistry();
}
这里列出两个,其他的都类似。这些方法都调用了一个相同的方法:
getOrApply 方法
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);
}
逻辑也很简单,先判断是否已经存在同类型的配置器,如果没有就调用父类的 apply 方法将这个配置器应用到 HttpSecurity 构造器。
这类方法的使用在源码注释中都有例子,详细的使用可以参考源码注释或者看官网的注释。
总结
本质:构建过滤器链 DefaultSecurityFilterChain 。
有各种简单方法添加 xxxConfigurer 配置器,并返回这个配置器方便进行配置
可以操作 AuthenticationManagerBuilder
过滤器链中有很多默认的过滤器,且有顺序
可以设置请求匹配器,包括正则匹配