最近在整合微服务OAuth 2认证过程当中,它是基于Spring Security之上,而本人对Spring Security架构原理并不太熟悉,致使不少配置搞不太清楚,遂咬牙啃完了Spring Security核心源码,花了差很少一星期,整体上来讲,其代码确实比较晦涩,以前在学习Apache Shiro框架以前也曾经在相关论坛里了解过,相比Spring Security,Apache Shiro真的是至关轻量,代码研读起来容易不少,而Spring Security类继承结构复杂,大量使用了其所谓Builder和Configuer模式,其代码跟踪过程很痛苦,遂记录下,分享给有须要的人,因为本人能力有限,在文章中有不对之处,还请各位执教,在此谢谢各位了。css
本人研读的Spring Security版本为:5.1.4.RELEASE
java
Spring Security在3.2版本以后支持Java Configuration,即:经过Java编码形式配置Spring Security,可再也不依赖XML文件配置,本文采用Java Configuration方式。web
在Spring Security官方文档中有一个最简配置例子:spring
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.*;
import org.springframework.security.config.annotation.authentication.builders.*;
import org.springframework.security.config.annotation.web.configuration.*;
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth
.inMemoryAuthentication()
.withUser("user").password("password").roles("USER");
}
}复制代码
咱们先不要看其它内容,先关注注解@EnableWebSecurity
,它是初始化Spring Security的入口,打开其源码以下:bash
@Retention(value = java.lang.annotation.RetentionPolicy.RUNTIME)
@Target(value = { java.lang.annotation.ElementType.TYPE })
@Documented
@Import({ WebSecurityConfiguration.class,
SpringWebMvcImportSelector.class,
OAuth2ImportSelector.class })
@EnableGlobalAuthentication
@Configuration
public @interface EnableWebSecurity {
/** * Controls debugging support for Spring Security. Default is false. * @return if true, enables debug support with Spring Security */
boolean debug() default false;
}复制代码
该注解类经过@Configuration
和@Import
配合使用引入了一个配置类(WebSecurityConfiguration
)和两个ImportSelector(SpringWebMvcImportSelector
,OAuth2ImportSelector
),咱们重点关注下WebSecurityConfiguration
,它是Spring Security的核心,正是它构建初始化了全部的Bean实例和相关配置,下面咱们详细分析下。session
打开WebSecurityConfiguration
源码,发现它被@Configuration
标记,说明它是配置类,架构
@Configuration
public class WebSecurityConfiguration implements ImportAware, BeanClassLoaderAware 复制代码
该类中最重要的工做就是实例并注册FilterChainProxy
,也就是咱们在之前XML文件中配置的过滤器:app
<filter>
<filter-name>springSecurityFilterChain</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
<filter-name>springSecurityFilterChain</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>复制代码
该过滤器负责拦截请求,并把请求经过必定的匹配规则(经过RequestMatcher匹配实现)路由(或者Delegate)到具体的SecurityFilterChain
,源码以下:框架
/** * Creates the Spring Security Filter Chain * @return the {@link Filter} that represents the security filter chain * @throws Exception */
@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);
}
return webSecurity.build();
}复制代码
@Bean
注解name
属性值AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME
就是XML中定义的springSecurityFilterChain
。ide
从源码中知道过滤器经过最后的webSecurity.build()
建立,webSecurity
的类型为:WebSecurity
,它在setFilterChainProxySecurityConfigurer
方法中优先被建立了:
/** * Sets the {@code <SecurityConfigurer<FilterChainProxy, WebSecurityBuilder>} * instances used to create the web configuration. * * @param objectPostProcessor the {@link ObjectPostProcessor} used to create a * {@link WebSecurity} instance * @param webSecurityConfigurers the * {@code <SecurityConfigurer<FilterChainProxy, WebSecurityBuilder>} instances used to * create the web configuration * @throws Exception */
@Autowired(required = false)
public void setFilterChainProxySecurityConfigurer( ObjectPostProcessor<Object> objectPostProcessor, @Value("#{@autowiredWebSecurityConfigurersIgnoreParents.getWebSecurityConfigurers()}") List<SecurityConfigurer<Filter, WebSecurity>> webSecurityConfigurers) throws Exception {
webSecurity = objectPostProcessor
.postProcess(new WebSecurity(objectPostProcessor));
if (debugEnabled != null) {
webSecurity.debug(debugEnabled);
}
Collections.sort(webSecurityConfigurers, AnnotationAwareOrderComparator.INSTANCE);
Integer previousOrder = null;
Object previousConfig = null;
for (SecurityConfigurer<Filter, WebSecurity> config : webSecurityConfigurers) {
Integer order = AnnotationAwareOrderComparator.lookupOrder(config);
if (previousOrder != null && previousOrder.equals(order)) {
throw new IllegalStateException(
"@Order on WebSecurityConfigurers must be unique. Order of "
+ order + " was already used on " + previousConfig + ", so it cannot be used on "
+ config + " too.");
}
previousOrder = order;
previousConfig = config;
}
for (SecurityConfigurer<Filter, WebSecurity> webSecurityConfigurer : webSecurityConfigurers) {
webSecurity.apply(webSecurityConfigurer);
}
this.webSecurityConfigurers = webSecurityConfigurers;
}复制代码
从代码中能够看到,它是直接被new出来的:
webSecurity = objectPostProcessor
.postProcess(new WebSecurity(objectPostProcessor));复制代码
setFilterChainProxySecurityConfigurer方法参数中须要被注入两个对象:objectPostProcessor
和webSecurityConfigurers
,objectPostProcessor
是在ObjectPostProcessorConfiguration
配置类中注册的,而webSecurityConfigurers
则是使用了@Value
注解方式,注解内容为:#{@autowiredWebSecurityConfigurersIgnoreParents.getWebSecurityConfigurers()}
,经过源码了解,autowiredWebSecurityConfigurersIgnoreParents
是在本类中被注册:
@Bean
public static AutowiredWebSecurityConfigurersIgnoreParents autowiredWebSecurityConfigurersIgnoreParents( ConfigurableListableBeanFactory beanFactory) {
return new AutowiredWebSecurityConfigurersIgnoreParents(beanFactory);
}复制代码
在AutowiredWebSecurityConfigurersIgnoreParents中定义了方法:getWebSecurityConfigurers
:
@SuppressWarnings({ "rawtypes", "unchecked" })
public List<SecurityConfigurer<Filter, WebSecurity>> getWebSecurityConfigurers() {
List<SecurityConfigurer<Filter, WebSecurity>> webSecurityConfigurers = new ArrayList<SecurityConfigurer<Filter, WebSecurity>>();
Map<String, WebSecurityConfigurer> beansOfType = beanFactory
.getBeansOfType(WebSecurityConfigurer.class);
for (Entry<String, WebSecurityConfigurer> entry : beansOfType.entrySet()) {
webSecurityConfigurers.add(entry.getValue());
}
return webSecurityConfigurers;
}复制代码
它经过BeanFactory获取了类型为WebSecurityConfigurer
的Bean实例列表。回到WebSecurityConfiguration
类中的setFilterChainProxySecurityConfigurer
方法,它把WebSecurityConfigurer
列表设置到了WebSecurity
中,源码以下:
for (SecurityConfigurer<Filter, WebSecurity> webSecurityConfigurer : webSecurityConfigurers) {
webSecurity.apply(webSecurityConfigurer);
}复制代码
经过apply
方法,apply方法其实就是webSecurityConfigurer
放入webSecurity
维护的configurers
属性中,configurers
是个LinkedHashMap
,源码以下:
/** * Applies a {@link SecurityConfigurer} to this {@link SecurityBuilder} overriding any * {@link SecurityConfigurer} of the exact same class. Note that object hierarchies * are not considered. * * @param configurer * @return the {@link SecurityConfigurerAdapter} for further customizations * @throws Exception */
public <C extends SecurityConfigurer<O, B>> C apply(C configurer) throws Exception {
add(configurer);
return configurer;
}复制代码
其中代码add(configurer)
就是将这些webSecurityConfigurer
添加到webSecurity
的configurers
属性中。
如今webSecurity
的初始化工做已经完成,如今回到springSecurityFilterChain
方法中,它首先检查当前是否配置了webSecurityConfigurer
,若是没有的会默认设置一个,而且调用上面提到的apply
方法,源码以下:
boolean hasConfigurers = webSecurityConfigurers != null
&& !webSecurityConfigurers.isEmpty();
if (!hasConfigurers) {
WebSecurityConfigurerAdapter adapter = objectObjectPostProcessor
.postProcess(new WebSecurityConfigurerAdapter() {
});
webSecurity.apply(adapter);
}复制代码
若是已经存在配置了webSecurityConfigurer
,则调用webSecurity.build()
进行构建。
在进入build
方法以前,首先简单介绍下WebSecurity的继承结构,
它实现了SecurityBuilder
接口,继承自AbstractConfiguredSecurityBuilder
,AbstractConfiguredSecurityBuilder
继承自AbstractSecurityBuilder
,AbstractSecurityBuilder
实现了SecurityBuilder
,其中AbstractConfiguredSecurityBuilder
实现了经过自定义SecurityConfigurer
类来配置SecurityBuilder
,上面提到的apply(SecurityConfigurer configurer)
就是在该类中实现的,它把configurer保存在它维护的LinkedHashMap<Class<? extends SecurityConfigurer<O, B>>, List<SecurityConfigurer<O, B>>> configurers = new LinkedHashMap<Class<? extends SecurityConfigurer<O, B>>, List<SecurityConfigurer<O, B>>>()
中。
调用webSecurity.build()
后,首先调用的父类AbstractSecurityBuilder
中的build
方法:
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");
}复制代码
而后调用doBuild()
,doBuild()
在子类中实现,AbstractConfiguredSecurityBuilder
实现了该方法:
@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;
}
}复制代码
在这里重点关注init()
、configure()
和performBuild()
,下面逐个分析它们的做用。
init()
方法在AbstractConfiguredSecurityBuilder
实现:
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);
}
}复制代码
它的工做是迭代调用全部配置的SecurityConfigrer
的init
方法,在这里实际上是它的子类WebSecurityConfigurer
,由于以前获取时指定的类型就是WebSecurityConfigurer
,在上文中提到AutowiredWebSecurityConfigurersIgnoreParents.getWebSecurityConfigurers()
中:
Map<String, WebSecurityConfigurer> beansOfType = beanFactory.getBeansOfType(WebSecurityConfigurer.class);复制代码
而实现了WebSecurityConfigurer
接口的就是WebSecurityConfigurerAdapter
,WebSecurityConfigurerAdapter.init()
源码以下:
public void init(final WebSecurity web) throws Exception {
final HttpSecurity http = getHttp();
web.addSecurityFilterChainBuilder(http).postBuildAction(new Runnable() {
public void run() {
FilterSecurityInterceptor securityInterceptor = http
.getSharedObject(FilterSecurityInterceptor.class);
web.securityInterceptor(securityInterceptor);
}
});
}复制代码
它只要完成两件重要的事情:
HttpSecurity
对象;HttpSecurity
对象添加至WebSecurity
的securityFilterChainBuilders
列表中;初始化HttpSecurity
对象在getHttp()
方法中实现:
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<? extends Object>, Object> sharedObjects = createSharedObjects();
http = new HttpSecurity(objectPostProcessor, authenticationBuilder,
sharedObjects);
if (!disableDefaults) {
// @formatter:off
http
.csrf().and()
.addFilter(new WebAsyncManagerIntegrationFilter())
.exceptionHandling().and()
.headers().and()
.sessionManagement().and()
.securityContext().and()
.requestCache().and()
.anonymous().and()
.servletApi().and()
.apply(new DefaultLoginPageConfigurer<>()).and()
.logout();
// @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;
}复制代码
从代码中能够了解,HttpSecurity
是直接被new出来的,在建立HttpSecurity
以前,首先初始化了AuthenticationManagerBuilder
对象,这里有段代码很重要就是: AuthenticationManager authenticationManager = authenticationManager();
,它建立AuthenticationManager
实例,打开authenticationManager()
方法:
protected AuthenticationManager authenticationManager() throws Exception {
if (!authenticationManagerInitialized) {
configure(localConfigureAuthenticationBldr);
if (disableLocalConfigureAuthenticationBldr) {
authenticationManager = authenticationConfiguration
.getAuthenticationManager();
}
else {
authenticationManager = localConfigureAuthenticationBldr.build();
}
authenticationManagerInitialized = true;
}
return authenticationManager;
}复制代码
在初始化时,它会调用configure(localConfigureAuthenticationBldr);
,默认的实现是:
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
this.disableLocalConfigureAuthenticationBldr = true;
}复制代码
【一、个性化配置入口之configure(AuthenticationManagerBuilder auth)
】
咱们能够经过继承WebSecurityConfigurerAdapter
并重写该方法来个性化配置AuthenticationManager
。
构建完authenticationManager
实例后,将它设置为authenticationBuilder
的父认证管理器:
authenticationBuilder.parentAuthenticationManager(authenticationManager);复制代码
并将该authenticationBuilder
传入HttpSecurity
构造器构建HttpSecurity
实例。
构建完HttpSecurity
实例后,默认状况下会添加默认的拦截其配置:
http
.csrf().and()
.addFilter(new WebAsyncManagerIntegrationFilter())
.exceptionHandling().and()
.headers().and()
.sessionManagement().and()
.securityContext().and()
.requestCache().and()
.anonymous().and()
.servletApi().and()
.apply(new DefaultLoginPageConfigurer<>()).and()
.logout();复制代码
最后调用configure(http);
,这又是一个可个性化的配置入口,它的默认实现是:
protected void configure(HttpSecurity http) throws Exception {
logger.debug("Using default configure(HttpSecurity). If subclassed this will potentially override subclass configure(HttpSecurity).");
http
.authorizeRequests()
.anyRequest().authenticated()
.and()
.formLogin().and()
.httpBasic();
}复制代码
默认的配置是拦截全部的请求须要认证以后才能访问,若是没有认证,会自动生成一个认证表单要求输入用户名和密码。
【二、个性化配置入口之configure(HttpSecurity http)
】
咱们能够经过继承WebSecurityConfigurerAdapter
并重写该方法来个性化配置HttpSecurity
。
OK,目前为止HttpSecurity
已经被初始化,接下去须要设置HttpSecurity
对象添加至WebSecurity
的securityFilterChainBuilders
列表中:
web.addSecurityFilterChainBuilder(http).postBuildAction(new Runnable() {
public void run() {
FilterSecurityInterceptor securityInterceptor = http
.getSharedObject(FilterSecurityInterceptor.class);
web.securityInterceptor(securityInterceptor);
}
});复制代码
打开HttpSecurity
类结构,和WebSecurity同样,它也实现了SecurityBuilder
接口,一样继承自AbstractConfiguredSecurityBuilder
。
当全部的WebSecurityConfigurer
的init
方法被调用以后,webSecurity.init()
工做就结束了。
接下去调用了webSecurity.configure()
,该方法一样是在AbstractConfiguredSecurityBuilder
中实现的:
private void configure() throws Exception {
Collection<SecurityConfigurer<O, B>> configurers = getConfigurers();
for (SecurityConfigurer<O, B> configurer : configurers) {
configurer.configure((B) this);
}
}复制代码
它的主要工做是迭代调用全部WebSecurityConfigurer
的configurer
方法,参数是WebSeucrity
自己,这又是另一个重要的个性化入口:
【三、个性化配置入口之configure(WebSecurity web)
】
咱们能够经过继承WebSecurityConfigurerAdapter
并重写该方法来个性化配置WebSecurity
。
自此,三个重要的个性化入口都已经被调用,即在实现WebSecurityConfigurerAdapter
常常须要重写的:
一、configure(AuthenticationManagerBuilder auth);
二、configure(WebSecurity web);
三、configure(HttpSecurity http);复制代码
回到webSecurity构建过程,接下去重要的的调用:
O result = performBuild();复制代码
该方法在WebSecurityConfigurerAdapter
中实现,返回的就是过滤器FilterChainProxy,源码以下:
@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<>(
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;
}复制代码
首先计算出chainSize
,也就是ignoredRequests.size() + securityFilterChainBuilders.size();
,若是你不配置ignoredRequests
,那就是securityFilterChainBuilders.size()
,也就是HttpSecurity
的个数,其本质上就是你一共配置几个WebSecurityConfigurerAdapter
,由于每一个WebSecurityConfigurerAdapter
对应一个HttpSecurity
,而所谓的ignoredRequests
就是FilterChainProxy
的请求,默认是没有的,若是你须要条跳过某些请求不须要认证或受权,能够以下配置:
@Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/statics/**");
}复制代码
在上面配置中,全部以/statics
开头请求都将被FilterChainProxy
忽略。
计算完chainSize
后,就会建立List<SecurityFilterChain> securityFilterChains = new ArrayList<>(chainSize);
,遍历全部的HttpSecurity
,调用HtppSecurity
的build()
构建其对应的过滤器链SecurityFilterChain
实例,并将SecurityFilterChain
添加到securityFilterChains
列表中:
for (SecurityBuilder<? extends SecurityFilterChain> securityFilterChainBuilder : securityFilterChainBuilders) {
securityFilterChains.add(securityFilterChainBuilder.build());
}复制代码
调用HtppSecurity
的build()
构建其实和调用WebSecurity
的build()
构建类相似,父类中方法一次被执行,最后执行自己的performBuild()
方法,其源码以下:
@Override
protected DefaultSecurityFilterChain performBuild() throws Exception {
Collections.sort(filters, comparator);
return new DefaultSecurityFilterChain(requestMatcher, filters);
}复制代码
构建SecurityFilterChain
主要是完成RequestMatcher
和对应的过滤器列表,咱们都知道在Spring Security中,过滤器执行按顺序顺序的,这个排序就是在performBuild()
中完成的,也就是:
Collections.sort(filters, comparator);复制代码
它经过一个比较器实现了过滤器的排序,这个比较器就是FilterComparator
,有兴趣的朋友能够本身去了解详情。
最后返回的是SecurityFilterChain
的默认实现DefaultSecurityFilterChain
。
构建完全部SecurityFilterChain
后,建立最为重要的FilterChainProxy
实例,
FilterChainProxy filterChainProxy = new FilterChainProxy(securityFilterChains);复制代码
构造器中传入SecurityFilterChain
列表,若是开启了Debug模式,还会被包装成DebugFilter
类型,共开发调试使用,默认是关闭的,能够经过过下面方式开启Debug模式:
@Override
public void configure(WebSecurity web) throws Exception {
web.debug(true);
}复制代码
至此Spring Security 初始化完成,咱们经过继承WebSecurityConfigurerAdapter
来代达到个性化配置目的,文中提到了三个重要的个性化入口,而且WebSecurityConfigurerAdapter
是能够配置多个的,其对应的接口就是会存在多个SecurityFilterChain
实例,可是它们人仍然在同一个FilterChainProxy中,经过RequestMatcher
来匹配并传入到对应的SecurityFilterChain
中执行请求。