上一章介绍了Spring Security的相关知识点,这章将详细分析源码。
首先需要认识到Spring Security的关键是filter——FilterChainProxy,经过一层层的filter才能最终访问到我们的资源信息。
同时要了解,访问不同的uri,系统会采取对应的filter列表进行过滤,如下图所示。
严谨点说不止是uri,可以自定义匹配头信息啊或者其他的,http请求中可以用来区分的都可以做为匹配条件。这里为了方便理解。
那么Spring Security启动的时候是如何加载这些filter和排序的呢?下面是访问/login uri对应的FilterChain。
Spring Security filter配置与原理
上一章我们了解到,需要人为配置WebSecurityConfigurerAdapter的继承类,如下图所示。注意到类上有注解@EnableWebSecurity。
@Configuration
@EnableWebSecurity
public class MyWebSecurityConfiguration extends WebSecurityConfigurerAdapter {
@Override
public void configure(WebSecurity web) throws Exception {
web.ignoring()
// Spring Security should completely ignore URLs starting with /resources/
.antMatchers("/resources/**");
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().antMatchers("/public/**").permitAll().anyRequest()
.hasRole("USER").and()
// Possibly more configuration ...
.formLogin() // enable form based log in
// set permitAll for all URLs associated with Form Login
.permitAll();
}
@Override
protected void configure(AuthenticationManagerBuilder auth) {
auth
// enable in memory based authentication with a user named "user" and "admin"
.inMemoryAuthentication().withUser("user").password("password").roles("USER")
.and().withUser("admin").password("password").roles("USER", "ADMIN");
}
// Possibly more overridden methods ...
}
@EnableWebSecurity是用于初始化WebSecurityConfiguration.class和引入@EnableGloabalAuthentication等,源码如下。
@Retention(value = java.lang.annotation.RetentionPolicy.RUNTIME)
@Target(value = { java.lang.annotation.ElementType.TYPE })
@Documented
@Import({ WebSecurityConfiguration.class,
SpringWebMvcImportSelector.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;
}
为何要加载WebSecurityConfiguration呢?它又有什么用呢?在此之前先得了解几个概念。
SecurityBuilder<O>
顾名思义是一个builder构造器,创建并返回一个类型为O的对象,源码如下
public interface SecurityBuilder<O> {
/**
* Builds the object and returns it or null.
*
* @return the Object to be built or null if the implementation allows it.
* @throws Exception if an error occurred when building the Object
*/
O build() throws Exception;
}
它的实现类如下图所示,可以看到很多熟悉的类。
AbstractSecurityBuilder<O>核心方法有
- build()
- doBuild()
- getObject()
源码如下,AtomicBoolean.compareAndSet(false,true)限定build()只会进行一次!然后子类需要重写的方法变为doBuild()。
public abstract class AbstractSecurityBuilder<O> implements SecurityBuilder<O> {
private AtomicBoolean building = new AtomicBoolean();
private O object;
// 只会创建一次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");
}
// 获取object
public final O getObject() {
if (!this.building.get()) {
throw new IllegalStateException("This object has not been built");
}
return this.object;
}
// 子类需要继承doBuild()方法
protected abstract O doBuild() throws Exception;
}
AbstractConfiguredSecurityBuilder<O,B extends SecurityBuilder<O>> 主要作用是将SpringConfigurer注入到属性configurers中,然后重写doBuild()方法遍历configurers进行init()和configure()。
它的子类HttpSecurity和WebSecurity都沿用了它的doBuild()方法!!!即遍历configures进行init()和configure()!!!
核心方法
- apply(C configurer)
- add(C configurer)
- doBuild()
- abstract performBuild()
核心属性(WebSecurity,HttpSecurity都会沿用该重要属性)
private final LinkedHashMap<Class<? extends SecurityConfigurer<O, B>>, List<SecurityConfigurer<O, B>>> configurers = new LinkedHashMap<Class<? extends SecurityConfigurer<O, B>>, List<SecurityConfigurer<O, B>>>();
configurers是个LinkedHashMap,key为class,value为List<SecurityConfigurer<O,B>>,通过apply()方法将configure存入configures中。
public <C extends SecurityConfigurer<O, B>> C apply(C configurer) throws Exception {
add(configurer);
return configurer;
}
@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) {
// buildState是个enum类,有几个状态如UNBUILT,INITIALIZING,CONFIGURING,BUILDING,BUILT
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);
// put进configurers中
this.configurers.put(clazz, configs);
if (buildState.isInitializing()) {
this.configurersAddedInInitializing.add(configurer);
}
}
}
既然apply()方法将SecurityConfigurer注入了属性configurers中,肯定要用到该属性,下面就是核心代码doBuild()
// 重写了AbstractSecurityBuilder方法
@Override
protected final O doBuild() throws Exception {
synchronized (configurers) {
// 状态=1
buildState = BuildState.INITIALIZING;
beforeInit();
// 遍历configurers中所有configurer,分别执行init()创建SecurityBuilder<O>
init();
buildState = BuildState.CONFIGURING;
beforeConfigure();
// 遍历后执行各个实现类的configure()方法
configure();
buildState = BuildState.BUILDING;
// abstract方法,获取build后的值
O result = performBuild();
buildState = BuildState.BUILT;
return result;
}
}
SecurityConfigurer<O,B extends SecurityBuilder<O>>
一句话概括:初始化(init)SecurityBuilder,且配置(configure)SecurityBuilder
/**
* 初始化B,且配置B的相关属性
* B SecurityBuilder<O>的子类
* O B.build()返回的object类型
*/
public interface SecurityConfigurer<O, B extends SecurityBuilder<O>> {
/**
* 初始化SecurityBuilder<O>
* 只创建设置了共享的变量,不会设置configure()中需要的特殊属性
* @param builder
* @throws Exception
*/
void init(B builder) throws Exception;
/**
* 设置SecurityBuilder<O>的特殊属性
* 如
*/
void configure(B builder) throws Exception;
}
SecurityConfigurer<O,B extends SecurityBuilder<O>>的实现类见下图,终于见到了我们的老朋友WebSecurityConfigurerAdapter
WebSecurityConfigurer是继承了SecurityConfigurer的接口,SecurityBuilder返回类型是Filter!!!
public interface WebSecurityConfigurer<T extends SecurityBuilder<Filter>> extends
SecurityConfigurer<Filter, T> {
}
WebSecurityConfigurerAdapter
对应的SecurityBuilder是WebSecurity(继承SecurityBuilder<Filter>),里面有方法init(WebSecurity web)和configure(WebSecurity Web)。三大configure()还记得么?
configure(AuthenticationManagerBuilder auth)
configure(HttpSecurity http)
configure(WebSecurity web)
如果不记得的话可以移步到上一章进行查阅。今天主要探讨下WebSecurityConfigurerAdapter作为SecuityBuilder<Filter>和SecurityConfigurer<Filter,T>的功能。
SecurityConfigurer的继承类自然需要init()和configure()。
其中init()的作用是获取HttpSecurity,并将http作为SecurityBuilder<? extends SecurityFilterChain> 存入webSecurity.SecurityFilterChainBuilders属性中。httpSecurity包含configurers属性(如ExceptionHandlingConfigurer、),通过http.csrf().and()来设置configurer,后期这些configurers进行configure时会将特定的filter加入到httpSecurity中。
下图是ExceptionHandlingConfigurer中的configure()方法
@Override
public void configure(H http) throws Exception {
AuthenticationEntryPoint entryPoint = getAuthenticationEntryPoint(http);
ExceptionTranslationFilter exceptionTranslationFilter = new ExceptionTranslationFilter(
entryPoint, getRequestCache(http));
if (accessDeniedHandler != null) {
exceptionTranslationFilter.setAccessDeniedHandler(accessDeniedHandler);
}
exceptionTranslationFilter = postProcess(exceptionTranslationFilter);
http.addFilter(exceptionTranslationFilter);
}
下图是WebSecurityConfigurerAdapter中的部分源码
@Order(100)
public abstract class WebSecurityConfigurerAdapter implements
WebSecurityConfigurer<WebSecurity> {
/**
* AbstractConfiguredSecurityBuilder(实现类有WebSecurity,HttpSecurity等)中的init()
* 会遍历所有configurers,调用configure.init()方法,那么WebSecurityConfigurerAdapter作为
* configure调用的init()方法就是下面的代码
* 获取HttpSecurity,存入webSecurity中
*/
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实例,configure(http)后,http中的属性configurers注入相应的configurer
* http.csrf()会将CsrfConfigurer<HttpSecurity>存入HttpSecurity.configurers中
* http.csrf().disable()从HttpSecurity.configurers中移除CsrfConfigurer
* 依次类推
*/
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);
Map<Class<? extends Object>, Object> sharedObjects = createSharedObjects();
http = new HttpSecurity(objectPostProcessor, authenticationBuilder,
sharedObjects);
if (!disableDefaults) {
// @formatter:off
// headers()等方法将configure apply()到了http的属性configurers中,这里默认会注入10个configurer
http
.csrf().and()
.addFilter(new WebAsyncManagerIntegrationFilter())
.exceptionHandling().and()
.headers().and()
.sessionManagement().and()
.securityContext().and()
.requestCache().and()
.anonymous().and()
.servletApi().and()
.apply(new DefaultLoginPageConfigurer<HttpSecurity>()).and()
.logout();
// @formatter:on
ClassLoader classLoader = this.context.getClassLoader();
// 默认是为空
List<AbstractHttpConfigurer> defaultHttpConfigurers =
SpringFactoriesLoader.loadFactories(AbstractHttpConfigurer.class, classLoader);
for(AbstractHttpConfigurer configurer : defaultHttpConfigurers) {
http.apply(configurer);
}
}
// this.configurer(http) 根据this的实现类选择对应方法
configure(http);
return http;
}
}
SecurityFilterChain
定义一个Filter Chain,包含一组Filters,且提供方法判断与request的路由是否匹配,源码如下。
public interface SecurityFilterChain {
boolean matches(HttpServletRequest request);
List<Filter> getFilters();
}
SecurityFilterChain常用于SecurityBuilder<? extends SecurityFilterChain>,作为builder的返回类型,通过build()获取到实例后,存入FilterChainProxy的属性List<SecurityFilterChain> filterChains中,在FilterChainProxy.doFilterInternal()中执行getFilters(HttpServletRequest request),找出对应request的SecurityFilterChain并返回Filters。
继承类为DefaultSecurityFilterChain,有属性RequestMatcher requestMatcher和List<Filter> filters
FilterChainProxy
非常重要的知识点!!!
filter chain代理,由Spring管理生命周期。
在Spring Security框架中,用户想要访问资源需要经过FilterChainProxy(本质上是个Filter)来过滤。
里面包含一组SecuriyFilterChains,每个uri都对应一个SecurityFilterChain,即对应SecurityFilterChain中的Filters
核心代码如下
// 继承了Filter
public class FilterChainProxy extends GenericFilterBean {
// 重要属性,由WebSecurity中的performBuild()方法传递值过来
private List<SecurityFilterChain> filterChains;
private FilterChainValidator filterChainValidator = new NullFilterChainValidator();
public FilterChainProxy() {
}
public FilterChainProxy(SecurityFilterChain chain) {
this(Arrays.asList(chain));
}
public FilterChainProxy(List<SecurityFilterChain> filterChains) {
this.filterChains = filterChains;
}
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
boolean clearContext = request.getAttribute(FILTER_APPLIED) == null;
if (clearContext) {
try {
request.setAttribute(FILTER_APPLIED, Boolean.TRUE);
// 跳转到doFilterInternal方法
doFilterInternal(request, response, chain);
}
finally {
SecurityContextHolder.clearContext();
request.removeAttribute(FILTER_APPLIED);
}
}
else {
doFilterInternal(request, response, chain);
}
}
private void doFilterInternal(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
FirewalledRequest fwRequest = firewall
.getFirewalledRequest((HttpServletRequest) request);
HttpServletResponse fwResponse = firewall
.getFirewalledResponse((HttpServletResponse) response);
// 根据request,SecurityFilterChains获取符合路由规则的SecurityFilterChain,并提取出filters
List<Filter> filters = getFilters(fwRequest);
if (filters == null || filters.size() == 0) {
if (logger.isDebugEnabled()) {
logger.debug(UrlUtils.buildRequestUrl(fwRequest)
+ (filters == null ? " has no matching filters"
: " has an empty filter list"));
}
fwRequest.reset();
chain.doFilter(fwRequest, fwResponse);
return;
}
// 虚拟filter chain,这是个内部类,模拟doFilter的动作
VirtualFilterChain vfc = new VirtualFilterChain(fwRequest, chain, filters);
vfc.doFilter(fwRequest, fwResponse);
}
// 返回第一个满足路由规则的SecurityFilterChain
private List<Filter> getFilters(HttpServletRequest request) {
for (SecurityFilterChain chain : filterChains) {
if (chain.matches(request)) {
return chain.getFilters();
}
}
return null;
}
}
WebSecurity
重头戏来了,前面介绍的所有知识点都是为了给它铺路,简单概括就是
本质是SecurityBuilder<Filter>
有重要属性List<SecurityBuilder<? extends SecurityFilterChains>> securityFilterChainsBuilder,默认值是由WebSecurityConfigurerApdapter中传入的HttpSecurity。
继承了抽象类AbstractConfiguredSecurityBuilder(Filter,WebSecurity),重写抽象方法performBuild(),创建FilterChainProxy(Filter的实现类)并返回
ignoring()方法排除无需认证的路径
public final class WebSecurity extends
AbstractConfiguredSecurityBuilder<Filter, WebSecurity>
implements
SecurityBuilder<Filter>, ApplicationContextAware {
private final List<SecurityBuilder<? extends SecurityFilterChain>> securityFilterChainBuilders = new ArrayList<SecurityBuilder<? extends SecurityFilterChain>>();
/**
* 添加securityFilterChainBuilder
* 默认WebSecurityConfigurerAdapter中的init(WebSecurity web)方法会调用这个方法
* 传入的securityFilterChainBuilder是getHttp()返回的HttpSecurity
* securityFilterChainBuilder 是SecurityBuilder.build()返回SecurityFilterChain
* SecurityFilterChain 有getFilters()和matches()方法
*/
public WebSecurity addSecurityFilterChainBuilder(
SecurityBuilder<? extends SecurityFilterChain> securityFilterChainBuilder) {
this.securityFilterChainBuilders.add(securityFilterChainBuilder);
return this;
}
/**
* 返回类型是FilterChainProxy
* 从securityFilterChainBuilders中遍历securityFilterChainBuilder,将build()的返回值SecurityFilterChain写入集合securityFilterChains中
* 将securityFilterChains注入到新建的FilterChainProxy里,FilterChainProxy.doFilterInternal()方法会从securityFilterChains中
* 选取与request匹配的SecurityFilterChain,提取其中的filters作为filter chain
*/
@Override
protected Filter performBuild() throws Exception {
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());
}
// 传入securityFilterChains初始化filterChainProxy,主要方法doFilterInternal()
FilterChainProxy filterChainProxy = new FilterChainProxy(securityFilterChains);
if (httpFirewall != null) {
filterChainProxy.setFirewall(httpFirewall);
}
filterChainProxy.afterPropertiesSet();
Filter result = filterChainProxy;
postBuildAction.run();
return result;
}
}
HttpSecurity
本质是SecurityBuilder<DefaultSecurityFilterChain>,可以传入到WebSecurity的securityFilterChainBuilders属性中
属性 List<Filter> filters 方法 addFilter(),addFilterAt()等
一般在WebSecurityConfigurerApdater的继承类中使用
@Configuration
@EnableWebSecurity
public class AuthorizeUrlsSecurityConfig extends WebSecurityConfigurerAdapter {
@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("adminr").password("password").roles("ADMIN", "USER");
}
}
WebSecurityConfiguration
主要作用是初始化WebSecurity,且创建名为“springSecurityFilterChain”类型为FilterChainProxy的过滤器。
核心方法有
- setFilterChainProxySecurityConfigurer
- springSecurityFilterChain()
setFilterChainProxySecurityConfigurer创建webSecurity,并通过apply(Configurer c)方法注入spring容器管理的所有WebSecurityConfigurer。
/**
* spring注入SecurityConfigurer<FilterChainProxy,WebSecurity>
* webSecurityConfigurers 默认为3个,
*/
@Autowired(required = false)
public void setFilterChainProxySecurityConfigurer(
ObjectPostProcessor<Object> objectPostProcessor,
@Value("#{@autowiredWebSecurityConfigurersIgnoreParents.getWebSecurityConfigurers()}") List<SecurityConfigurer<Filter, WebSecurity>> webSecurityConfigurers)
throws Exception {
// 新建webSecurity,然后apply() configurers
webSecurity = objectPostProcessor
.postProcess(new WebSecurity(objectPostProcessor));
...
//
for (SecurityConfigurer<Filter, WebSecurity> webSecurityConfigurer : webSecurityConfigurers) {
webSecurity.apply(webSecurityConfigurer);
}
this.webSecurityConfigurers = webSecurityConfigurers;
}
根据@Value("#{@autowiredWebSecurityConfigurersIgnoreParents.getWebSecurityConfigurers()}") 定位到下图所示的方法,从beanFactory里获取所有类型为WebSecurityConfigurer的实例,这里有3个,其中securityConfig是自己编写继承WebSecurityConfigurerAdapter的类。
springSecurityFilterChain()通过webSecurity.build()方法创建名为"springSecurityFilterChain"的FilterChainProxy
@Bean(name = AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME)
public Filter springSecurityFilterChain() throws Exception {
// 先执行了setFilterChainProxySecurityConfigurer()方法,所以属性webSecurityConfigurers有值
boolean hasConfigurers = webSecurityConfigurers != null
&& !webSecurityConfigurers.isEmpty();
if (!hasConfigurers) {
WebSecurityConfigurerAdapter adapter = objectObjectPostProcessor
.postProcess(new WebSecurityConfigurerAdapter() {
});
webSecurity.apply(adapter);
}
// 核心方法,返回FilterChainProxy
return webSecurity.build();
}
webSecurity.build()会触发AbstractConfiguredSecurityBuilder中的doBuild()方法,里面关键方法有init(),configurer()和performBuild(),最终返回FilterChainProxy。
现在webSecurity里的属性configurers有3个:
securityConfig (用户自定义类继承WebSecurityConfigurerAdapter)
ignoredPathsWebSecurityConfigurerApdater
SpringBootWebSecurityConfiguration
@Override
protected final O doBuild() throws Exception {
synchronized (configurers) {
buildState = BuildState.INITIALIZING;
beforeInit();
// 遍历init(),其中WebSecurityConfigurerAdapter的init()方法获取包含configurers的HttpSecurity实例,存入webSecurity中
init();
buildState = BuildState.CONFIGURING;
beforeConfigure();
// 遍历configure()
configure();
buildState = BuildState.BUILDING;
// 执行WebSecurity的performBuild()方法,将SecurityFilterChainBuilders(即httpSecurity集合)遍历执行build()方法
// 此时http.build()会将http里的configurers属性转换后写入filters中
// 获得SecurityFilterChains,存入FilterChainProxy中并返回
O result = performBuild();
buildState = BuildState.BUILT;
return result;
}
}
总结
如果你看了一遍发现什么也没记住,那么恭喜你,你已经达到了道家中无我的境界了
张三丰教张无忌太极拳,问他记住了多少。无忌说只记住了一半,再问记住多少,无忌想了会说已经全忘了,张三丰满意的笑了,现在你可以去会玄冥二老了。
上面讲的很零散,但都是基本,现在我们按程序执行顺序串着讲一遍,看能记住多少。
1.WebSecurityConfiguration初始化WebSecurity,并传入configurers(类型为WebSecurityConfigurer.class)属性,如WebSecurityConfigurerApdater继承类,IgnoredPathsWebSecurityConfigurerAdapter,ApplicationWebSecurityConfigurerAdapter等。最后执行webSecurity.build()返回FilterChainProxy,取名为"springSecurityFilterChain"。
2.webSecurity.build()方法会调用AbstractConfiguredSecurityBuilder.doBuild(),即遍历webSecurity.configurers,执行init()和configure(),最后performBuild()返回FilterChainProxy。
2.1.WebSecurityConfigurerApdater.init()方法,通过getHttp()方法获得配置好的HttpSecurity,里面包含了configurers(如HeadersConfigurer,SecurityContextConfigure,LogoutConfigurer等)。将HttpSecurity添加到webSecurity的属性securityFilterChainBuilders集合里,以后在webSecurity.performBuild()中会调用。
2.2.WebSecurityConfigurerApdater.configure(webSecurity)方法,默认为空,继承类中可重写该方法
2.3.webSecurity.performBuild(),遍历securityFilterChainBuilders(即httpSecurity集合),执行http.build()方法获得DefaultSecurityFilterChain,存入新建的FilterChainProxy中并返回。其中http.build()方法会将http中的configures属性转换成filter添加到http.filters属性中,再根据http.performBuild()方法new DefaultSecurityFilterChain(requestMatcher,filters)并返回。
3.至此webSecurity.build()返还了FilterChainProxy,作为bean由spring管理,专门用于用户验证和资源授权。当用户访问uri时,FilterChainProxy执行doFilterInternal()方法,选择合适的SecurityFilterChain,提取其中的List<Filter> filters作为addtionalFilters依次执行。
3.1 UsernamePasswordAuthenticationFilter providerManager.authenticate() 将authentication存入SecurityConext里和session中
3.2 FilterSecurityInterceptor 验证权限,成功则直接访问资源,失败则跳转到ExceptionTranslationFilter,接着跳转到登录界面
至此,FilterChainProxy从创建到调用整个流程我们就讲完了,记住多少就看个人造化了。下一章我们将着重介绍UsernamePasswordAuthenticationFilter是怎么运作的。