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