上述介紹的就是 Spring Security 的認證過程。在認證成功后,用戶就可以繼續(xù)操作去訪問其它受保護的資源了,但是在訪問的時候?qū)褂帽4嬖?SecurityContext 中的 Authentication 對象進行相關的權限鑒定。
如果用戶直接訪問登錄頁面,那么認證過程跟上節(jié)描述的基本一致,只是在認證完成后將跳轉到指定的成功頁面,默認是應用的根路徑。如果用戶直接訪問一個受保護的資源,那么認證過程將如下:
在上述步驟中將有很多不同的類參與,但其中主要的參與者是 ExceptionTranslationFilter。
ExceptionTranslationFilter 是用來處理來自 AbstractSecurityInterceptor 拋出的 AuthenticationException 和 AccessDeniedException 的。AbstractSecurityInterceptor 是 Spring Security 用于攔截請求進行權限鑒定的,其擁有兩個具體的子類,攔截方法調(diào)用的 MethodSecurityInterceptor 和攔截 URL 請求的 FilterSecurityInterceptor。當 ExceptionTranslationFilter 捕獲到的是 AuthenticationException 時將調(diào)用 AuthenticationEntryPoint 引導用戶進行登錄;如果捕獲的是 AccessDeniedException,但是用戶還沒有通過認證,則調(diào)用 AuthenticationEntryPoint 引導用戶進行登錄認證,否則將返回一個表示不存在對應權限的 403 錯誤碼。
可能你早就有這么一個疑問了,既然 SecurityContext 是存放在 ThreadLocal 中的,而且在每次權限鑒定的時候都是從 ThreadLocal 中獲取 SecurityContext 中對應的 Authentication 所擁有的權限,并且不同的 request 是不同的線程,為什么每次都可以從 ThreadLocal 中獲取到當前用戶對應的 SecurityContext 呢?在 Web 應用中這是通過 SecurityContextPersistentFilter 實現(xiàn)的,默認情況下其會在每次請求開始的時候從 session 中獲取 SecurityContext,然后把它設置給 SecurityContextHolder,在請求結束后又會將 SecurityContextHolder 所持有的 SecurityContext 保存在 session 中,并且清除 SecurityContextHolder 所持有的 SecurityContext。這樣當我們第一次訪問系統(tǒng)的時候,SecurityContextHolder 所持有的 SecurityContext 肯定是空的,待我們登錄成功后,SecurityContextHolder 所持有的 SecurityContext 就不是空的了,且包含有認證成功的 Authentication 對象,待請求結束后我們就會將 SecurityContext 存在 session 中,等到下次請求的時候就可以從 session 中獲取到該 SecurityContext 并把它賦予給 SecurityContextHolder 了,由于 SecurityContextHolder 已經(jīng)持有認證過的 Authentication 對象了,所以下次訪問的時候也就不再需要進行登錄認證了。
更多建議: