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