【Spring Boot】第17.4課-從 Security Context 取得 API 存取方的認證資訊 #2024 年更新


https://unsplash.com/photos/LmyPLbbUWhA

在開發商業邏輯時,我們經常需要知道 API 存取方的身份。舉例來說,在網路發表文章、按讚和留言,理論上都要在資料庫中紀錄「誰發的文」、「誰按的讚」和「誰留的言」,亦即資料的建立者。

本文將以 HTTP Basic 認證方式為例,示範如何從「Security Context」取得 API 存取方的使用者資料。接著準備自定義的使用者類別,以便在程式邏輯中能有更多的資料可運用。

最後透過觀察原始碼,認識 Spring Security 進行 HTTP Basic 認證的原理,讓讀者知道該認證資訊從何而來。


本文已經搬家,歡迎到「【Spring Boot】第12.4課-從 Security Context 取得 API 存取方的認證資訊」繼續閱讀。

留言

  1. 您好,想請問一般情況下,專案針對SpringSecurity+Jwt的部分也都是會進行單元測試嗎?
    因為要用mockMvc驗證API權限的情況下,感覺也比較偏向整合測試,
    再來這邊還有一點疑問就是,
    針對權限設置
    http.authorizeHttpRequests(registry ->
    registry.requestMatchers(HttpMethod.POST, "/users").permitAll()
    這邊使用的方式是讀取資料庫權限表做動態加載,
    如果要做測試勢必是不會進資料庫,那有沒有什麼方式做手動的權限配置呢?
    因為預設是用.anyRequest().authenticated(),
    requestMatchers沒有設置路徑會變成所有API都會被擋下

    回覆刪除
    回覆
    1. 嗨嗨,如果只是想測產生或驗證 JWT 的邏輯,單元測試要寫也是可以寫

      但若想測試發送 request 時,是否會被 Spring Security 阻擋,那就是整合測試的範疇了,直接用 MockMvc 即可!
      你會需要建立 HttpHeaders 物件,放置 access token,並將 HttpHeaders 傳入 MockMvc,詳細的用法再稍微查一下

      至於後面「要做測試勢必是不會進資料庫」的問題,我沒有看懂,可以再說明一下 :)

      刪除
    2. 您好,JWT測試的部分,secretKey如果不是固定字串也沒辦法驗證吧?
      secretKey = Keys.secretKeyFor(SignatureAlgorithm.HS256);
      jwtParser = Jwts.parserBuilder().setSigningKey(secretKey).build();
      因為我是用這種方式建,變成每次重啟都會變動,
      沒實戰過JWT,練習單純是不想用固定字段,也不確定一般的做法是怎麼樣或有什麼規範

      至於進資料庫的問題,我是想說一般做單元測試都是塞假資料或是mock,不會連資料庫,那要讀取資料庫做動態加載的部分要怎麼處理,但如果是整合測試這邊想法是直接用H2應該就可行了吧,也不會影響到測試環境的資料,就單純拿來運行測試

      刪除
    3. 我覺得還是用固定字串(Keys.hmacShaKeyFor 方法)來當作 Key 好了,不然單元測試似乎不好寫。感覺要去 mock「secretKey 」的方法,但又不知該如何 mock 才好。

      而後面的問題,意思是你想要寫整合測試,且希望 UserDetailsServiceImpl 能連真實的 DB。但範例程式中的資料卻是 hard code 的,不知道怎麼用自己的測試資料來寫嗎?(你也可以舉例測試情境)

      刪除
    4. 沒事,大致上沒問題了哈哈
      決定先寫controller以外的unitTest
      最後再用整合測試直接測API
      感謝你

      刪除
  2. 作者已經移除這則留言。

    回覆刪除
  3. 您好!感謝您的分享,我從中收穫很多
    這邊想要詢問比較進階的情況,就是使用H2 Databsaes跟Spring Security的時候,雖然可以Permit H2的網址,可以進入H2的登入畫面,但輸入帳密之後會無法進入控制台,都顯示Forbidden的情況,看網路上是說因為iframe被Security靜止的關係,但網路上並沒有人針對新版本的Security教如何開啟iframe,請問您可以開一個教學或者提供建議嗎~?

    回覆刪除
    回覆
    1. 嗨,我沒有用過 H2 這款資料庫,可能沒辦法很好的回答。
      你在 Security 設定 permit H2 的網址時,API 路徑格式是如何寫的呢?記得要用「**」才能套用到底下所有路徑,比方說「/h2-console/**」。

      刪除
    2. 謝謝您的分享,我已經用**套用到所有的路徑,有趣的是H2有一個登入介面,要在登入後才能操作整個資料庫,而在沒有套用/h2-console/**連登入介面都是forbidden,套用後可以進入登入介面,但登入資料庫後卻被forbidden,如果您有興趣可以嘗試引入H2的依賴玩玩看

      刪除
    3. 請問使用 jwt 的方式要如何在使用remember me?

      刪除
  4. 請問使用 jwt 的方式要如何在使用remember me?

    回覆刪除
    回覆
    1. 嗨,你說的「remember me」是指使用者下次回到網站,不必重新登入嗎?我覺得這個需要前端來配合。
      前端透過登入的 API 取得 JWT 後,請瀏覽器存在 Local Storage 之類的地方。當使用者下次回來網站,就取出該 JWT,放在 request header 中即可。
      由於前端不是我的專業,可能沒辦法給太多想法 :(

      刪除
  5. 過半年再回來看,還是覺得springSecurity真是深不見底
    到現在還是感謝看到這些文才能進入學習,雖然後來才發現自己基本是倒著學,一開始就配合JWT用,全部都自己手動操作,也沒真的理解無狀態,後面越看越深發現其實什麼登入驗證、session持久化,springSecurity都自動幫你做完了,一開始根本看不懂哈哈
    相信再過半年回來體悟應該又會不同XD

    回覆刪除
    回覆
    1. 我也是學了幾次,感覺都在一直記 Spring Security 各種物件的名詞
      去年參加 iThome 鐵人賽時有重寫文章,但感覺好混亂
      今年學習時,又有了新認識,於是再更新文章,果然還是要循序漸進,才能學得清楚

      刪除

張貼留言