1. 程式人生 > >Spring Security 4.2.3 Filters 解析

Spring Security 4.2.3 Filters 解析

其中 validate ali 配置 生命 擁有 path str support

一、 熟悉一個模塊的最快方法

1. 配置logback文件,打印相應的debug信息

2. 根據相應的信息,打斷點查看執行結果

二、spring 使用 DelegatingFilterProxy 管理 filter chain

allow the IoC container to manage the lifecycle instead of the servlet container

org.springframework.web.filter.DelegatingFilterProxy 是 Spring 中定義的一個 Filter 實現類,其作用是代理真正的 Filter 實現類,

也就是說在調用 DelegatingFilterProxy 的 doFilter() 方法時實際上調用的是其代理 Filter 的 doFilter() 方法。使用 DelegatingFilterProxy

的好處就是Filter 類可以使用 Spring 的依賴註入機制方便自由的使用 ApplicationContext 中的 bean。

技術分享圖片

需要註意的是被代理的 Filter 的初始化方法 init() 和銷毀方法 destroy() 默認是不會被執行的。通過設置 DelegatingFilterProxy 的

targetFilterLifecycle 屬性為 true,可以使被代理 Filter 與 DelegatingFilterProxy 具有同樣的生命周期。

三、 FilterChainProxy

DelegatingFilterProxy 代理的就是一個 FilterChainProxy。一個 FilterChainProxy 中可以包含有多個 FilterChain,但是某個請求

只會對應一個 FilterChain,而一個 FilterChain 中又可以包含有多個 Filter。當我們使用 Spring Security 時,系統會自動為我們

註冊一個名為 springSecurityFilterChain, 類型為 FilterChainProxy 的 bean(可查看HttpSecurityBeanDefinitionParser)。

Request Firewalling

An HttpFirewall instance is used to validate incoming requests and create a wrapped request which provides consistent path

values for matching against. See DefaultHttpFirewall, for more information on the type of attacks which the default i

mplementation protects against. A custom implementation can be injected to provide stricter control over the request contents

or if an application needs to support certain types of request which are rejected by default.

Note that this means that you must use the Spring Security filters in combination with a FilterChainProxy if you want this

protection. Don‘t define them explicitly in your web.xml file.

FilterChainProxy will use the firewall instance to obtain both request and response objects which will be fed down the filter chain,

so it is also possible to use this functionality to control the functionality of the response. When the request has passed through the

security filter chain, the reset method will be called. With the default implementation this means that the original values of

servletPath and pathInfowill be returned thereafter, instead of the modified ones used for security pattern matching.

四、 AuthenticationManager 和 AuthenticationProvider

AuthenticationManager 是一個用來處理認證請求的接口。在其中只定義了一個方法 authenticate(),該方法只接收一個代表認證請求的

Authentication對象作為參數,如果認證成功,則會返回一個封裝了當前用戶權限等信息的 Authentication 對象進行返回。

    public Authentication authenticate(Authentication authentication) throws AuthenticationException {
Class
<? extends Authentication> toTest = authentication.getClass(); AuthenticationException lastException = null; Authentication result = null; boolean debug = logger.isDebugEnabled();
     //使用authenticationProvider列表處理認證請求
for (AuthenticationProvider provider : getProviders()) { if (!provider.supports(toTest)) { continue; } if (debug) { logger.debug("Authentication attempt using " + provider.getClass().getName()); } try { result = provider.authenticate(authentication);           //認證成功,跳出循環 if (result != null) { copyDetails(authentication, result); break; } } catch (AccountStatusException e) { prepareException(e, authentication); // SEC-546: Avoid polling additional providers if auth failure is due to // invalid account status throw e; } catch (InternalAuthenticationServiceException e) { prepareException(e, authentication); throw e; } catch (AuthenticationException e) { lastException = e; } }      //沒有結果,重試認證 if (result == null && parent != null) { // Allow the parent to try. try { result = parent.authenticate(authentication); } catch (ProviderNotFoundException e) { // ignore as we will throw below if no other exception occurred prior to // calling parent and the parent // may throw ProviderNotFound even though a provider in the child already // handled the request } catch (AuthenticationException e) { lastException = e; } } //認證成功,發布認證結果 if (result != null) { if (eraseCredentialsAfterAuthentication && (result instanceof CredentialsContainer)) { // Authentication is complete. Remove credentials and other secret data // from authentication ((CredentialsContainer) result).eraseCredentials(); } eventPublisher.publishAuthenticationSuccess(result); return result; } // Parent was null, or didn‘t authenticate (or throw an exception). if (lastException == null) { lastException = new ProviderNotFoundException(messages.getMessage( "ProviderManager.providerNotFound", new Object[] { toTest.getName() }, "No AuthenticationProvider found for {0}")); } prepareException(lastException, authentication); throw lastException; }

1.認證過程

在 Spring Security 中,AuthenticationManager 的默認實現是 ProviderManager,而且它不直接自己處理認證請求,而是委托給其所配

置的 AuthenticationProvider 列表,然後會依次使用每一個 AuthenticationProvider 進行認證,如果有一個 AuthenticationProvider 認

證後的結果不為 null,則表示該 AuthenticationProvider 已經認證成功,之後的 AuthenticationProvider 將不再繼續認證。然後直接以該

AuthenticationProvider 的認證結果作為 ProviderManager 的認證結果。如果所有的 AuthenticationProvider 的認證結果都為 null,則表

示認證失敗,將拋出一個 ProviderNotFoundException。

2. 校驗認證

校驗認證請求最常用的方法是根據請求的用戶名加載對應的 UserDetails,然後比對 UserDetails 的密碼與認證請求的密碼是否一致。

如DaoAuthenticationProvider其內部使用 UserDetailsService 來負責加載 UserDetails。在認證成功以後會使用加載的

UserDetails 來封裝要返回的 Authentication 對象,加載的 UserDetails 對象是包含用戶權限等信息的。認證成功返回的 Authentication

對象將會保存在當前的 SecurityContext 中。

技術分享圖片

4. 在 request 之間共享 SecurityContext

既然 SecurityContext 是存放在 ThreadLocal 中的,而且在每次權限鑒定的時候都是從 ThreadLocal 中獲取 SecurityContext 中對應的

Authentication 所擁有的權限,但不同的 request 是不同的線程,為什麽每次都可以從 ThreadLocal 中獲取到當前用戶對應的 SecurityContext 呢?

每次請求開始的時候從 session 中獲取 SecurityContext,然後把它設置給 SecurityContextHolder

參考:

極客學院:初識Spring Security

Spring Security 4.2.3 API

Spring Security 4.2.3 Filters 解析