如何在 ASP.NET Core 網站中使用聲明 Claim 做權限管理 Authorization #
最近在用 ASP.NET Core 開發自己的點餐網站時,需要加入會員登入系統,同時限制使用者對 API 的存取權限。如果使用者未經授權,則回傳 401 Unauthorized
;若權限不足,則回傳 403 Forbidden
,以防止未授權的用戶惡意存取或更改後端資料庫。這時候就需要用到 Claim (中文翻作聲明) 來做 Authorization (授權)。
認證與授權的概念 #
-
Authenticate(認證)
用來驗證使用者身份的方式,通常有 Cookie Authentication、JWT Bearer Authentication、OAuth 2.0(單一登入 SSO)。因這部分非本文重點,暫不討論。 -
Authorization(授權)
在使用者驗證完成後,根據資料庫中的使用者資訊,賦予不同的操作權限。
實現權限管理 #
以下我用一個簡單的範例教大家如何設定 Claim,賦予使用者權限~
var userName = "Hendrix";
var permission = "reader";
var claims = new[] {
new Claim(ClaimTypes.Name, userName),
new Claim(ClaimTypes.Role, permission)
};
var identity = new ClaimsIdentity(claims, permission);
var principal = new ClaimsPrincipal(identity);
var ticket = new AuthenticationTicket(principal, Scheme.Name);
return AuthenticateResult.Success(ticket);
各種 Claim 的介紹 #
看完以後會發現很像俄羅斯娃娃,一層包一層😆,以下我一個個講解:
-
Claim
這是指使用者的各式資訊,包含姓名、生日、性別、權限角色,是使用者的基本資訊單位。在此例中,建立了兩個Claim
:Name
和Role
,並指定角色為reader
。 -
ClaimsIdentity
將多個Claim
資訊打包,就像一張身份證,上面會有使用者不同的資訊。 -
ClaimsPrincipal
代表一組證件的集合,如同一個人可能擁有市民卡、健保卡等多種證件。就像對政府來說,你所擁有的所有證件就會充份代表你這個人,Principal就是這樣的概念。 -
AuthenticationTicket
但是有了這麼多證件,要驗證你到底是誰、你的證件是否正確,方式有很多種。有的可能是看你身份證照片跟你本人像不像,有的會拿紫外光照一下身份證看是不是證本,AuthenticationTicket就是一個明確包含指定驗證過程(如 Cookie 或 JWT 驗證)的文件/票卷,內含:AuthenticationScheme
:驗證方式。Principal
:驗證的使用者身份。Properties
:驗證票證的其他屬性(如票證到期時間)。
-
AuthenticateResult
本身包含以下內容:- 有效的通關文件(
AuthenticationTicket
)。 - 身分(
ClaimPrincipal
)。 - 驗證資訊(
AuthenticationProperties
)。 - 驗證結果(
AuthenticateResult
)。
我這邊直接使用
AuthenticateResult.Success
,是因為我自定義了我的驗證方式,並且確認輸入的userName
和permission
都有其值且有效。如果有使用情境是傳來的 Token 無效,可以考慮使用以下方式:
Fail
:表示驗證失敗。NoResult
:表示無法判斷驗證結果。
- 有效的通關文件(
注意:如果使用的是 .NET 預設的 Cookie 認證,不需要手動返回
AuthenticateResult.Success
,因為框架會自動處理認證的檢查和結果返回。
使用 Claim 跟傳統認證方式的差別 #
傳統的認證方式通常由每個應用程式自行設計,使用各自的使用者資料庫,但這種方式有局限性,難以滿足需多種認證的需求。
基於 Claim 的認證,就能把認證與授權分離為獨立服務,授權服務只需驗證 Claims
即可,就無需在意具體的認證方式。
Controller 控制器方法的 Role 角色授權 #
以下範例使用 Roles 授權,只需在方法上加上 [Authorize(Roles = "你允許使用的角色")]
即可。
這樣 .NET 會直接從使用者的 ClaimsPrincipal
中的角色(ClaimTypes.Role
)進行檢查,確保只有符合角色條件的使用者能夠執行該方法。
-
只有
editer
可以新增文章:[HttpPost] [Authorize(Roles = "editer")] public async Task<ActionResult<ArticleDto>> Add([FromBody] ArticleDto dto) { var createdArticle = await _articleService.AddAsync(dto); return CreatedAtAction(nameof(GetById), new { id = createdArticle.ArticleId }, createdArticle); }
-
reader
與editer
都可以查詢文章:[HttpGet("{id}")] [Authorize(Roles = "reader, editer")] public async Task<ActionResult<ArticleDto>> GetById(int id) { var article = await _articleService.GetByIdAsync(id); return Ok(article); }
結語 #
我下一篇再繼續打 Policy 授權 以及 自定義 AuthenticationScheme 的部份,第一次打教學文,看別人教學文感覺很簡單,但自己打真的一片空白,而且自己查完資料才發現原來我使用的Authorization 方法和別人不太一樣。
原本我是用自定義一個自己的 AuthenticationScheme,一直試錯後誤打誤撞到授權成功,發現別人都是用預設的 Cookie Authentication後,對自己用的土炮方法傻眼… 算是邊打教學文邊教學自己🤣