SpringSecurity過濾器原理


SpringSecurity原理

主要過濾器鏈

SpringSecurity的功能主要是由一系列的過濾器鏈相互配合完成的。驗證一個過濾器之后放行到下一個過濾器鏈,然后到最后。

認證流程

過濾器作用

  1. SecurityContextPersistenceFilter:會在每次請求處理之前從配置好的SecurityContextRepository中獲取SecurityContext安全上下文信息,然后加載到SecurityContextHolder中,然后在該次請求處理完成之后,將SecurityContextHolder中關於這次請求的信息存儲到一個“倉庫”中,然后將SecurityContextHolder中的信息清除,例如在Session中維護一個用戶的安全信息就是這個過濾器處理的。

  2. DefaultLoginPageGeneratingFilter:如果沒有配置自定義登錄頁面,那系統初始化時就會配置這個過濾器,並且用於在需要進行登錄時生成一個登錄表單頁面。

  3. BasicAuthenticationFilter:檢測和處理http basic認證。

  4. UsernamePasswordAuthenticationFilter:用於處理基於表單的登錄請求,從表單中獲取用戶名和密碼。默認情況下處理來自/login的表單action。從表單中獲取用戶名和密碼時,默認使用的表單name屬性值為username和password,這倆個值也可以通過usernameParameter和passwordParameter在配置中自定義。

    這個過濾器在表單提交登錄請求之時會起作用。那么假設現在采用SpringSecurity整合Jwt,那么我需要配置一個Jwt登錄認證類(繼承BasicAuthenticationFilter或者繼承OncePerRequestFilter都可以,因為BasicAuthenticationFilter繼承了OncePerRequestFilter),重寫過濾器方法。Jwt的token認證登錄是需要在在采用用戶名密碼登錄認證之前,所以在配置Jwt登錄認證類的時候需要在UsernamePasswordAuthenticationFilter之前添加過濾器。

    //配置自定義過濾器 添加jwt登錄授權過濾器
    //在過濾器UsernamePasswordAuthenticationFilter之前
    http.addFilterBefore(jwtAuthenticationFilter,UsernamePasswordAuthenticationFilter.class);
    
  5. RequestCacheAwareFilter:用來處理請求的緩存。

  6. SecurityContextHolderAwareRequestFilter:主要是包裝請求對象request。

  7. AnonymousAuthenticationFilter:檢測SecurityContextHolder中是否存在Authentication對象,如果不存在則為其提供一個匿名Authentication。

  8. SessionManagementFilter:管理Session的過濾器

  9. ExceptionTranslationFilter:捕獲來自過濾器鏈的所有異常,並進行處理。但是只處理兩類異常:AccessDeniedException和AuthenticationException 異常,其他的異常會繼續拋出。

    如果捕獲到的AuthenticationException,那么將會使用其對應的AuthenticationEntryPoint的commence()方法處理。在處理之前,ExceptionTranslationFilter先使用RequestCache將當前的HTTPServletRequest的信息保存起來,方便用戶登錄成功后可以跳轉到之前的頁面。

    可以自定義AuthenticationException的處理方法。需要實現AuthenticationEntryPoint接口,然后重寫commence()方法。

    /**
     * 當未登錄或者token失效時訪問接口自定義的返回結果
     */
    @Component
    public class RestfulAuthorizationEntryPoint implements AuthenticationEntryPoint {
        @Override
        public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException e) throws IOException, ServletException {
            response.setCharacterEncoding("utf-8");
            response.setContentType("application/json");
            PrintWriter writer = response.getWriter();
            RespBean bean = RespBean.error("請先登錄!");
            bean.setCode(401);
            writer.write(new ObjectMapper().writeValueAsString(bean));
            writer.flush();
            writer.close();
        }
    }
    

    如果捕獲的AuthenticationDeniedException,那么將會根據當前訪問的用戶是否已經登錄認證做不同的處理,如果未登錄,則會使用關聯的AuthenticationEntryPoint的commence()方法進行處理,否則將使用關聯的AccessDeniedHandler的handle()方法進行處理。

    可以進行自定義AuthenticationDeniedException的處理方法。需要實現AccessDeniedHandler接口,然后重寫handle()方法。

    @Component
    public class RestfulAccessDeniedHandler implements AccessDeniedHandler {
        @Override
        public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException e) throws IOException, ServletException {
            response.setCharacterEncoding("utf-8");
            response.setContentType("application/json");
            PrintWriter writer = response.getWriter();
            RespBean error = RespBean.error("權限不足,聯系管理員!");
            writer.write(new ObjectMapper().writeValueAsString(error));
            error.setCode(403);
            writer.flush();
            writer.close();
        }
    }
    
  10. FilterSecurityInterceptor:可以看做過濾器鏈的出口

  11. RememberMeAuthenticationFilter:當用戶沒有登錄而直接訪問資源時, 從 cookie 里找出用戶的信息, 如果 Spring Security 能夠識別出用戶提供的remember me cookie, 用戶將不必填寫用戶名和密碼, 而是直接登錄進入系統,該過濾器默認不開啟。

SecurityContextHolder

SecurityContext對象是安全上下文信息,包括當前使用系統的用戶的信息。每個用戶都會有它的安全上下文對象,所以把每一個用戶的SecurityContext保存到SecurityContextHolder中。

SecurityContextHolder存儲SecurityContext的方式根據應用場景不同也有區別:

(1)單機系統,即應用從開啟到關閉的整個生命周期只有一個用戶在使用。由於整個應用只需要保存一個SecurityContext(安全上下文即可)

(2)多用戶系統,比如典型的Web系統,整個生命周期可能同時有多個用戶在使用。這時候應用需要保存多個SecurityContext(安全上下文),需要利用ThreadLocal進行保存,每個線程都可以利用ThreadLocal獲取其自己的SecurityContext,及安全上下文。ThreadLocal內部會用數組來存儲多個對象的。原理是,ThreadLocal會為每個線程開辟一個存儲區域,來存儲相應的對象。

Authentication:用戶信息的表示

在SecurityContextHolder中存儲了當前與系統交互的用戶的信息。Spring Security使用一個Authentication 對象來表示這些信息。

Authentication 主要包含了:

  • 用戶權限集合
  • 用戶證書(密碼)
  • 細節(Details)
  • Principal(就是這個用戶的賬戶信息)

在自定義登錄認證過濾器的時候,記得需要把用戶的信息(Authentication )保存到SecurityContextHolder中,以便后續用戶的正常使用。比如我在做和Jwt認證的整合的時候,繼承OncePerRequestFilter,重寫doFilterInternal方法,認證完token之后,就需要把用戶的信息存入安全上下文Holder中。

UsernamePasswordAuthenticationToken authenticationToken
    =new UsernamePasswordAuthenticationToken(user,null, null);
authenticationToken.setDetails(new WebAuthenticationDetailsSource()
                               .buildDetails(request));

SecurityContextHolder.getContext().setAuthentication(authenticationToken);

關於SecurityContextHolder大概就這樣,有一些關於SecurityContextHolder具體的源碼的細節可以參考一篇博客:

https://www.cnblogs.com/longfurcat/p/9417912.html


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM