Spring Security在启动阶段会注册一些bean,并构建过滤器链,Spring Security的“安全”功能都是封装在该过滤器链中的。一般我们会根据业务需求向bean容器中注册一个过滤器链bean,该bean对象在启动阶段会被扫描到,并设置到相应的组件中。
SecurityFilterAutoConfiguration
该类是Spring Boot中提供的一个自动配置类,在该类中会注册一个特殊的bean,该bean会向servlet上下文中注册Spring Security中使用的过滤器。
@AutoConfiguration(after = SecurityAutoConfiguration.class)
@ConditionalOnWebApplication(type = Type.SERVLET)
@EnableConfigurationProperties(SecurityProperties.class)
@ConditionalOnClass({ AbstractSecurityWebApplicationInitializer.class, SessionCreationPolicy.class })
public class SecurityFilterAutoConfiguration {
private static final String DEFAULT_FILTER_NAME = AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME;
@Bean
/*
* 存在名为springSecurityFilterChain的bean时才注册该bean,
* WebSecurityConfiguration中会注册该bean
*/
@ConditionalOnBean(name = DEFAULT_FILTER_NAME)
public DelegatingFilterProxyRegistrationBean securityFilterChainRegistration(
SecurityProperties securityProperties) {
// 创建注册对象,该对象是一个servlet上下文初始化器,会创建并注册DelegatingFilterProxy这个特殊的过滤器
DelegatingFilterProxyRegistrationBean registration = new DelegatingFilterProxyRegistrationBean(
DEFAULT_FILTER_NAME);
registration.setOrder(securityProperties.getFilter().getOrder());
registration.setDispatcherTypes(getDispatcherTypes(securityProperties));
return registration;
}
private EnumSet<DispatcherType> getDispatcherTypes(SecurityProperties securityProperties) {
if (securityProperties.getFilter().getDispatcherTypes() == null) {
return null;
}
return securityProperties.getFilter().getDispatcherTypes().stream()
.map((type) -> DispatcherType.valueOf(type.name()))
.collect(Collectors.toCollection(() -> EnumSet.noneOf(DispatcherType.class)));
}
}
DelegatingFilterProxyRegistrationBean是一个RegistrationBean,在介绍Spring Boot中DispatcherServlet注册时,就讲到Spring Boot也是通过RegistrationBean来注册的servlet。而RegistrationBean是一个servlet上下文初始化器,当servlet容器准备好servlet上下文时,会调用servlet上下文初始化器的onStartup方法。
// Servlet上下文初始化后会调用该方法
@Override
public final void onStartup(ServletContext servletContext) throws ServletException {
String description = getDescription();
if (!isEnabled()) {
logger.info(StringUtils.capitalize(description) + " was not registered (disabled)");
return;
}
// 执行注册servlet的操作
register(description, servletContext);
}
protected abstract void register(String description, ServletContext servletContext);
@Override
protected final void register(String description, ServletContext servletContext) {
// 执行注册servlet的操作
D registration = addRegistration(description, servletContext);
if (registration == null) {
logger.info(StringUtils.capitalize(description) + " was not registered (possibly already registered?)");
return;
}
configure(registration);
}
protected abstract D addRegistration(String description, ServletContext servletContext);
@Override
protected Dynamic addRegistration(String description, ServletContext servletContext) {
// 获取过滤器,模板方法,由子类实现
Filter filter = getFilter();
// 注册过滤器到servlet上下文中
return servletContext.addFilter(getOrDeduceName(filter), filter);
}
public abstract T getFilter();
@Override
public DelegatingFilterProxy getFilter() {
/*
* 创建DelegatingFilterProxy对象,这个过滤器会被注册到servlet上下文中,是Spring Security的入口点
* 这里的targetBeanName的值是springSecurityFilterChain,参考SecurityFilterAutoConfiguration类。
*/
return new DelegatingFilterProxy(this.targetBeanName, getWebApplicationContext()) {
@Override
protected void initFilterBean() throws ServletException {
// Don't initialize filter bean on init()
}
};
}
上面的调用链比较长,最终在DelegatingFilterProxyRegistrationBean的getFilter方法中,创建了过滤器。DelegatingFilterProxy是一个过滤器代理,是一个特殊的过滤器,是Spring Framework中提供的类。通过构造器设置了目标过滤器bean的名称,在SecurityFilterAutoConfiguration中可以发现,是定义在AbstractSecurityWebApplicationInitializer中的属性。
public static final String DEFAULT_FILTER_NAME = "springSecurityFilterChain";
好了,Spring Boot中的配置就结束了,主要是创建并设置DelegatingFilterProxy这个特殊的过滤器到servlet上下文中。
WebSecurityConfiguration
这是Spring Security中的一个配置类,会被Spring Framework中的ConfigurationClassPostProcess扫描到并处理。在该类中注册了上面创建的DelegatingFilterProxy中的目标过滤器。
// 注入spring security的过滤器链,在DelegatingFilterChain中会被调用
@Bean(name = AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME)
public Filter springSecurityFilterChain() throws Exception {
/*
* 一般来说,我们都会自定义一个过滤器链并注册到bean工厂中。那么这里就会使用我们配置好的,
* 如果没有配置,则会使用默认的。
* 当然我们自定义过滤器链还是会通过HttpSecurity来构建。
*/
boolean hasFilterChain = !this.securityFilterChains.isEmpty();
if (!hasFilterChain) { // this.securityFilterChains是空的情况
// 添加默认的过滤器链
this.webSecurity.addSecurityFilterChainBuilder(() -> {
this.httpSecurity.authorizeHttpRequests((authorize) -> authorize.anyRequest().authenticated());
this.httpSecurity.formLogin(Customizer.withDefaults());
this.httpSecurity.httpBasic(Customizer.withDefaults());
return this.httpSecurity.build();
});
}
// this.securityFilterChains不为空的情况
// 一般我们会自定义过滤器链,所以这里会添加到webSecurity中
for (SecurityFilterChain securityFilterChain : this.securityFilterChains) {
this.webSecurity.addSecurityFilterChainBuilder(() -> securityFilterChain);
}
// 遍历自定义扩展器
for (WebSecurityCustomizer customizer : this.webSecurityCustomizers) {
// 执行自定义操作
customizer.customize(this.webSecurity);
}
// 构建过滤器
return this.webSecurity.build();
}
一般我们会根据业务自定义并注册一个SecurityFilterChain类型的bean,在上面的方法中会被添加到WebSecurity对象中。最后通过WebSecurity来构建过滤器。
WebSecurity
AbstractSecurityBuilder
build方法的实现在父类AbstractSecurityBuilder中。
public abstract class AbstractSecurityBuilder<O> implements SecurityBuilder<O> {
private AtomicBoolean building = new AtomicBoolean();
private O object;
@Override
public final O build() throws Exception {
// 通过CAS来保证不要重复构建
if (this.building.compareAndSet(false, true)) {
// 构建
this.object = doBuild();
return this.object;
}
throw new AlreadyBuiltException("This object has already been built");
}
/**
* Gets the object that was built. If it has not been built yet an Exception is
* thrown.
* @return the Object that was built
*/
public final O getObject() {
if (!this.building.get()) {
throw new IllegalStateException("This object has not been built");
}
return this.object;
}
/**
* Subclasses should implement this to perform the build.
* @return the object that should be returned by {@link #build()}.
* @throws Exception if an error occurs
*/
protected abstract O doBuild() throws Exception;
}
AbstractSecurityBuilder不仅是WebSecurity的父类,还是HttpSecurity的父类。
AbstractConfiguredSecurityBuilder
子类AbstractConfiguredSecurityBuilder中实现了doBuild方法。
@Override
protected final O doBuild() throws Exception {
synchronized (this.configurers) {
this.buildState = BuildState.INITIALIZING;
beforeInit();
init();
this.buildState = BuildState.CONFIGURING;
beforeConfigure();
configure();
this.buildState = BuildState.BUILDING;
// 执行构建
O result = performBuild();
this.buildState = BuildState.BUILT;
return result;
}
}
protected abstract O performBuild() throws Exception;
实际的构建操作在子类实现的performBuild方法中,子类WebSecurity和HttpSecurtiy都实现了这个方法。先来看WebSecurity中的实现。
WebSecurity
@Override
protected Filter performBuild() throws Exception {
Assert.state(!this.securityFilterChainBuilders.isEmpty(),
() -> "At least one SecurityBuilder<? extends SecurityFilterChain> needs to be specified. "
+ "Typically this is done by exposing a SecurityFilterChain bean. "
+ "More advanced users can invoke " + WebSecurity.class.getSimpleName()
+ ".addSecurityFilterChainBuilder directly");
// 可以看出,有两类的filterChain
int chainSize = this.ignoredRequests.size() + this.securityFilterChainBuilders.size();
// filterChain集合
List<SecurityFilterChain> securityFilterChains = new ArrayList<>(chainSize);
List<RequestMatcherEntry<List<WebInvocationPrivilegeEvaluator>>> requestMatcherPrivilegeEvaluatorsEntries = new ArrayList<>();
//
for (RequestMatcher ignoredRequest : this.ignoredRequests) {
WebSecurity.this.logger.warn("You are asking Spring Security to ignore " + ignoredRequest
+ ". This is not recommended -- please use permitAll via HttpSecurity#authorizeHttpRequests instead.");
// 创建过滤器链
SecurityFilterChain securityFilterChain = new DefaultSecurityFilterChain(ignoredRequest);
// 添加到列表中
securityFilterChains.add(securityFilterChain);
requestMatcherPrivilegeEvaluatorsEntries
.add(getRequestMatcherPrivilegeEvaluatorsEntry(securityFilterChain));
}
for (SecurityBuilder<? extends SecurityFilterChain> securityFilterChainBuilder : this.securityFilterChainBuilders) {
// 获取过滤器链
SecurityFilterChain securityFilterChain = securityFilterChainBuilder.build();
// 添加到列表中
securityFilterChains.add(securityFilterChain);
requestMatcherPrivilegeEvaluatorsEntries
.add(getRequestMatcherPrivilegeEvaluatorsEntry(securityFilterChain));
}
if (this.privilegeEvaluator == null) {
this.privilegeEvaluator = new RequestMatcherDelegatingWebInvocationPrivilegeEvaluator(
requestMatcherPrivilegeEvaluatorsEntries);
}
/*
* 创建过滤器,DelegatingFilterProxy中的目标过滤器就是这个对象。
*/
FilterChainProxy filterChainProxy = new FilterChainProxy(securityFilterChains);
// 设置http防火墙
if (this.httpFirewall != null) {
filterChainProxy.setFirewall(this.httpFirewall);
}
// 设置请求拒绝处理器
if (this.requestRejectedHandler != null) {
filterChainProxy.setRequestRejectedHandler(this.requestRejectedHandler);
}
else if (!this.observationRegistry.isNoop()) {
CompositeRequestRejectedHandler requestRejectedHandler = new CompositeRequestRejectedHandler(
new ObservationMarkingRequestRejectedHandler(this.observationRegistry),
new HttpStatusRequestRejectedHandler());
filterChainProxy.setRequestRejectedHandler(requestRejectedHandler);
}
// 设置修饰器
filterChainProxy.setFilterChainDecorator(getFilterChainDecorator());
// bean的生命周期相关方法
filterChainProxy.afterPropertiesSet();
Filter result = filterChainProxy;
if (this.debugEnabled) {
this.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);
}
this.postBuildAction.run();
return result;
}
在该方法中,先是合并了两类过滤器链,然后创建并初始化了FilterChainProxy对象,这个对象就是最终注册到bean工厂中的bean,也是Spring Security中DelegatingFilterProxy的目标过滤器。
DelegatingFilterProxy的初始化
DelegatingFilterProxy是继承了GenericFilterBean,后者实现了Filter接口。所以在初始化的时候会调用GenericFilterBean的init方法,而该方法又会调用DelegatingFilterProxy实现的initFilterBean方法。
@Override
protected void initFilterBean() throws ServletException {
synchronized (this.delegateMonitor) {
// 没有设置才获取,即调用的参数是字符串的构造方法才会执行
if (this.delegate == null) {
// If no target bean name specified, use filter name.
if (this.targetBeanName == null) {
this.targetBeanName = getFilterName();
}
// Fetch Spring root application context and initialize the delegate early,
// if possible. If the root application context will be started after this
// filter proxy, we'll have to resort to lazy initialization.
WebApplicationContext wac = findWebApplicationContext();
if (wac != null) {
// 从应用上下文中获取目标过滤器
this.delegate = initDelegate(wac);
}
}
}
}
protected Filter initDelegate(WebApplicationContext wac) throws ServletException {
String targetBeanName = getTargetBeanName();
Assert.state(targetBeanName != null, "No target bean name set");
// 获取目标过滤器bean对象
Filter delegate = wac.getBean(targetBeanName, Filter.class);
if (isTargetFilterLifecycle()) {
delegate.init(getFilterConfig());
}
return delegate;
}
在上面已经介绍过了,这里获取到的bean是FilterChainProxy类型的。
通过HttpSecurity自定义过滤器链
在5.x中,一般会创建一个配置类,并继承WebSecurityConfigurerAdapter,并重写该类中的configure方法, 该方法的唯一参数就是configure,然后基于该类来配置。 在6.x版本中,直接向容器中注册SecurityFilterChain就行了,当然也是通过该类来创建过滤器链。 在HttpSecurityConfiguration配置类中会创建并注册HttpSecurity,所以可以在用户自定义的配置类中注入该bean。
// 注入httpSecurity
@Bean(HTTPSECURITY_BEAN_NAME)
@Scope("prototype")
HttpSecurity httpSecurity() throws Exception {
// 创建密码编码器
LazyPasswordEncoder passwordEncoder = new LazyPasswordEncoder(this.context);
// 构造认证管理器的构建器
AuthenticationManagerBuilder authenticationBuilder = new DefaultPasswordEncoderAuthenticationManagerBuilder(
this.objectPostProcessor, passwordEncoder);
// 添加认证管理器
authenticationBuilder.parentAuthenticationManager(authenticationManager());
authenticationBuilder.authenticationEventPublisher(getAuthenticationEventPublisher());
// 构建HttpSecurity对象
HttpSecurity http = new HttpSecurity(this.objectPostProcessor, authenticationBuilder, createSharedObjects());
WebAsyncManagerIntegrationFilter webAsyncManagerIntegrationFilter = new WebAsyncManagerIntegrationFilter();
webAsyncManagerIntegrationFilter.setSecurityContextHolderStrategy(this.securityContextHolderStrategy);
// @formatter:off
/*
* 一系列默认配置,其实这里都是在往HttpSecurity对象中添加各种配置器(configurer),配置器又对应了各种filter
* 我们会通过http.build()方法来获取SecurityFilterChain对象,在build之前会执行beforeInit、init、beforeConfigure、configure一系列的方法,
* 在configure方法中会往httpSecurity对象中添加过滤器,然后build的时候会将这些过滤器进行排序,然后传递到DefaultSecurityFilterChain的构造器中
*/
http
.csrf(withDefaults())
.addFilter(webAsyncManagerIntegrationFilter)
.exceptionHandling(withDefaults())
.headers(withDefaults())
.sessionManagement(withDefaults())
.securityContext(withDefaults())
.requestCache(withDefaults())
.anonymous(withDefaults())
.servletApi(withDefaults())
.apply(new DefaultLoginPageConfigurer<>());
http.logout(withDefaults());
// @formatter:on
applyDefaultConfigurers(http);
return http;
}
上面方法中创建了HttpSecurity对象,并为其添加了默认配置,可以在业务实现中覆盖默认设置。
构建过滤器链
@SuppressWarnings("unchecked")
@Override
protected DefaultSecurityFilterChain performBuild() {
// URL表达式授权配置器
ExpressionUrlAuthorizationConfigurer<?> expressionConfigurer = getConfigurer(
ExpressionUrlAuthorizationConfigurer.class);
// http授权配置器
AuthorizeHttpRequestsConfigurer<?> httpConfigurer = getConfigurer(AuthorizeHttpRequestsConfigurer.class);
// 这里使用了异或运算符(^)
boolean oneConfigurerPresent = expressionConfigurer == null ^ httpConfigurer == null;
// 如果都为null或都不为null,则断言失败,请选一个使用
Assert.state((expressionConfigurer == null && httpConfigurer == null) || oneConfigurerPresent,
"authorizeHttpRequests cannot be used in conjunction with authorizeRequests. Please select just one.");
// 对过滤器进行排序
this.filters.sort(OrderComparator.INSTANCE);
List<Filter> sortedFilters = new ArrayList<>(this.filters.size());
for (Filter filter : this.filters) {
// 添加到列表中
sortedFilters.add(((OrderedFilter) filter).filter);
}
// 创建过滤器链
return new DefaultSecurityFilterChain(this.requestMatcher, sortedFilters);
}
这里创建的是DefaultSecurityFilterChain类型的对象,封装了实例属性filters。那么个filters属性中的过滤器是哪里来的?
调用配置器
一般我们会调用HttpSecurity中的方法并传入自定义器来实现扩展,好像并没有创建和添加过滤器,实际上这个奥秘就在配置器中。因为Spring Security支持太多种类型的配置器了,所以这里以HeadersConfigurer为例。
public HttpSecurity headers(Customizer<HeadersConfigurer<HttpSecurity>> headersCustomizer) throws Exception {
headersCustomizer.customize(getOrApply(new HeadersConfigurer<>()));
return HttpSecurity.this;
}
@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);
}
getConfigurer方法和apply方法都定义在父类AbstractConfiguredSecurityBuilder中。
private final LinkedHashMap<Class<? extends SecurityConfigurer<O, B>>, List<SecurityConfigurer<O, B>>> configurers = new LinkedHashMap<>();
@SuppressWarnings("unchecked")
public <C extends SecurityConfigurer<O, B>> C getConfigurer(Class<C> clazz) {
List<SecurityConfigurer<O, B>> configs = this.configurers.get(clazz);
if (configs == null) {
return null;
}
Assert.state(configs.size() == 1,
() -> "Only one configurer expected for type " + clazz + ", but got " + configs);
return (C) configs.get(0);
}
@SuppressWarnings("unchecked")
public <C extends SecurityConfigurerAdapter<O, B>> C apply(C configurer) throws Exception {
configurer.addObjectPostProcessor(this.objectPostProcessor);
configurer.setBuilder((B) this);
// 添加配置器
add(configurer);
return configurer;
}
@SuppressWarnings("unchecked")
private <C extends SecurityConfigurer<O, B>> void add(C configurer) {
Assert.notNull(configurer, "configurer cannot be null");
Class<? extends SecurityConfigurer<O, B>> clazz = (Class<? extends SecurityConfigurer<O, B>>) configurer
.getClass();
synchronized (this.configurers) {
if (this.buildState.isConfigured()) {
throw new IllegalStateException("Cannot apply " + configurer + " to already built object");
}
List<SecurityConfigurer<O, B>> configs = null;
if (this.allowConfigurersOfSameType) { // 允许相同类型的配置器
configs = this.configurers.get(clazz);
}
configs = (configs != null) ? configs : new ArrayList<>(1);
// 添加到列表中
configs.add(configurer);
// 添加到缓存中
this.configurers.put(clazz, configs);
if (this.buildState.isInitializing()) {
this.configurersAddedInInitializing.add(configurer);
}
}
}
这样看起来好像getOrApply只是添加了配置器而已,还是没有找到过滤器的踪影。其实在调用build时,在执行performBuild方法之前,还执行doBuild方法,在该方法中调用了init和configure等方法。
@Override
protected final O doBuild() throws Exception {
synchronized (this.configurers) {
this.buildState = BuildState.INITIALIZING;
beforeInit();
init();
this.buildState = BuildState.CONFIGURING;
beforeConfigure();
configure();
this.buildState = BuildState.BUILDING;
// 执行构建
O result = performBuild();
this.buildState = BuildState.BUILT;
return result;
}
}
@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 : this.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);
}
}
上面调用了每个配置器的init和configure方法。
对于HeadersConfigurer,重写了configure方法,并创建和注册了过滤器。
// 这里的http就是就是HttpSecurity
@Override
public void configure(H http) {
HeaderWriterFilter headersFilter = createHeaderWriterFilter();
http.addFilter(headersFilter);
}
private HeaderWriterFilter createHeaderWriterFilter() {
List<HeaderWriter> writers = getHeaderWriters();
if (writers.isEmpty()) {
throw new IllegalStateException(
"Headers security is enabled, but no headers will be added. Either add headers or disable headers security");
}
// 创建过滤器
HeaderWriterFilter headersFilter = new HeaderWriterFilter(writers);
headersFilter = postProcess(headersFilter);
return headersFilter;
}
有的配置器是在init方法中创建过滤器,然后在configure方法中添加过滤器。反正不同的配置器的逻辑过滤器创建和添加逻辑都差不过。
最后调用HttpSecurity的addFilter方法来添加过滤器。
@Override
public HttpSecurity addFilter(Filter filter) {
// 这里添加的filter必须是官方定义的,否则会报错
Integer order = this.filterOrders.getOrder(filter.getClass());
if (order == null) {
throw new IllegalArgumentException("The Filter class " + filter.getClass().getName()
+ " does not have a registered order and cannot be added without a specified order. Consider using addFilterBefore or addFilterAfter instead.");
}
// 封装为OrderedFilter,并添加到属性列表中
this.filters.add(new OrderedFilter(filter, order));
return this;
}
这样就知道了performBuild方法中的过滤器列表是哪里来的了。当然除了配置器,也可以直接调用HttpSecurity的addFilter方法来添加过滤器。
总结
HttpSecurity中定义了很多规则方法,每个方法的参数是一个定制化器,主要是对配置器进行定制化,定制化器中可以修改配置器中的属性来实现自定义。在构建过滤器之前,会调用各个配置器的init和configure方法来创建和添加过滤器,最后构建时,将所有的过滤器封装为一条过滤器链。