這篇文章會介紹如從零開始一步一步透過使用 Spring Security 來實作登入、認證、角色權限等機制

所有程式碼可以到我的 GitHub 查看: https://github.com/lakesd6531/springsecurity

 

Dependency

建立 Spring boot project

這次的範例是使用 spring boot 3.0.5 版本,java 17

最一開始我們加入一些基本的 dependency 

像是 spring jpa, spring security, spring web, mysql, lombok

後面在實作時,若有需要我們再加入其他的 dependency

init dependency.png

 

資料庫連線設定

接著我們在 application.properties 設定資料庫連線資訊

我這邊是連接我在本機的 mysql 資料庫

DB conection.png

 

Model Object

接著下面我們會開始建立 Entity 物件,前置作業是需要在本機的資料庫當中建立這些所需資料表

本篇文章的重點不在於資料庫的設置,所以各位可以替換任何自己偏好的資料庫設定

 

User Entity - 這是一張主要記錄使用者的資料表,包含名字、帳號、密碼

user entity.png

 

Role Entity - 角色資料表

role entity.png

 

UserRole Entity - 這是一張關聯使用者和角色的資料表,一個使用者可以擁有多個角色

user role entity.png

 

本次範例會先略過註冊使用者、建立角色、使用者賦予角色這些API的功能實作

會在資料庫當中直接建立測試資料

本次範例主要將重點放在使用者登入後取得 jwt token,使用者再拿 token 進行API的操作

 

登入授權

在這部分,我們要開始實作一個登入的API

在Request部分,我們將會帶入帳號、密碼,若比對正確則發一個JWT token給使用者

當使用者拿到這個 token,形同是登入成功,接下拿打API都會拿這個 token 作為通關的依據

(這邊的實作只是簡易的示範,關於資訊安全,密碼加密等並未在此範例中考量)

 

在開始實作前我們要先加入三個依賴

longin dependency.png

 

接著我們建立一個 AuthenticationController

AuthenticationController.png

在這個 Controller 當中有一個 Post 的方法,RequestBody 物件如下

這邊我們用 spirng validation 來幫我們檢查 request,account 和 password 不得為空也不能為空字串

詳細介紹用法可參考 Spring Boot Validation - 如何使用Spring Boot自動驗證參數正確性 這篇文章

LoginDTO.png

最後的Response 我們預期為一個字串,我們要回傳的就是一個 token

AuthenticationService.png

接著我們在 AuthenticationService 裡面就開始實作產生 token 的細節

首先我們透過 UserRepository 從資料庫當中取出資料,並進行帳號、密碼的比對驗證

UserRepository.png

比對成功,代表使用者要驗證的資料正確

就可以開始產生 token, 首先要先準備好 payload,

這個payload 就是接下來我們會放在 token 內容當中,在這邊我們把使用者的 id 和 名字放進去

接著我們建立一個 JwtUtils 來幫我們產生 token

JwtUtils.png

在這邊我們設定 token 的過期時間為 60分鐘,並以相關的演算法和Key來加密token內容

 

到這邊我們的實作應該算是到了一個段落,我們若將程式跑起來,實際打看看這支登入 API 試試看

test longin 1.png

我們會得到 401 的錯誤碼,

這是因為 在你還沒有對 Spring security 進行認證的設定下,Spring security 預設的狀況下會拒絕所有 request 的訪問

為了讓我們現在能夠成功的測試登入API功能是否正常,我們先設定一個 security config,讓所有的request 都能夠被允許

JWTSecurityConfig1.png

我們重啟程式,再打一次API

這時候我們就能夠成功的拿到 token

(這邊能夠登入成功的前提是我在資料庫有新增一個 mark 這個使用者)

test longin 2.png

我們可以拿著這個 token 透過 jwt的線上工具 來看看這個 token 的內容長怎麼樣子

jwt token.png

我們就可以看到我們程式是有成功的把使用者的資料加入到 token 的內容當中

 

認證

到目前為止,我們已經完成發證的動作,接著則是做認證的設定

首先先實作 JwtAuthenticationService

JwtAuthenticationService.png

首先我們先在 JwtUtils 實作一個方法,幫我們解析 token 的內容

getClaim.png

接著根據分析出來的 token 當中的 userId, 進行資料檢查與查詢

接著找出該使用者擁有的角色權限,並找出該角色的名稱

最後組成一個 JwtAuthenticationTokenDTO 回傳

JwtAuthenticationTokenDTO.png

 

接下來要實作 JwtAuthenticationFilter 繼承於 OncePerRequestFilter

JwtAuthenticationFilter.png

filter 首先會先檢查 request 的 header 是否有 Authorization 這個 key

接著檢查 Authorization 的 value 是不是正確的 token

並且利用我們剛剛實作的 JwtAuthenticationService 來進行 JWT Token 的認證檢查

並取得使用者的基本資料,以及該使用者有的角色名稱

接著把 JwtAuthenticationTokenDTO 轉成 JwtAuthenticationToken 物件

toJwtAuthenticationToken.png

 JwtAuthenticationToken 物件如下所示:

該物件繼承於 UsernamePasswordAuthenticationToken

JwtAuthenticationToken.png

最終我們把 JwtAuthenticationToken 這個物件放進 Spring security context holder 當中

這有兩個用意:

其一是未來我們可以從 Spring security context holder 中取得目前登入的使用者基本資訊

其二是我們可以在 Controller 設設置 @PreAuthorize 的標籤,進行權限管理

由於我們剛剛已經利用 角色名稱 (role name) 建立了 GrantedAuthority

所以接下來我們若在 Controller 設設置 @PreAuthorize 的標籤,

Spring 會根據 Spring security context holder 中的 GrantedAuthority 自動幫你判斷該使用者是否有權限操作此 api

 

最後,我們要來調整 JWTSecurityConfig

文章開始前,我們為了測試程式是否能夠正常的發 token 出來,所以允許了所有的 request

JWTSecurityConfig2.png

而我們此次的調整,我們則是只允許特定的路徑能夠在沒有認證的狀況下可以直接通行

像是 swagger 的路徑,以及登入的 API

因為登入的 API 應開放給所有人去嘗試登入取得 token ,因此我們這邊就不進行認證檢查

如果並非在我們的設定中的路徑當中,則是需要經過 JwtAuthenticationFilter 來進行認證檢查

 

測試

接下來我們建立一個測試用的Controller

TestController1.png

啟動程式後,我們試著打這支 API ,就會得到 403 的結果

image

所以首先我們必須先打 login 的 API 取得 token

image

 

接著我們在 teat api 加上 Authorization 的 header,值的話就是用 Bearer 再加上 剛剛取回來的 token (例如: Bearer token...)

這樣一來我們就可以正確取得 response

也驗證了我們的授權認證是成功的

image

 

我在 TestController 再加上兩個 測試 API

分別貼上 @PreAuthorize ,進行權限的綁定

testAdmin 這支 API 需要使用者有 admin 的角色 (這邊的admin就是我在資料庫當中設定的角色名字)

testUser 則是需要有 user 的角色才能操作

TestController2.png

在測試之前,我已經將 Mark 這個使用者在資料庫當中加上了 user 的角色

所以我打 testUser 是能夠成功取得 response

image

而 testAdmin 則會回 403, 這是因為 Mark 這個使用者並沒有 admin 這個角色

 

image

arrow
arrow

    Mark Zhang 發表在 痞客邦 留言(0) 人氣()