上一章介紹了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是怎麼運作的。