Spring Security 原理分析

引言

在試題系統開發過程當中,認證方式愈來愈完善,也對Spring Security有了更加深入的理解。前端

本文,咱們一塊兒來領略Spring Security的設計原理。java

原理

必備基礎

ServletJava Web領域中的軟件開發規範,Tomcat是實現Servlet規範的Java Web服務器。安全

package javax.servlet;

public interface Servlet {

    public void init(ServletConfig config) throws ServletException;

    public ServletConfig getServletConfig();

    public void service(ServletRequest req, ServletResponse res)
            throws ServletException, IOException;

    public String getServletInfo();

    public void destroy();
}

Servlet長這樣,生命週期十分簡單,初始化init、業務邏輯service、銷燬destroy服務器

簡單來講:咱們根據Servlet接口開發咱們的應用,服務器開發商根據Servlet接口開發Servlet服務器。架構

image.png

前端HTTP請求,Tomcat選擇路由匹配的Servlet,若是已經實例化,直接調用service;若是未實例化,實例化後調用service,處理完以後返回響應。併發

圖中示例的url統一採用小寫,關於url大小寫的問題,如下是結論,雖然有些url是大小寫不敏感的,可是這使得表示惟一性變得困難,咱們一般應該認爲大小寫是敏感的。框架

There may be URLs, or parts of URLs, where case doesn't matter, but identifying these may not be easy. Users should always consider that URLs are case-sensitive.

image.png

爲了在Servlet先後執行其餘邏輯,定義了Filter接口,在請求先後都會執行。ide

原理分析

Spring Security官方文檔摘抄的一張圖:高併發

image.png

Spring Security的原理其實就是經過Servlet中的Filter技術進行實現的,經過一系列內置的或自定義的安全Filter,實現接口認證與受權。性能

image.png

Spring Security官方也給出了默認Filter的執行順序,先執行認證的過濾器,後執行受權的過濾器,其中就有咱們經常使用的UsernamePasswordAuthenticationFilterBasicAuthenticationFilter等等。

閱讀源碼一個Filter源碼便可理解整個認證架構設計。

注:如下代碼中部分無關代碼已被刪減。

public class BasicAuthenticationFilter extends OncePerRequestFilter {

     @Override
     protected void doFilterInternal(HttpServletRequest request,
               HttpServletResponse response, FilterChain chain) {
          try {
               /** 從請求中獲取用戶名密碼信息 */
               UsernamePasswordAuthenticationToken authRequest = authenticationConverter.convert(request);
               /** 若是沒有相關信息,說明不是此種認證方式,執行後續過濾器 */
               if (authRequest == null) {
                    chain.doFilter(request, response);
                    return;
               }
               /** 獲取用戶名 */
               String username = authRequest.getName();
               /** 判斷該用戶是否須要認證 */
               if (authenticationIsRequired(username)) {
                    /** 嘗試使用 token 進行認證 */
                    Authentication authResult = this.authenticationManager
                              .authenticate(authRequest);
                    /** 認證成功,將認證結果置入上下文 */
                    SecurityContextHolder.getContext().setAuthentication(authResult);
                    /** 認證成功相關回調 */
                    this.rememberMeServices.loginSuccess(request, response, authResult);
                    onSuccessfulAuthentication(request, response, authResult);
               }
          }
          catch (AuthenticationException failed) {
               /** 認證失敗,清空當前上下文信息 */
               SecurityContextHolder.clearContext();
               /** 認證失敗相關回調 */
               this.rememberMeServices.loginFail(request, response);
               onUnsuccessfulAuthentication(request, response, failed);
               /** 若是須要忽略失敗,則繼續執行後續過濾器 */
               if (this.ignoreFailure) {
                    chain.doFilter(request, response);
               }
               /** 不然開始執行新的認證方案 */
               else {
                    this.authenticationEntryPoint.commence(request, response, failed);
               }
               return;
          }
          /** 本過濾器執行完畢,執行後續過濾器 */
          chain.doFilter(request, response);
     }
}

其實很簡單是否是?

添加自定義認證邏輯

若是默認的驗證方式不知足要求,要怎麼添加自定義驗證方式呢?其實只須要添加自定義的Filter便可。

就好比說常見的短信驗證碼登陸方式:

image.png

默認不支持短信方式,咱們能夠在過濾器鏈中植入一個自定義的短信驗證過濾器,認證成功後設置認證信息便可。

SecurityContextHolder.getContext().setAuthentication(authResult);

Reactive

這個是上個月遇到的問題,嘗試了一下OAuth 2.0認證架構。

image.png

其實這個架構很廣泛,許多項目都採用該種架構。只不過都是採用Spring Cloud Netflix Zuul + Spring Security Resource Server的實現。

這裏我嘗試將Zuul換成Spring Cloud Gateway,由於Zuul的阻塞IO在網關層面實在太影響性能了。

前面已經說了,Spring Security中的認證與受權方式是經過Servlet技術套裝中的Filter實現的,Tomcat提供了Servlet的運行環境。

Spring Cloud Netflix Zuul集成Spring Boot Starter Web也就是默認的Tomcat實現網關,因此Zuul中是有Servlet的運行環境的。

而非阻塞的Spring Cloud Gateway基於Spring WebFlux框架,不支持Servlet,底層採用高性能的Netty服務器。

Spring WebFluxSpring MVC的對比請看下圖:

image.png

因此當Spring SecuritySpring Cloud Gateway集成時,就會出錯,Spring Security須要的Servlet環境沒有被知足。

這也是以前對Reactive的理解不到位而引發的錯誤,WebFlux環境下,應該集成Spring Security Reactive,而非默認的Spring Security

總結

經資料查閱,高性能的 Netty十分受各大互聯網企業歡迎,TwitterFacebook、蘋果、微博都在使用Netty,具體Netty爲何更適合高併發?之後咱們一塊兒學習。

相關文章
相關標籤/搜索