把玩 Spring Security [3] 拼上最後一哩路 AuthenticationProvider
我們即將進入多數 Spring Security 帶你入門的文章或教學範例起手式的地方:AuthenticationManager。說到「即將」,只是先開始有個念頭,實際上由前二篇的概念解說,你會發現即使沒有 AuthenticationManager 依然能驅動 Spring Security 並進行權限控管 (Authorization)。AuthenticationManager 的角色只是提供一個管理的介面,來選擇適當的 Authentication。
我們能簡單地想,AuthenticationManager 是認證領域中的 Plugin Manager,世上有千奇百怪的驗證方式,若是接下來要使用的驗證方式是「已經」有個善心人士實作過的,不管他是來自 Open Source 專案,或是公司內舊專案複製出來的程式片段。只要他們向 AuthenticationManager 宣示自己的存在,並加入 Security Filter 的責任鏈參與認證流程即可。
對於 Web Application 來說,能用來進行 Authentication 的材料並不是什麼特別的存在,它就是一個 HTTP REQUEST 中能提供的內容罷了。我們利用 curl -v
來顯示他發出去的 HTTP REQUEST 與得到的 HTTP RESPONSE:
只要是 HTTP REQUEST 提供的資訊都可以被用來作為 Authentication 的材料,例如:
- URL 的 Query String
- HTTP REQUEST 的 header
Security Filter 的責任
理解了認證需要的材料來源後,我們來重新理解一下能取得 HTTP REQUEST 的 Security Filter 該做些什麼。在先前我們實作的是不太負責的 Security Filter,作為參與 Authentication 的一份子,它其實有一些責任要負的。我們用虛擬碼的型式來描述一下:
這組虛擬碼,如果要把它表達成我們先前「不負責」的型式,那就是剩下最後一段:
所以,未完成的拼圖有 2 處:
- 由 HTTP REQUEST 取出認證資料,並用 Authentication 型別裝起來。(先前實作的
ApiToken
算是有稍為摸到邊),最終目標是把轉好的 Authentication,讓 AuthenticationManager 提供驗證結果。 - 向 AuthenticationManager 提供 AuthenticationProvider 來達成轉換 Authentication 的功能
認證情境
概念解說先告一段落,我們來設計一個實際的驗證方法體驗一下流程。
我們的認證資料會放在
x-twjug-authorization
Header 內,它是一個神秘的 token,沒有人知道規則。不過,我們有個祖傳的 map 記錄了 token 對應到哪個使用者。
白話地說,我們會抓出 HTTP REQUEST 的 x-twjug-authorization
Header 轉成 Authentication 物件,也就是 asAuthentication 方法:
實作起來相當直覺 (這裡順便替 ApiToken 加了新的建構子來傳入 token 內容):
而祖傳的 map 只是我們簡化了資料庫或外部資料讀取的情境罷了。
拼圖之一:完成 Filter 實作
僅僅是將虛擬碼轉成實際的 Java 程式:
拼圖之二:提供 AuthenticationProvider
AuthenticationProvider 即為要向 AuthenticationManager 註冊的 Plugin,單純有 Filter 還不算完工是因為,我們還沒提供實際的驗證方法:
不像在第一篇時,我們硬是 hard-code 了任何人都通過,這回我們來嚴肅一點,好好把它做出來唄!只有簡單的二步:
- 實作 AuthenticationProvider
- 註冊 AuthenticationProvider
實作 AuthenticationProvider
介面只有 2 個方法,一個是決定哪一個實際的 Authentication 類別是需要支援的,以我們的例子就是 ApiToken:
這裡留了一部分
TODO
,那其實就是要接上 祖傳 map 的部分
註冊 AuthenticationProvider,就是回頭修改 WebSecurityConfigurerAdapter
找利用 AuthenticationManagerBuilder
註冊我們新寫的 Plugin 即可:
重點摘要
本篇是《把玩 Spring Security》系列的最後一篇,我們透過實作自有的 AuthenticationProvider 來理解了 Security Filter 與 AuthenticationProvider 的互動。
有了這樣的經驗,往後拿到「別人」寫的好函式庫時,就知道要讓它正確運作要事情是:
- 向 AuthenticationManager 註冊自己
- 註冊新的 Security Filter 來轉換 Authentication 並轉交給 AuthenticationManager 使用
當使用不同的驗證方式遇到困難時,就以上述二個大方向著手:
- 在 Spring 啟動 log message 應該要能看得到新的 Filter 的順位
- 知道 Debugger 的 breakpoint 可以下在 Filter 與 Provider
再配合各種「驗證方法」自身的領域知識,那就能掌握多數的使用情境囉!