springSecurity安全框架的學習和原理解讀
最近在公司的專案中使用了spring security框架,所以有機會來學習一下,公司的專案是使用springboot搭建 springBoot版本1.59
spring security 版本4.2.3
(個人理解可能會有偏差,希望有不正確之處,大家能夠指出來,共同探討交流。)
目錄
4、舉例說明如何將一個Configurer轉換為filter
一、Spring security框架簡介
1、簡介
一個能夠為基於Spring的企業應用系統提供宣告式的安全訪問控制解決方式的安全框架(簡單說是對訪問許可權進行控制嘛),應用的安全性包括使用者認證(Authentication)和使用者授權(Authorization)兩個部分。使用者認證指的是驗證某個使用者是否為系統中的合法主體,也就是說使用者能否訪問該系統。使用者認證一般要求使用者提供使用者名稱和密碼。系統通過校驗使用者名稱和密碼來完成認證過程。使用者授權指的是驗證某個使用者是否有許可權執行某個操作。在一個系統中,不同使用者所具有的許可權是不同的。比如對一個檔案來說,有的使用者只能進行讀取,而有的使用者可以進行修改。一般來說,系統會為不同的使用者分配不同的角色,而每個角色則對應一系列的許可權。 spring security的主要核心功能為 認證和授權,所有的架構也是基於這兩個核心功能去實現的。
2、框架原理
眾所周知 想要對對Web資源進行保護,最好的辦法莫過於Filter,要想對方法呼叫進行保護,最好的辦法莫過於AOP。所以springSecurity在我們進行使用者認證以及授予許可權的時候,通過各種各樣的攔截器來控制權限的訪問,從而實現安全。
如下為其主要過濾器
- WebAsyncManagerIntegrationFilter
- SecurityContextPersistenceFilter
- HeaderWriterFilter
- CorsFilter
- LogoutFilter
- RequestCacheAwareFilter
- SecurityContextHolderAwareRequestFilter
- AnonymousAuthenticationFilter
- SessionManagementFilter
- ExceptionTranslationFilter
- FilterSecurityInterceptor
- UsernamePasswordAuthenticationFilter
- BasicAuthenticationFilter
3、框架的核心元件
- SecurityContextHolder:提供對SecurityContext的訪問
- SecurityContext,:持有Authentication物件和其他可能需要的資訊
- AuthenticationManager 其中可以包含多個AuthenticationProvider
- ProviderManager物件為AuthenticationManager介面的實現類
- AuthenticationProvider 主要用來進行認證操作的類 呼叫其中的authenticate()方法去進行認證操作
- Authentication:Spring Security方式的認證主體
- GrantedAuthority:對認證主題的應用層面的授權,含當前使用者的許可權資訊,通常使用角色表示
- UserDetails:構建Authentication物件必須的資訊,可以自定義,可能需要訪問DB得到
- UserDetailsService:通過username構建UserDetails物件,通過loadUserByUsername根據userName獲取UserDetail物件 (可以在這裡基於自身業務進行自定義的實現 如通過資料庫,xml,快取獲取等)
二、自定義安全配置的載入機制
1、前提 基於自身業務需要
自定義了一個springSecurity安全框架的配置類 繼承WebSecurityConfigurerAdapter,重寫其中的方法configure,但是並不清楚自定義的類是如何被載入並起到作用,這裡一步步通過debug來了解其中的載入原理。
其實在我們實現該類後,在web容器啟動的過程中該類例項物件會被WebSecurityConfiguration類處理。
@Configuration
public class SpringSecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private AccessDeniedHandler accessDeniedHandler;
@Autowired
private CustAuthenticationProvider custAuthenticationProvider;
// roles admin allow to access /admin/**
// roles user allow to access /user/**
// custom 403 access denied handler
//重寫了其中的configure()方法設定了不同url的不同訪問許可權
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable()
.authorizeRequests()
.antMatchers("/home", "/about","/img/*").permitAll()
.antMatchers("/admin/**","/upload/**").hasAnyRole("ADMIN")
.antMatchers("/order/**").hasAnyRole("USER","ADMIN")
.antMatchers("/room/**").hasAnyRole("USER","ADMIN")
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/login")
.permitAll()
.and()
.logout()
.permitAll()
.and()
.exceptionHandling().accessDeniedHandler(accessDeniedHandler);
}
// create two users, admin and user
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
// auth.inMemoryAuthentication()
// .withUser("user").password("user").roles("USER")
// .and()
// .withUser("admin").password("admin").roles("ADMIN");
// auth.jdbcAuthentication()
auth.authenticationProvider(custAuthenticationProvider);
}
2、WebSecurityConfiguration類
@Configuration
public class WebSecurityConfiguration implements ImportAware, BeanClassLoaderAware {
private WebSecurity webSecurity;
private Boolean debugEnabled;
private List<SecurityConfigurer<Filter, WebSecurity>> webSecurityConfigurers;
private ClassLoader beanClassLoader;
...省略部分程式碼
@Bean(
name = {"springSecurityFilterChain"}
)
public Filter springSecurityFilterChain() throws Exception {
boolean hasConfigurers = this.webSecurityConfigurers != null
&& !this.webSecurityConfigurers.isEmpty();
if(!hasConfigurers) {
WebSecurityConfigurerAdapter adapter = (WebSecurityConfigurerAdapter)
this.objectObjectPostProcessor
.postProcess(new WebSecurityConfigurerAdapter() {
});
this.webSecurity.apply(adapter);
}
return (Filter)this.webSecurity.build();
}
/*1、先執行該方法將我們自定義springSecurity配置例項
(可能還有系統預設的有關安全的配置例項 ) 配置例項中含有我們自定義業務的許可權控制配置資訊
放入到該物件的list陣列中webSecurityConfigurers中
使用@Value註解來將例項物件作為形參注入
*/
@Autowired(
required = false
)
public void setFilterChainProxySecurityConfigurer(ObjectPostProcessor<Object>
objectPostProcessor,
@Value("#{@autowiredWebSecurityConfigurersIgnoreParents.getWebSecurityConfigurers()}")
List<SecurityConfigurer<Filter, WebSecurity>> webSecurityConfigurers)
throws Exception {
//建立一個webSecurity物件
this.webSecurity = (WebSecurity)objectPostProcessor.postProcess(new WebSecurity(objectPostProcessor));
if(this.debugEnabled != null) {
this.webSecurity.debug(this.debugEnabled.booleanValue());
}
//對所有配置類的例項進行排序
Collections.sort(webSecurityConfigurers, WebSecurityConfiguration.AnnotationAwareOrderComparator.INSTANCE);
Integer previousOrder = null;
Object previousConfig = null;
//迭代所有配置類的例項 判斷其order必須唯一
Iterator var5;
SecurityConfigurer config;
for(var5 = webSecurityConfigurers.iterator(); var5.hasNext(); previousConfig = config) {
config = (SecurityConfigurer)var5.next();
Integer order = Integer.valueOf(WebSecurityConfiguration.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;
}
//將所有的配置例項新增到建立的webSecutity物件中
var5 = webSecurityConfigurers.iterator();
while(var5.hasNext()) {
config = (SecurityConfigurer)var5.next();
this.webSecurity.apply(config);
}
//將webSercurityConfigures 例項放入該物件的webSecurityConfigurers屬性中
this.webSecurityConfigurers = webSecurityConfigurers;
}
}
2.1、 setFilterChainProxySecurityConfigurer()方法
@Value("#{@autowiredWebSecurityConfigurersIgnoreParents.getWebSecurityConfigurers()}") List<SecurityConfigurer<Filter, WebSecurity>> webSecurityConfigurers
該引數webSecurityConfigurers會將所有的配置例項放入該形參中
該方法中 主要執行如下
1、建立webSecurity物件
2、主要檢驗了配置例項的order順序(order唯一 否則會報錯)
3、將所有的配置例項存放進入到webSecurity物件中,其中配置例項中含有我們自定義業務的許可權控制配置資訊
2.2、springSecurityFilterChain()方法
呼叫springSecurityFilterChain()方法,這個方法會判斷我們上一個方法中有沒有獲取到webSecurityConfigurers,沒有的話這邊會建立一個WebSecurityConfigurerAdapter例項,並追加到websecurity中。接著呼叫websecurity的build方法。實際呼叫的是websecurity的父類AbstractSecurityBuilder的build方法 ,最終返回一個名稱為springSecurityFilterChain的過濾器鏈。裡面有眾多Filter(springSecurity其實就是依靠很多的Filter來攔截url從而實現許可權的控制的安全框架)
3、AbstractSecurityBuilder類
public abstract class AbstractSecurityBuilder<O> implements SecurityBuilder<O> {
private AtomicBoolean building = new AtomicBoolean();
private O object;
//呼叫build方法來返回過濾器鏈,還是呼叫SecurityBuilder的dobuild()方法
public final O build() throws Exception {
if(this.building.compareAndSet(false, true)) {
this.object = this.doBuild();
return this.object;
} else {
throw new AlreadyBuiltException("This object has already been built");
}
}
//...省略部分程式碼
}
3.1 呼叫子類的doBuild()方法
public abstract class AbstractConfiguredSecurityBuilder<O, B extends SecurityBuilder<O>> extends AbstractSecurityBuilder<O> {
private final Log logger;
private final LinkedHashMap<Class<? extends SecurityConfigurer<O, B>>, List<SecurityConfigurer<O, B>>> configurers;
private final List<SecurityConfigurer<O, B>> configurersAddedInInitializing;
private final Map<Class<? extends Object>, Object> sharedObjects;
private final boolean allowConfigurersOfSameType;
private AbstractConfiguredSecurityBuilder.BuildState buildState;
private ObjectPostProcessor<Object> objectPostProcessor;
//doBuild()核心方法 init(),configure(),perFormBuild()
protected final O doBuild() throws Exception {
LinkedHashMap var1 = this.configurers;
synchronized(this.configurers) {
this.buildState = AbstractConfiguredSecurityBuilder.BuildState.INITIALIZING;
this.beforeInit();
this.init();
this.buildState = AbstractConfiguredSecurityBuilder.BuildState.CONFIGURING;
this.beforeConfigure();
this.configure();
this.buildState = AbstractConfiguredSecurityBuilder.BuildState.BUILDING;
O result = this.performBuild();
this.buildState = AbstractConfiguredSecurityBuilder.BuildState.BUILT;
return result;
}
}
protected abstract O performBuild() throws Exception;
//呼叫init方法 呼叫配置類WebSecurityConfigurerAdapter的init()方法
private void init() throws Exception {
Collection<SecurityConfigurer<O, B>> configurers = this.getConfigurers();
Iterator var2 = configurers.iterator();
SecurityConfigurer configurer;
while(var2.hasNext()) {
configurer = (SecurityConfigurer)var2.next();
configurer.init(this);
}
var2 = this.configurersAddedInInitializing.iterator();
while(var2.hasNext()) {
configurer = (SecurityConfigurer)var2.next();
configurer.init(this);
}
}
private void configure() throws Exception {
Collection<SecurityConfigurer<O, B>> configurers = this.getConfigurers();
Iterator var2 = configurers.iterator();
while(var2.hasNext()) {
SecurityConfigurer<O, B> configurer = (SecurityConfigurer)var2.next();
configurer.configure(this);
}
}
private Collection<SecurityConfigurer<O, B>> getConfigurers() {
List<SecurityConfigurer<O, B>> result = new ArrayList();
Iterator var2 = this.configurers.values().iterator();
while(var2.hasNext()) {
List<SecurityConfigurer<O, B>> configs = (List)var2.next();
result.addAll(configs);
}
return result;
}
//...省略部分程式碼
}
3.2 先呼叫本類的init()方法
build過程主要分三步,init->configure->peformBuild
- 1 init方法做了兩件事,一個就是呼叫getHttp()方法獲取一個http例項,並通過web.addSecurityFilterChainBuilder方法把獲取到的例項賦值給WebSecurity的securityFilterChainBuilders屬性,這個屬性在我們執行build的時候會用到,第二個就是為WebSecurity追加了一個postBuildAction,在build都完成後從http中拿出FilterSecurityInterceptor物件並賦值給WebSecurity。
- 2 getHttp()方法,這個方法在當我們使用預設配置時(大多數情況下)會為我們追加各種SecurityConfigurer的具體實現類到httpSecurity中,如exceptionHandling()方法會追加一個ExceptionHandlingConfigurer,sessionManagement()方法會追加一個SessionManagementConfigurer,securityContext()方法會追加一個SecurityContextConfigurer物件,這些SecurityConfigurer的具體實現類最終會為我們配置各種具體的filter。
- 3 另外getHttp()方法的最後會呼叫configure(http),這個方法也是我們繼承WebSecurityConfigurerAdapter類後最可能會重寫的方法 。
- 4 configure(HttpSecurity http)方法,預設的configure(HttpSecurity http)方法繼續向httpSecurity類中追加SecurityConfigurer的具體實現類,如authorizeRequests()方法追加一個ExpressionUrlAuthorizationConfigurer,formLogin()方法追加一個FormLoginConfigurer。 其中ExpressionUrlAuthorizationConfigurer這個實現類比較重要,因為他會給我們建立一個非常重要的物件FilterSecurityInterceptor物件,FormLoginConfigurer物件比較簡單,但是也會為我們提供一個在安全認證過程中經常用到會用的一個Filter:UsernamePasswordAuthenticationFilter。
以上三個方法就是WebSecurityConfigurerAdapter類中init方法的主要邏輯,
public abstract class WebSecurityConfigurerAdapter implements
WebSecurityConfigurer<WebSecurity> {
public void init(final WebSecurity web) throws Exception {
final HttpSecurity http = this.getHttp();
web.addSecurityFilterChainBuilder(http).postBuildAction(new Runnable() {
public void run() {
FilterSecurityInterceptor securityInterceptor = (FilterSecurityInterceptor)http.getSharedObject(FilterSecurityInterceptor.class);
web.securityInterceptor(securityInterceptor);
}
});
}
protected final HttpSecurity getHttp() throws Exception {
if(this.http != null) {
return this.http;
} else {
DefaultAuthenticationEventPublisher eventPublisher = (DefaultAuthenticationEventPublisher)this.objectPostProcessor.postProcess(new DefaultAuthenticationEventPublisher());
//新增認證的事件的釋出者
this.localConfigureAuthenticationBldr.authenticationEventPublisher(eventPublisher);
//獲取AuthenticationManager物件其中一至多個進行認證處理的物件例項,後面會進行講解
AuthenticationManager authenticationManager = this.authenticationManager();
this.authenticationBuilder.parentAuthenticationManager(authenticationManager);
Map<Class<? extends Object>, Object> sharedObjects = this.createSharedObjects();
this.http = new HttpSecurity(this.objectPostProcessor, this.authenticationBuilder, sharedObjects);
if(!this.disableDefaults) {
((HttpSecurity)((DefaultLoginPageConfigurer)((HttpSecurity)((HttpSecurity)((HttpSecurity)((HttpSecurity)((HttpSecurity)((HttpSecurity)((HttpSecurity)((HttpSecurity)this.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();
ClassLoader classLoader = this.context.getClassLoader();
List<AbstractHttpConfigurer> defaultHttpConfigurers = SpringFactoriesLoader.loadFactories(AbstractHttpConfigurer.class, classLoader);
Iterator var6 = defaultHttpConfigurers.iterator();
while(var6.hasNext()) {
AbstractHttpConfigurer configurer = (AbstractHttpConfigurer)var6.next();
this.http.apply(configurer);
}
}
//最終呼叫我們的繼承的WebSecurityConfigurerAdapter中重寫的configure()
//將我們業務相關的許可權配置規則資訊進行初始化操作
this.configure(this.http);
return this.http;
}
}
protected AuthenticationManager authenticationManager() throws Exception {
if(!this.authenticationManagerInitialized) {
this.configure(this.localConfigureAuthenticationBldr);
if(this.disableLocalConfigureAuthenticationBldr) {
this.authenticationManager = this.authenticationConfiguration.getAuthenticationManager();
} else {
this.authenticationManager = (AuthenticationManager)this.localConfigureAuthenticationBldr.build();
}
this.authenticationManagerInitialized = true;
}
return this.authenticationManager;
}
}
3.3、第二步configure
- configure方法最終也呼叫到了WebSecurityConfigurerAdapter的configure(WebSecurity web)方法,預設實現中這個是一個空方法,具體應用中也經常重寫這個方法來實現特定需求。
3.4、第三步 peformBuild
- 具體的實現邏輯在WebSecurity類中
- 這個方法中最主要的任務就是遍歷securityFilterChainBuilders屬性中的SecurityBuilder物件,並呼叫他的build方法。
這個securityFilterChainBuilders屬性我們前面也有提到過,就是在WebSecurityConfigurerAdapter類的init方法中獲取http後賦值給了WebSecurity。因此這個地方就是呼叫httpSecurity的build方法。 - httpSecurity的build方式向其中追加一個個過濾器
public final class WebSecurity extends AbstractConfiguredSecurityBuilder<Filter, WebSecurity> implements SecurityBuilder<Filter>, ApplicationContextAware {
...省略部分程式碼
//呼叫該方法通過securityFilterChainBuilder.build()方法來建立securityFilter過濾器
//並新增到securityFilterChains物件中,包裝成FilterChainProxy 返回
protected Filter performBuild() throws Exception {
Assert.state(!this.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 = this.ignoredRequests.size() + this.securityFilterChainBuilders.size();
List<SecurityFilterChain> securityFilterChains = new ArrayList(chainSize);
Iterator var3 = this.ignoredRequests.iterator();
while(var3.hasNext()) {
RequestMatcher ignoredRequest = (RequestMatcher)var3.next();
securityFilterChains.add(new DefaultSecurityFilterChain(ignoredRequest, new Filter[0]));
}
var3 = this.securityFilterChainBuilders.iterator();
while(var3.hasNext()) {
SecurityBuilder<? extends SecurityFilterChain> securityFilterChainBuilder = (SecurityBuilder)var3.next();
securityFilterChains.add(securityFilterChainBuilder.build());
}
FilterChainProxy filterChainProxy = new FilterChainProxy(securityFilterChains);
if(this.httpFirewall != null) {
filterChainProxy.setFirewall(this.httpFirewall);
}
filterChainProxy.afterPropertiesSet();
Filter result = filterChainProxy;
if(this.debugEnabled) {
this.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);
}
this.postBuildAction.run();
return (Filter)result;
}
}
4、舉例說明如何將一個Configurer轉換為filter
ExpressionUrlAuthorizationConfigurer的繼承關係
ExpressionUrlAuthorizationConfigurer->AbstractInterceptUrlConfigurer->AbstractHttpConfigurer->SecurityConfigurerAdapter->SecurityConfigurer
對應的init方法在SecurityConfigurerAdapter類中,是個空實現,什麼也沒有做,configure方法在SecurityConfigurerAdapter類中也有一個空實現,在AbstractInterceptUrlConfigurer類中進行了重寫
Abstractintercepturlconfigurer.java程式碼
@Override
public void configure(H http) throws Exception {
FilterInvocationSecurityMetadataSource metadataSource = createMetadataSource(http);
if (metadataSource == null) {
return;
}
FilterSecurityInterceptor securityInterceptor = createFilterSecurityInterceptor(
http, metadataSource, http.getSharedObject(AuthenticationManager.class));
if (filterSecurityInterceptorOncePerRequest != null) {
securityInterceptor
.setObserveOncePerRequest(filterSecurityInterceptorOncePerRequest);
}
securityInterceptor = postProcess(securityInterceptor);
http.addFilter(securityInterceptor);
http.setSharedObject(FilterSecurityInterceptor.class, securityInterceptor);
}
...
private AccessDecisionManager createDefaultAccessDecisionManager(H http) {
AffirmativeBased result = new AffirmativeBased(getDecisionVoters(http));
return postProcess(result);
}
...
private FilterSecurityInterceptor createFilterSecurityInterceptor(H http,
FilterInvocationSecurityMetadataSource metadataSource,
AuthenticationManager authenticationManager) throws Exception {
FilterSecurityInterceptor securityInterceptor = new FilterSecurityInterceptor();
securityInterceptor.setSecurityMetadataSource(metadataSource);
securityInterceptor.setAccessDecisionManager(getAccessDecisionManager(http));
securityInterceptor.setAuthenticationManager(authenticationManager);
securityInterceptor.afterPropertiesSet();
return securityInterceptor;
}
4.1、 在這個類的configure中建立了一個FilterSecurityInterceptor,並且也可以明確看到spring security預設給我們建立的AccessDecisionManager是AffirmativeBased。
4.2、.最後再看下HttpSecurity類執行build的最後一步 performBuild,這個方法就是在HttpSecurity中實現的
Httpsecurity.java程式碼
@Override
protected DefaultSecurityFilterChain performBuild() throws Exception {
Collections.sort(filters, comparator);
return new DefaultSecurityFilterChain(requestMatcher, filters);
}
可以看到,這個類只是把我們追加到HttpSecurity中的security進行了排序,用的排序類是FilterComparator,從而保證我們的filter按照正確的順序執行。接著將filters構建成filterChian返回。在前面WebSecurity的performBuild方法中,這個返回值會被包裝成FilterChainProxy,並作為WebSecurity的build方法的放回值。從而以springSecurityFilterChain這個名稱註冊到springContext中(在WebSecurityConfiguration中做的)
4.3.在WebSecurity的performBuild方法的最後一步還執行了一個postBuildAction.run,這個方法也是spring security給我們提供的一個hooks,可以在build完成後再做一些事情,比如我們在WebSecurityConfigurerAdapter類的init方法中我們利用這個hook在構建完成後將FilterSecurityInterceptor賦值給了webSecurity類的filterSecurityInterceptor屬性
三、使用者登入的驗證和授權過程
1、使用者一次完整的登入驗證和授權,是一個請求經過 層層攔截器從而實現許可權控制,整個web端配置為DelegatingFilterProxy(springSecurity的委託過濾其代理類 ),它並不實現真正的過濾,而是所有過濾器鏈的代理類,真正執行攔截處理的是由spring 容器管理的個個filter bean組成的filterChain.
呼叫實際的FilterChainProxy 的doFilterInternal()方法 去獲取所有的攔截器並進行過濾處理如下是DelegatingFilterProxy的doFilter()方法
public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) throws ServletException, IOException {
Filter delegateToUse = this.delegate;
if(delegateToUse == null) {
Object var5 = this.delegateMonitor;
synchronized(this.delegateMonitor) {
delegateToUse = this.delegate;
if(delegateToUse == null) {
WebApplicationContext wac = this.findWebApplicationContext();
if(wac == null) {
throw new IllegalStateException("No WebApplicationContext found: no ContextLoaderListener or DispatcherServlet registered?");
}
delegateToUse = this.initDelegate(wac);
}
this.delegate = delegateToUse;
}
}
//呼叫實際的FilterChainProxy 的doFilterInternal()方法 去獲取所有的攔截器並進行過濾處理
this.invokeDelegate(delegateToUse, request, response, filterChain);
}
呼叫實際的FilterChainProxy 的doFilter()方法 去獲取所有的攔截器並進行過濾處理。
2、FilterChainProxy類
最終呼叫FilterChainProxy 的doFilterInternal()方法,獲取所有的過濾器例項
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);
//doFilter 呼叫doFilterInternal方法
this.doFilterInternal(request, response, chain);
} finally {
SecurityContextHolder.clearContext();
request.removeAttribute(FILTER_APPLIED);
}
} else {
this.doFilterInternal(request, response, chain);
}
}
private void doFilterInternal(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
FirewalledRequest fwRequest = this.firewall.getFirewalledRequest((HttpServletRequest)request);
HttpServletResponse fwResponse = this.firewall.getFirewalledResponse((HttpServletResponse)response);
//過去所有的過濾器
List<Filter> filters = this.getFilters((HttpServletRequest)fwRequest);
if(filters != null && filters.size() != 0) {
FilterChainProxy.VirtualFilterChain vfc = new FilterChainProxy.VirtualFilterChain(fwRequest, chain, filters);
vfc.doFilter(fwRequest, fwResponse);
} else {
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);
}
}
private List<Filter> getFilters(HttpServletRequest request) {
//遍歷所有的matcher類 如果支援就繼續獲取
Iterator var2 = this.filterChains.iterator();
SecurityFilterChain chain;
do {
if(!var2.hasNext()) {
return null;
}
chain = (SecurityFilterChain)var2.next();
} while(!chain.matches(request));
//後去匹配中的所有過濾器
return chain.getFilters();
}
如上 其實是獲取到本次請求的所有filter 並安裝指定順序進行執行doFilter()方法
這是筆者本次業務請求所要執行的所有過濾器
- WebAsyncManagerIntegrationFilter
- SecurityContextPersistenceFilter
- HeaderWriterFilter
- LogoutFilter
- UsernamePasswordAuthenticationFilter
- RequestCacheAwareFilter
- SecurityContextHolderAwareRequestFilter
- AnonymousAuthenticationFilter
- SessionManagementFilter
- ExceptionTranslationFilter
- FilterSecurityInterceptor
關於springSecutity攔截器的介紹請參考如下連結地址
https://blog.csdn.net/dushiwodecuo/article/details/78913113
http://blog.didispace.com/xjf-spring-security-4/
https://www.cnblogs.com/HHR-SUN/p/7095720.html
https://blog.csdn.net/zheng963/article/details/50427320
https://blog.csdn.net/m0_37834471/article/details/81142246
https://www.cnblogs.com/mingluosunshan/p/5485259.html