話不多說(shuō),直接進(jìn)入主題,關(guān)于spring security oauth認(rèn)證原理我已經(jīng)在上一篇博客Spring security oauth2認(rèn)證流程分析分析過(guò)源碼闸氮,里面有一個(gè)名稱為springSecurityFilterChain的過(guò)濾器鏈,也正是這個(gè)過(guò)濾器鏈基本承載了spring security的核心功能,下面我們就來(lái)分析一下這個(gè)過(guò)濾器鏈的創(chuàng)建過(guò)程和工作原理嵌纲。
1.配置的讀取
WebSecurityConfiguration
首先,咱們先看這個(gè)方法腥沽,明眼人一看到@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);
}
// 前面不用關(guān)心逮走,只關(guān)心這段代碼
return webSecurity.build();
}
重點(diǎn)是webSecurity.build(),但是大家可能會(huì)有疑問(wèn)今阳,不知道webSecurity啥時(shí)候創(chuàng)建的师溅,也不知道webSecurity里面有些啥?
再往下看:
@Autowired(required = false)
public void setFilterChainProxySecurityConfigurer(
ObjectPostProcessor<Object> objectPostProcessor,
@Value("#{@autowiredWebSecurityConfigurersIgnoreParents.getWebSecurityConfigurers()}") List<SecurityConfigurer<Filter, WebSecurity>> webSecurityConfigurers)
throws Exception {
//創(chuàng)建一個(gè)WebSecurity對(duì)象,這里就是創(chuàng)建來(lái)webSecurity
webSecurity = objectPostProcessor
.postProcess(new WebSecurity(objectPostProcessor));
// 排序可以忽略盾舌,不是重點(diǎn)
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;
}
// 上面都是排序墓臭,不用管,下面才是重點(diǎn)
for (SecurityConfigurer<Filter, WebSecurity> webSecurityConfigurer : webSecurityConfigurers) {
// 把Filter相關(guān)配置添加進(jìn)去
webSecurity.apply(webSecurityConfigurer);
}
this.webSecurityConfigurers = webSecurityConfigurers;
}
下面繼續(xù)分析webSecurityConfigurers配置到底是從哪兒來(lái)的矿筝,我們發(fā)現(xiàn)了
@Value("#{@autowiredWebSecurityConfigurersIgnoreParents.getWebSecurityConfigurers()}") List<SecurityConfigurer<Filter, WebSecurity>> webSecurityConfigurers
這樣一段代碼起便,咱平時(shí)也沒(méi)見(jiàn)過(guò)這樣的代碼,應(yīng)該也是spring的相關(guān)功能,連猜帶蒙榆综,就在idea中搜索一下AutowiredWebSecurityConfigurersIgnoreParents這個(gè)類妙痹,還真的有這么一個(gè)類:
/**
* 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>>();
// 查詢?nèi)萜髦兴蠾ebSecurityConfigurer的實(shí)例
Map<String, WebSecurityConfigurer> beansOfType = beanFactory
.getBeansOfType(WebSecurityConfigurer.class);
for (Entry<String, WebSecurityConfigurer> entry : beansOfType.entrySet()) {
// 將所有的WebSecurityConfigurer實(shí)例放進(jìn)列表
webSecurityConfigurers.add(entry.getValue());
}
return webSecurityConfigurers;
}
}
并且也找到了getWebSecurityConfigurers方法,類上面的注釋說(shuō)這個(gè)類是用來(lái)獲取所有spring中WebSecurityConfigurer子類實(shí)例的鼻疮,根據(jù)代碼也能映證注釋的意思怯伊。
咱們?cè)倩氐絎ebSecurityConfiguration類,正好可以找到
@Bean
public static AutowiredWebSecurityConfigurersIgnoreParents autowiredWebSecurityConfigurersIgnoreParents(
ConfigurableListableBeanFactory beanFactory) {
return new AutowiredWebSecurityConfigurersIgnoreParents(beanFactory);
}
也就是說(shuō)AutowiredWebSecurityConfigurersIgnoreParents已經(jīng)被容器管理起來(lái)了判沟。
這樣的話耿芹,咱們之前的邏輯也就銜接上了,無(wú)論是資源服務(wù)挪哄,還是認(rèn)證服務(wù)吧秕,亦或是繼承了WebSecurityConfigurerAdapter類,還是實(shí)現(xiàn)了WebSecurityConfigurer迹炼,都會(huì)被AutowiredWebSecurityConfigurersIgnoreParents讀進(jìn)列表砸彬,并添加到一個(gè)WebSecurity對(duì)象里面去。
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");
}
通過(guò)idea工具進(jìn)入doBuild()方法斯入,進(jìn)入到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;
}
}
上面就是典型的模版模式了砂碉,記住當(dāng)前對(duì)象是WebSecurity,在調(diào)用init()方法時(shí)刻两,會(huì)循環(huán)去調(diào)用所有WebSecurityConfigurer對(duì)象的init()方法
private void init() throws Exception {
Collection<SecurityConfigurer<O, B>> configurers = getConfigurers();
for (SecurityConfigurer<O, B> configurer : configurers) {
//循環(huán)調(diào)用WebSecurityConfigurerAdapter的init()方法
configurer.init((B) this);
}
for (SecurityConfigurer<O, B> configurer : configurersAddedInInitializing) {
configurer.init((B) this);
}
}
以上init()方法調(diào)用完畢后增蹭,也就是配置信息初始化后,WebSecurity里面的securityFilterChainBuilders屬性就會(huì)生成多個(gè)HttpSecurity對(duì)象磅摹,每一個(gè)HttpSecurity都是和之前的WebSecurityConfigurer對(duì)象一一對(duì)應(yīng)滋迈。
HttpSecurity中會(huì)有一個(gè)configurers屬性,里面全部都是各種Filter的配置對(duì)象偏瓤,都是AbstractHttpConfigurer的子類杀怠。例如:CsrfConfigurer就是CsrfFilter的配置類。
在后續(xù)的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");
// 其實(shí)就是獲取HttpSecurity的數(shù)量厅克,因?yàn)閟ecurityFilterChainBuilders數(shù)量和HttpSecurity一致赂弓,ignoredRequests一般情況為0.
int chainSize = ignoredRequests.size() + securityFilterChainBuilders.size();
List<SecurityFilterChain> securityFilterChains = new ArrayList<>(
chainSize);
for (RequestMatcher ignoredRequest : ignoredRequests) {
securityFilterChains.add(new DefaultSecurityFilterChain(ignoredRequest));
}
// 重點(diǎn)在securityFilterChainBuilder.build()刻伊,其實(shí)就是HttpSecurity.build()
// 最終發(fā)現(xiàn)又回到了之前的模版方法盒至,不同的是之前是WebSecurity缺谴,現(xiàn)在是HttpSecurity
// 在調(diào)用HttpSecurity的configure()方法的時(shí)候,其實(shí)是在循環(huán)調(diào)用configurers屬性里面每一個(gè)AbstractHttpConfigurer的configure()方法
// 通過(guò)循環(huán)調(diào)用AbstractHttpConfigurer的configure()方法女责,給HttpSecurity創(chuàng)建了一系列的Filter漆枚,存放在屬性filters中
// 最后通過(guò)HttpSecurity的performBuild()創(chuàng)建了一個(gè)DefaultSecurityFilterChain對(duì)象返回并存放在securityFilterChains中
for (SecurityBuilder<? extends SecurityFilterChain> securityFilterChainBuilder : securityFilterChainBuilders) {
securityFilterChains.add(securityFilterChainBuilder.build());
}
// 通過(guò)securityFilterChains創(chuàng)建最終的FilterChainProxy,并返回抵知,存放在spring容器中墙基,最后由spring容器設(shè)置到servlet過(guò)濾器鏈中
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;
}
通過(guò)以上過(guò)程软族,我們簡(jiǎn)單地跟蹤了FilterChainProxy(也就是springSecurityFilterChain)的創(chuàng)建過(guò)程。
3.FilterChainProxy如何工作
作為一個(gè)Filter残制,第一反應(yīng)就是看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);
}
}
好像也沒(méi)做啥立砸,重點(diǎn)在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);
//根據(jù)請(qǐng)求匹配對(duì)應(yīng)的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);
}
重點(diǎn)在getFilters(),還記得HttpSecurity創(chuàng)建DefaultSecurityFilterChain的過(guò)程嘛?
@Override
protected DefaultSecurityFilterChain performBuild() throws Exception {
Collections.sort(filters, comparator);
return new DefaultSecurityFilterChain(requestMatcher, filters);
}
注意requestMatcher這個(gè)屬性初茶,它被放進(jìn)DefaultSecurityFilterChain對(duì)象中颗祝,也就是說(shuō),requestMatcher同樣存在FilterChainProxy對(duì)象中恼布,getFilters()的原理就是通過(guò)判斷當(dāng)前Request是否和requestMatcher匹配來(lái)決定是否要被spring security的過(guò)濾器鏈攔截
private List<Filter> getFilters(HttpServletRequest request) {
//注意這個(gè)filterChains螺戳,其實(shí)就是前面securityFilterChainBuilder.build()創(chuàng)建出來(lái)的
for (SecurityFilterChain chain : filterChains) {
if (chain.matches(request)) {
return chain.getFilters();
}
}
return null;
}
當(dāng)確定當(dāng)前請(qǐng)求需要被spring security攔截,那么就進(jìn)入VirtualFilterChain.doFilter()方法折汞,也就是上一篇博客Spring security oauth2認(rèn)證流程分析重點(diǎn)分析的spring security各Filter的作用了(ps:只分析了部分Filter的作用)倔幼。所有Filter請(qǐng)參考FilterComparator這個(gè)類。
至此字支,關(guān)于spring security的Filter創(chuàng)建過(guò)程和工作原理基本分析完畢凤藏,小伙伴可以按照博客思路自己重新捋一遍,印象會(huì)更加深刻堕伪。
貼上關(guān)于整個(gè)思路的腦圖: