话不多说,直接进入主题,关于spring security oauth认证原理我已经在上一篇博客Spring security oauth2认证流程分析分析过源码,里面有一个名称为springSecurityFilterChain的过滤器链,也正是这个过滤器链基本承载了spring security的核心功能,下面我们就来分析一下这个过滤器链的创建过程和工作原理。
1.配置的读取
WebSecurityConfiguration
首先,咱们先看这个方法,明眼人一看到@Bean就明白了
/**
* 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();
}
重点是webSecurity.build(),但是大家可能会有疑问,不知道webSecurity啥时候创建的,也不知道webSecurity里面有些啥?
再往下看:
@Autowired(required = false)
public void setFilterChainProxySecurityConfigurer(
ObjectPostProcessor<Object> objectPostProcessor,
@Value("#{@autowiredWebSecurityConfigurersIgnoreParents.getWebSecurityConfigurers()}") List<SecurityConfigurer<Filter, WebSecurity>> webSecurityConfigurers)
throws Exception {
//创建一个WebSecurity对象,这里就是创建来webSecurity
webSecurity = objectPostProcessor
.postProcess(new WebSecurity(objectPostProcessor));
// 排序可以忽略,不是重点
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) {
// 把Filter相关配置添加进去
webSecurity.apply(webSecurityConfigurer);
}
this.webSecurityConfigurers = webSecurityConfigurers;
}
下面继续分析webSecurityConfigurers配置到底是从哪儿来的,我们发现了
@Value("#{@autowiredWebSecurityConfigurersIgnoreParents.getWebSecurityConfigurers()}") List<SecurityConfigurer<Filter, WebSecurity>> webSecurityConfigurers
这样一段代码,咱平时也没见过这样的代码,应该也是spring的相关功能,连猜带蒙,就在idea中搜索一下AutowiredWebSecurityConfigurersIgnoreParents这个类,还真的有这么一个类:
/**
* A class used to get all the {@link WebSecurityConfigurer} instances from the current
* {@link ApplicationContext} but ignoring the parent.
*
* @author Rob Winch
*
*/
final class AutowiredWebSecurityConfigurersIgnoreParents {
private final ConfigurableListableBeanFactory beanFactory;
public AutowiredWebSecurityConfigurersIgnoreParents(
ConfigurableListableBeanFactory beanFactory) {
Assert.notNull(beanFactory, "beanFactory cannot be null");
this.beanFactory = beanFactory;
}
@SuppressWarnings({ "rawtypes", "unchecked" })
public List<SecurityConfigurer<Filter, WebSecurity>> getWebSecurityConfigurers() {
List<SecurityConfigurer<Filter, WebSecurity>> webSecurityConfigurers = new ArrayList<SecurityConfigurer<Filter, WebSecurity>>();
// 查询容器中所有WebSecurityConfigurer的实例
Map<String, WebSecurityConfigurer> beansOfType = beanFactory
.getBeansOfType(WebSecurityConfigurer.class);
for (Entry<String, WebSecurityConfigurer> entry : beansOfType.entrySet()) {
// 将所有的WebSecurityConfigurer实例放进列表
webSecurityConfigurers.add(entry.getValue());
}
return webSecurityConfigurers;
}
}
并且也找到了getWebSecurityConfigurers方法,类上面的注释说这个类是用来获取所有spring中WebSecurityConfigurer子类实例的,根据代码也能映证注释的意思。
咱们再回到WebSecurityConfiguration类,正好可以找到
@Bean
public static AutowiredWebSecurityConfigurersIgnoreParents autowiredWebSecurityConfigurersIgnoreParents(
ConfigurableListableBeanFactory beanFactory) {
return new AutowiredWebSecurityConfigurersIgnoreParents(beanFactory);
}
也就是说AutowiredWebSecurityConfigurersIgnoreParents已经被容器管理起来了。
这样的话,咱们之前的逻辑也就衔接上了,无论是资源服务,还是认证服务,亦或是继承了WebSecurityConfigurerAdapter类,还是实现了WebSecurityConfigurer,都会被AutowiredWebSecurityConfigurersIgnoreParents读进列表,并添加到一个WebSecurity对象里面去。
2.配置生成Filter
咱们回到webSecurity.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");
}
通过idea工具进入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;
}
}
上面就是典型的模版模式了,记住当前对象是WebSecurity,在调用init()方法时,会循环去调用所有WebSecurityConfigurer对象的init()方法
private void init() throws Exception {
Collection<SecurityConfigurer<O, B>> configurers = getConfigurers();
for (SecurityConfigurer<O, B> configurer : configurers) {
//循环调用WebSecurityConfigurerAdapter的init()方法
configurer.init((B) this);
}
for (SecurityConfigurer<O, B> configurer : configurersAddedInInitializing) {
configurer.init((B) this);
}
}
以上init()方法调用完毕后,也就是配置信息初始化后,WebSecurity里面的securityFilterChainBuilders属性就会生成多个HttpSecurity对象,每一个HttpSecurity都是和之前的WebSecurityConfigurer对象一一对应。
HttpSecurity中会有一个configurers属性,里面全部都是各种Filter的配置对象,都是AbstractHttpConfigurer的子类。例如:CsrfConfigurer就是CsrfFilter的配置类。
在后续的performBuild()方法中:
@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");
// 其实就是获取HttpSecurity的数量,因为securityFilterChainBuilders数量和HttpSecurity一致,ignoredRequests一般情况为0.
int chainSize = ignoredRequests.size() + securityFilterChainBuilders.size();
List<SecurityFilterChain> securityFilterChains = new ArrayList<>(
chainSize);
for (RequestMatcher ignoredRequest : ignoredRequests) {
securityFilterChains.add(new DefaultSecurityFilterChain(ignoredRequest));
}
// 重点在securityFilterChainBuilder.build(),其实就是HttpSecurity.build()
// 最终发现又回到了之前的模版方法,不同的是之前是WebSecurity,现在是HttpSecurity
// 在调用HttpSecurity的configure()方法的时候,其实是在循环调用configurers属性里面每一个AbstractHttpConfigurer的configure()方法
// 通过循环调用AbstractHttpConfigurer的configure()方法,给HttpSecurity创建了一系列的Filter,存放在属性filters中
// 最后通过HttpSecurity的performBuild()创建了一个DefaultSecurityFilterChain对象返回并存放在securityFilterChains中
for (SecurityBuilder<? extends SecurityFilterChain> securityFilterChainBuilder : securityFilterChainBuilders) {
securityFilterChains.add(securityFilterChainBuilder.build());
}
// 通过securityFilterChains创建最终的FilterChainProxy,并返回,存放在spring容器中,最后由spring容器设置到servlet过滤器链中
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;
}
通过以上过程,我们简单地跟踪了FilterChainProxy(也就是springSecurityFilterChain)的创建过程。
3.FilterChainProxy如何工作
作为一个Filter,第一反应就是看doFilter()方法:
@Override
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(request, response, chain);
}
finally {
SecurityContextHolder.clearContext();
request.removeAttribute(FILTER_APPLIED);
}
}
else {
doFilterInternal(request, response, chain);
}
}
好像也没做啥,重点在doFilterInternal()方法:
private void doFilterInternal(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
FirewalledRequest fwRequest = firewall
.getFirewalledRequest((HttpServletRequest) request);
HttpServletResponse fwResponse = firewall
.getFirewalledResponse((HttpServletResponse) response);
//根据请求匹配对应的Filter列表
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;
}
VirtualFilterChain vfc = new VirtualFilterChain(fwRequest, chain, filters);
vfc.doFilter(fwRequest, fwResponse);
}
重点在getFilters(),还记得HttpSecurity创建DefaultSecurityFilterChain的过程嘛?
@Override
protected DefaultSecurityFilterChain performBuild() throws Exception {
Collections.sort(filters, comparator);
return new DefaultSecurityFilterChain(requestMatcher, filters);
}
注意requestMatcher这个属性,它被放进DefaultSecurityFilterChain对象中,也就是说,requestMatcher同样存在FilterChainProxy对象中,getFilters()的原理就是通过判断当前Request是否和requestMatcher匹配来决定是否要被spring security的过滤器链拦截
private List<Filter> getFilters(HttpServletRequest request) {
//注意这个filterChains,其实就是前面securityFilterChainBuilder.build()创建出来的
for (SecurityFilterChain chain : filterChains) {
if (chain.matches(request)) {
return chain.getFilters();
}
}
return null;
}
当确定当前请求需要被spring security拦截,那么就进入VirtualFilterChain.doFilter()方法,也就是上一篇博客Spring security oauth2认证流程分析重点分析的spring security各Filter的作用了(ps:只分析了部分Filter的作用)。所有Filter请参考FilterComparator这个类。
至此,关于spring security的Filter创建过程和工作原理基本分析完毕,小伙伴可以按照博客思路自己重新捋一遍,印象会更加深刻。
贴上关于整个思路的脑图: