把玩 Spring Security [2] 探索 Access Control 功能

Ching Yi, Chan
5 min readOct 3, 2021

在先前的實驗中,我們製作了一個新的 Security Filter 放行了任何 HTTP REQUEST。在這個基礎上,我們可以來探索 Spring Security 的 Access Control。

在安全的領域中,有二個主要的術語要認得:

  • Authentication
  • Authorization

在前一篇介中,我們知道了 Spring Security 如何得知一個 HTTP REQUEST 有沒有獲得 Authentication (獲得身份驗證),也就是系統知道這一個 HTTP REQUST 是代表者某一個特定的使用者,或特定的裝置,或更通俗一點的 Client。那麼在獲得身份之後,一個安全系統會關心的問題是,用這個身份能做些什麼?即為 Authorization,被 授權 了哪些能力。

路徑與授權控制

將上回的範例修改如下,在 antMatchers 後,我們多加了 hasRole 去限制對應路徑需要的 授權

這樣的寫法相當直覺好懂:

  • 修改 /users 指令要有 USER Role 的人才能使用
  • 增加 /admin 並指定要有 ADMIN Role 的人才能使用
  • 增加 /info 沒有特定角色的授權,只要登入就行了
  • 沒有提到的 / 是由 anyRequest() 開通無限制存取 permitAll()

由於增加了這部分,我們的 Controller 也會加一些新的方法:

提供授權資訊

在目前版本的 Security Filter 除了回應 isAuthenticated() 之外沒有額外提供進一步資訊:

我們能透過它來提供授權資料,ApiToken 只是單純實作了 Authentication 介面,並強制 isAuthenticated 回應 true 罷了。這麼作只是為了讓 Spring Security 認為這個 HTTP REQUEST 是已通過驗證。授權資料可以透過覆寫 getAuthorities() 方法來提供:

這簡單地修改,強制了目前的 Authentication 物件,回傳了一個含有 ROLE_ADMIN 的授權資料。這邊比先前的 hasRole 中的設定,多出了 ROLE_ 前綴字串,這是預設的 AccessDecisionVoter 的行為 (RoleVoter)。簡單實測,僅有需要 USER role 的 /users 被阻擋:

使用標註式授權

除了透過 HttpSecurity 對特定路徑指令可以存取的角色,也能透過 Annotation 來指定授權。這樣,權限就能針對任何 Spring 管理的物件方法進行設定,以下是使用 @PreAuthorize 指定 info 方法需要有 USER role 才能使用:

但別忘了,這種用法需要對 Spring Boot Application 加開新的設定 @EnableGlobalMethodSecurity

  • prePostEnabled 是啟用 @PreAuthorize / @PostAuthorize
  • securedEnabled 是啟用 @Secured

重點摘要

  • 學習在 HttpSecurity 中,利用 hasRole() 指定路徑需要的角色權限
  • Security Filter 產生的 Authentication 物件,可以透過 getAuthorities() 方法提供授權資料。
  • 提用 Annotation 的方式標註權限控管

到目前為止,你可能會發現我們都還沒有談到循序圖中的 AuthenticationManager。先不談它是可以降低學習的門檻,因為身份驗證的方式百百種,看了太多的方式難勉眼花撩亂而壞了學習的興致。我們可以簡略地想成這樣:

這段程式範例,它與先前直接 new ApiToken() 沒有差太多,只是改成了可變的,是由查詢而來的!而要去哪查詢就依不同的 AuthenticationProvider 實作來決定了,可以是 OAuth Provider 或是 JDBC 連線查詢,當然也可以是 JWT 解碼後的資料。有了這樣的認知後 Authentication 就不再是難以掌握的主題。

本篇完整範例請參考:

工商時間

2021 JCConf 將在 11/19 線上舉辦,即日起售票中

https://twjug.kktix.cc/events/jcconf-2021

--

--