把玩 Spring Security [3] 拼上最後一哩路 AuthenticationProvider

Ching Yi, Chan
6 min readOct 7, 2021
Work with Authentication Provider

我們即將進入多數 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

再配合各種「驗證方法」自身的領域知識,那就能掌握多數的使用情境囉!

--

--