---
# System prepended metadata

title: Spring Security 經驗談 by 博玄

---

# Spring Security 經驗談 by 博玄

最近因為在台灣之星做重構案，加上專案只有兩人，因此很多事情都需要自己包山包海，因此在Spring Security上稍微用的更深入一些了

### Spring Security 簡介
Spring Security 是 Spring 官方推出的一款安全驗證框架，內容大略分為驗證、授權，在使用上除了簡單的實作 UserDetailsService 完成帳號密碼登入外，Spring Security 也提供使用自定義的驗證完成登入功能

### 自定義驗證
Spring Security除了本身已開發內建的 Filter、Provider 功能外，他也允許開發者實作自定義的 Filter 或者 Provider 來完成驗證功能，至於要怎麼做可能需要看實際需求來進行開發，譬如

1. 一般帳號密碼登入 request 參數非 username、password
    Spring Security Filter 本身會攔截這兩個參數進行帳密登入流程，假設今天需求或者開發上無法使用這兩個參數名稱，則需自行開發 Filter (繼承AbstractAuthenticationProcessingFilter) 攔截參數並封裝存入 UsernamePasswordAuthenticationToken 回傳，然後將 Filter 註冊至Spring Security Filter 序列中，後續 Provider 拿到該 Token 物件時會在執行原流程的登入驗證
    
2. 一般帳號密碼登入，登入驗證時除帳密驗證外，還有其他驗證
    如果有其他驗證需求，則需實作 Provider，並在 WebSecurityConfig (繼承 WebSecurityConfigurerAdapter) 中 Override authenticationManager 方法，將該 Provider 加入至 ProviderManager 之中。
    
3. 特殊登入機制
    若有其他特殊驗證登入機制，則可以看需要要如何去開發，可以簡單的直接在Filter 完成所有驗證機制，也可以開發自定義的 Filter、Provider
    
### 權限授權
授權的實作方式有幾種，一般帳號密碼登入在實作 UserDetailsService 時，會連同角色權限一起取得，在驗證完成後一併塞入權限物件中，若是自訂的Provider，也可自行在這完成，除此之外Spring Security 還有提供一個介面 AthenticationSuccessHandler，該介面需加到對應的 Filter 中，透過 setAuthenticationSuccessHandler 加入，加入後會在登入驗證成功後呼叫，也可以在這裡完成授權機制。

### 權限驗證
Spring Security 提供了許多驗證策略，一種是實作 PermissionEvaluator 並加入到 WebSecurityExpressionHandler，另一種是 AccessDeniedManager

PermissionEvaluator 提供兩種驗證 Method 需進行實作，開發者可以依照自己的需求實作所需要的驗證機制，並且該驗證方法可以在 Thymeleaf Template 或者是在 Controller Method 上透過 Annotation 使用

AccessDeniedManager 則較為複雜，他是一種投票策略的驗證方式 Spring Security 本身內建三套機制可以使用，我目前有用到的是 AffirmativeBased 這種策略是他所有驗證只要有一項通過，就判定用戶有權限，詳細的使用說明可以上網找資訊或者參考官網

### 結尾
Spring Security 還有提供 Exception Handler 可以結合使用作一些例外判斷處哩，而這次重構案鎖讓我嘗試學到的經驗大致上如此，歡迎大家未來可以多多修改或者增加內容

### 補充 Spring Security JWT 實作
以下是簡單的說明

首先在 WebSecurityConfig 我們必須透過 SessionManagement 設定 SessionCreationPolicy(SessionCreationPolicy.STATELESS)，這個設定是讓用戶請求永遠不會產生 Session

再來透過實作 AthenticationSuccessHandler 在登入成功後產生 JWT Token 並放到　Response 用回登入成功後回傳資訊

接著自訂一個 Filter 用來取得 Request Header 檢查是否含有 authorization，並驗證 Token 正確性，若驗證成功就產生權限物件放到Spring Security Context 中，失敗看要用 exception 或者不作任何處理也可以 (Spring Security 整套驗證走玩沒有建立權限物件就會判定登入失敗)

後續前端只需要將拿到的 JWT Token 放到 Request Header 內 key: authorization, value: JWT Token，就可以呼叫 API 功能

基本的 JWT 應用這樣就可以完成，但實務上會有很多其他問題需要解決，像是用戶如何 Refresh JWT Token，若用戶提早 Refresh Token 由於 JWT Token 只要產生在時效內就都會有效，舊的 Token 是否該黑名單處理等等之類的