鍍金池/ 教程/ Java/ 認(rèn)證簡(jiǎn)介
初體驗(yàn)
權(quán)限鑒定基礎(chǔ)
Remember-Me 功能
匿名認(rèn)證
intercept-url配置
認(rèn)證簡(jiǎn)介
退出登錄 logout
AuthenticationProvider
Filter
關(guān)于登錄
異常信息本地化
緩存 UserDetails
session 管理
核心類(lèi)簡(jiǎn)介

認(rèn)證簡(jiǎn)介

認(rèn)證過(guò)程

  1. 用戶(hù)使用用戶(hù)名和密碼進(jìn)行登錄。
  2. Spring Security 將獲取到的用戶(hù)名和密碼封裝成一個(gè)實(shí)現(xiàn)了 Authentication 接口的 UsernamePasswordAuthenticationToken。
  3. 將上述產(chǎn)生的 token 對(duì)象傳遞給 AuthenticationManager 進(jìn)行登錄認(rèn)證。
  4. AuthenticationManager 認(rèn)證成功后將會(huì)返回一個(gè)封裝了用戶(hù)權(quán)限等信息的 Authentication 對(duì)象。
  5. 通過(guò)調(diào)用 SecurityContextHolder.getContext().setAuthentication(...) 將 AuthenticationManager 返回的 Authentication 對(duì)象賦予給當(dāng)前的 SecurityContext。

上述介紹的就是 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)限鑒定。

Web 應(yīng)用的認(rèn)證過(guò)程

如果用戶(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ò)程將如下:

  1. 引導(dǎo)用戶(hù)進(jìn)行登錄,通常是重定向到一個(gè)基于 form 表單進(jìn)行登錄的頁(yè)面,具體視配置而定。
  2. 用戶(hù)輸入用戶(hù)名和密碼后請(qǐng)求認(rèn)證,后臺(tái)還是會(huì)像上節(jié)描述的那樣獲取用戶(hù)名和密碼封裝成一個(gè) UsernamePasswordAuthenticationToken 對(duì)象,然后把它傳遞給 AuthenticationManager 進(jìn)行認(rèn)證。
  3. 如果認(rèn)證失敗將繼續(xù)執(zhí)行步驟 1,如果認(rèn)證成功則會(huì)保存返回的 Authentication 到 SecurityContext,然后默認(rèn)會(huì)將用戶(hù)重定向到之前訪(fǎng)問(wèn)的頁(yè)面。
  4. 用戶(hù)登錄認(rèn)證成功后再次訪(fǎng)問(wèn)之前受保護(hù)的資源時(shí)就會(huì)對(duì)用戶(hù)進(jìn)行權(quán)限鑒定,如不存在對(duì)應(yīng)的訪(fǎng)問(wèn)權(quán)限,則會(huì)返回 403 錯(cuò)誤碼。

在上述步驟中將有很多不同的類(lèi)參與,但其中主要的參與者是 ExceptionTranslationFilter。

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ò)誤碼。

在 request 之間共享 SecurityContext

可能你早就有這么一個(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)證了。

上一篇:Filter