ASP.NET Core 3.0 一個 jwt 的輕量角色/使用者、單個API控制的授權認證庫
目錄
- 說明
- 一、定義角色、API、使用者
- 二、新增自定義事件
- 三、注入授權服務和中介軟體
- 三、如何設定API的授權
- 四、新增登入頒發 Token
- 五、部分說明
- 六、驗證
說明
ASP.NET Core 3.0 一個 jwt 的輕量角色/使用者、單個API控制的授權認證庫
最近得空,重新做一個角色授權庫,而之前做了一個角色授權庫,是利用微軟的預設介面做的,查閱了很多文件,因為理解不夠,所以最終做出了有問題。
之前的舊版本 https://github.com/whuanle/CZGL.Auth/tree/1.0.0
如果要使用微軟的預設介面,我個人認為過於繁雜,而且對於這部分的資料較少。。。
使用預設介面實現授權認證,可以參考我另一篇文章
ASP.NET Core 使用 JWT 自定義角色/策略授權需要實現的介面
得益於大笨熊哥的引導,利用放假時間重新做了一個,利用微軟本身的授權認證,在此基礎上做拓展。特點是使用十分簡便,無需過多配置;因為本身沒有“造輪子”,所以如果需要改造,也十分簡單。
此庫更新到 .Net Core 3.0 了,如果需要在 2.2X 上使用,可以到倉庫下載專案,然後把 Nuget 包換成 2.2 的。
感謝大笨熊哥的指導。
專案倉庫地址 https://github.com/whuanle/CZGL.Auth
一、定義角色、API、使用者
隨便新建一個網站或API專案,例如 MyAuth。
Nuget 裡搜尋 CZGL.Auth,按照 2.0.1 版本,或者使用 Package Manager 命令
Install-Package CZGL.Auth -Version 2.0.1
CZGL.Auth 設計思路是,網站可以存在多個角色、多個使用者、多個API,
一個角色擁有一些 API,可以新增或刪除角色或修改角色所有權訪問的 API;
一個使用者可以同時屬於幾個角色。
第一步要考慮網站的角色、使用者、API設計,
CZGL.Auth 把這些資訊儲存到記憶體中,一個使用者擁有那幾個角色、一個角色具有哪些API的訪問許可權。
角色跟 API 是對應關係,使用者跟角色是多對多關係。
新建一個類 RoleService.cs ,引入 using CZGL.Auth.Services;
,RoleService 繼承 ManaRole。
通過以下介面操作角色許可權資訊
protected bool AddRole(RoleModel role);
protected bool AddUser(UserModel user);
protected bool RemoveRole(string roleName);
protected bool RemoveUser(string userName);
很明顯,新增/移除一個角色,新增/移除一個使用者
假如有 A、B、C 三個角色,
有 /A、/B、/C、/AB、/AC、/BC、/ABC 共7個API,設定許可權
A 可以訪問 A、AB、AC、ABC
B 可以訪問 B、AB、BC、ABC
C 可以訪問 C、AC、BC、ABC
這裡採用模擬資料的方法,不從資料庫裡面載入實際資料。
在 RoleService 裡面增加一個方法
/// <summary>
/// 用於載入角色禾API
/// </summary>
public void UpdateRole()
{
List<RoleModel> roles = new List<RoleModel>
{
new RoleModel
{
RoleName="A",
Apis=new List<OneApiModel>
{
new OneApiModel
{
ApiName="A",
ApiUrl="/A"
},
new OneApiModel
{
ApiName="AB",
ApiUrl="/AB"
},
new OneApiModel
{
ApiName="AC",
ApiUrl="/AC"
},
new OneApiModel
{
ApiName="ABC",
ApiUrl="/ABC"
}
}
},
new RoleModel
{
RoleName="B",
Apis=new List<OneApiModel>
{
new OneApiModel
{
ApiName="B",
ApiUrl="/B"
},
new OneApiModel
{
ApiName="AB",
ApiUrl="/AB"
},
new OneApiModel
{
ApiName="BC",
ApiUrl="/BC"
},
new OneApiModel
{
ApiName="ABC",
ApiUrl="/ABC"
}
}
},
new RoleModel
{
RoleName="A",
Apis=new List<OneApiModel>
{
new OneApiModel
{
ApiName="A",
ApiUrl="/A"
},
new OneApiModel
{
ApiName="AB",
ApiUrl="/AB"
},
new OneApiModel
{
ApiName="AC",
ApiUrl="/AC"
},
new OneApiModel
{
ApiName="ABC",
ApiUrl="/ABC"
}
}
}
};
foreach (var item in roles)
{
AddRole(item);
}
}
有了角色禾對應的API資訊,就要新增使用者了,
假設有 aa、bb、cc 三個使用者,密碼都是 123456,aa 屬於 A 角色, bb 屬於 B角色...
public void UpdateUser()
{
AddUser(new UserModel { UserName = "aa", BeRoles = new List<string> { "A" } });
AddUser(new UserModel { UserName = "bb", BeRoles = new List<string> { "B" } });
AddUser(new UserModel { UserName = "cc", BeRoles = new List<string> { "C" } });
}
為了能夠把角色和使用者載入進 CZGL.Auth ,你需要在程式啟動時,例如在 Program 裡,使用
RoleService roleService = new RoleService();
roleService.UpdateRole();
roleService.UpdateUser();
二、新增自定義事件
授權是,可能會有各種情況,你可以新增自定義事件記錄下使用者訪問的授權資訊、影響授權結果。
引用 using CZGL.Auth.Interface;
,
新增一個類 RoleEvents 繼承 IRoleEventsHadner
public class RoleEvents : IRoleEventsHadner
{
public async Task Start(HttpContext httpContext)
{
await Task.CompletedTask;
}
public void TokenEbnormal(object eventsInfo)
{
}
public void TokenIssued(object eventsInfo)
{
}
public void NoPermissions(object eventsInfo)
{
}
public void Success(object eventsInfo)
{
}
public async Task End(HttpContext httpContext)
{
await Task.CompletedTask;
}
}
在 CZGL.Auth 開始驗證授權前呼叫 Start,結束時呼叫 End,傳入傳引數是 HttpContext 型別,你可以在裡面新增自定義授權的資訊,在裡面可以影響請求管道。
其他幾個方法含義如下:
TokenEbnormal 客戶端攜帶的 Token 不是有效的 Jwt 令牌,將不能被解析
TokenIssued 令牌解碼後,issuer 或 audience不正確
NoPermissions 無權訪問此 API
在授權認證的各個階段將會呼叫上面的方法。
三、注入授權服務和中介軟體
使用 CZGL.Auth ,你需要注入以下兩個服務
services.AddRoleService(authOptions);
services.AddSingleton<IRoleEventsHadner, RoleEvents>();
AddRoleService
是注入授權服務,AddSingleton
注入你的事件。
AddRoleService 需要一個 AuthConfigModel 型別作引數。
你可以這樣配置
var authOptions = new AuthBuilder()
.Security("aaaafsfsfdrhdhrejtrjrt", "ASPNETCORE", "ASPNETCORE")
.Jump("accoun/login", "account/error", false, false)
.Time(TimeSpan.FromMinutes(20))
.InfoScheme(new CZGL.Auth.Models.AuthenticateScheme
{
TokenEbnormal = "Login authentication failed!",
TokenIssued = "Login authentication failed!",
NoPermissions = "Login authentication failed!"
}).Build();
services.AddRoleService(authOptions);
services.AddSingleton<IRoleEventsHadner, RoleEvents>();
Security 配置金鑰相關,引數分別是金鑰字串、頒發者、訂閱者。
Jump 配置授權失敗時,跳轉地址。引數分別是未授權時跳轉、授權無效跳轉,後面兩個 bool 可以設定跳轉或跳轉。
Time 配置 Token 有效期。
InfoScheme 授權失敗提示資訊,例如
上圖的是時間過期的提示訊息,使用者請求API失敗時返回 401 狀態碼,Header 會攜帶提示訊息,CZGL.Auth 裡面設定了三種情況下,自定義頭部:
TokenEbnormal 客戶端攜帶的 Token 不是有效的 Jwt 令牌,將不能被解析
TokenIssued 令牌解碼後,issuer 或 audience不正確
NoPermissions 無權訪問此 API
新增三個中介軟體
app.UseAuthentication();
app.UseAuthorization();
app.UseMiddleware<RoleMiddleware>();
app.UseAuthorization();
是微軟授權認證的中介軟體,CZGL.Auth 會先讓,預設的驗證管道過濾一些無效請求和認證資訊,再由 CZGL.Auth 來校驗授權。
三、如何設定API的授權
很簡單,CZGL.Auth 的認證授權,你只需在 Controller 或 Action上 新增 [Authorize]
。
CZGL.Auth 只會對使用了 [Authorize]
特性的 Controller 或 Action 生效。
如果一個 Controller 已經設定了 [Authorize]
,但是你想裡面的 Action 跳過授權認證,則使用 [AllowAnonymous]
修飾 Action。
使用方法跟微軟的預設的完全一致。這樣無需過多配置。
如果你想另外定義一個特性用來另外設定 授權的話,可以到我的倉庫提 Issue 或者直接聯絡我微信。
新增一個 APIController ,
[Authorize]
[Route("api/[controller]")]
[ApiController]
public class TestController : ControllerBase
{
[HttpGet("/A")]
public JsonResult A()
{
return new JsonResult(new { Code = 200, Message = "Success!" });
}
[HttpGet("/B")]
public JsonResult B()
{
return new JsonResult(new { Code = 200, Message = "Success!" });
}
[HttpGet("/C")]
public JsonResult C()
{
return new JsonResult(new { Code = 200, Message = "Success!" });
}
[HttpGet("/AB")]
public JsonResult AB()
{
return new JsonResult(new { Code = 200, Message = "Success!" });
}
[HttpGet("/BC")]
public JsonResult BC()
{
return new JsonResult(new { Code = 200, Message = "Success!" });
}
[HttpGet("/AC")]
public JsonResult AC()
{
return new JsonResult(new { Code = 200, Message = "Success!" });
}
[HttpGet("/ABC")]
public JsonResult ABC()
{
return new JsonResult(new { claims = User.Claims });
}
/// <summary>
/// 任何人都不能訪問
/// </summary>
/// <returns></returns>
[HttpGet("D")]
public JsonResult D()
{
return new JsonResult(new { Code = 200, Message = "Success!" });
}
[HttpGet("error")]
public JsonResult Denied()
{
return new JsonResult(
new
{
Code = 0,
Message = "訪問失敗!",
Data = "此賬號無權訪問!"
});
}
}
四、新增登入頒發 Token
新增一個 AccountController.cs 用來頒發登入、 Token。
[Route("api/[controller]")]
[ApiController]
public class AccountController : ControllerBase
{
[HttpPost("/Login")]
public async Task<JsonResult> Login([FromQuery]string username, string password, string rolename)
{
// 使用者名稱密碼是否正確
if (string.IsNullOrWhiteSpace(username) || string.IsNullOrWhiteSpace(password) || string.IsNullOrWhiteSpace(rolename))
{
return new JsonResult(new
{
Code = 0,
Message = "尼瑪,上傳什麼垃圾資訊",
});
}
if(!((username=="aa"||username=="bb"||username=="cc")&&password=="123456"))
{
return new JsonResult(new
{
Code = 0,
Message = "賬號或密碼錯誤",
});
}
// 你自己定義的角色/使用者資訊服務
RoleService roleService = new RoleService();
// 檢驗使用者是否屬於此角色
var role = roleService.IsUserToRole(username,rolename);
// CZGL.Auth 中一個用於加密解密的類
EncryptionHash hash = new EncryptionHash();
// 設定使用者標識
var userClaims = hash.BuildClaims(username, rolename);
//// 自定義構建配置使用者標識
/// 自定義的話,至少包含如下標識
//var userClaims = new Claim[]
//{
//new Claim(ClaimTypes.Name, userName),
// new Claim(ClaimTypes.Role, roleName),
// new Claim(JwtRegisteredClaimNames.Aud, Audience),
// new Claim(ClaimTypes.Expiration, TimeSpan.TotalSeconds.ToString()),
// new Claim(JwtRegisteredClaimNames.Iat, new DateTimeOffset(DateTime.Now).ToUnixTimeSeconds().ToString())
//};
/*
iss (issuer):簽發人
exp (expiration time):過期時間
sub (subject):主題
aud (audience):受眾
nbf (Not Before):生效時間
iat (Issued At):簽發時間
jti (JWT ID):編號
*/
// 方法一,直接頒發 Token
ResponseToken token = hash.BuildToken(userClaims);
//方法二,拆分多步,頒發 token,方便除錯
//var identity = hash.GetIdentity(userClaims);
//var jwt = hash.BuildJwtToken(userClaims);
//var token = hash.BuildJwtResponseToken(jwt);
return new JsonResult(token);
}
}
五、部分說明
注入 Jwt 服務、頒發 Token
CZGL.Auth 把使用 jwt 的服務和頒發 Token 的程式碼封裝好了,這個庫不是在“造輪子”,所以實際上你可以很輕鬆的把這部分的程式碼抽出來,另外設計。
這部分的程式碼所在位置 RoleServiceExtension.cs 、EncryptionHash.cs。
授權中介軟體
app.UseAuthentication();
app.UseAuthorization();
app.UseMiddleware<RoleMiddleware>();
我的寫法是利用 ASP.NET Core 的 jwt 完成基礎的認證授權,然後在下一個管道中實現拓展的認證。但是本身的認證是在 app.UseAuthorization(); 做了拓展,所以使用 CZGL.Auth,只需要按照平常 jwt 的方式去使用,只是加了一個 RoleMiddleware 中介軟體。
CZGL.Auth 只是我受到新思路啟發臨時寫出來的。。。最好不要由於生產,去 github 庫把專案下載下來,按照自己應用場景改一下~。
六、驗證
先使用 aa 使用者登入,登入時選擇 A 角色。
因為 A 使用者只能訪問 “帶有 A ” 的API, "/A"、"/AB" 等,所以我們可以試試。
繼續用這個 Token 訪問一下 "/B"
可以繼續嘗試新增 API 或者使用其他使用者登入,訪問不同的 API。
由於別人對前端不熟,所以就不寫帶頁面的示例了~。
可以用 Postman 就行測試。
什麼示例的 專案可以到倉庫裡下載,名稱是 MyAuth。
一般上,使用者許可權、角色許可權資訊是儲存在資料庫裡面的,另一個示例是 CZGL.Auth.Sample2。
這個庫只是較為粗略的授權認證,與更豐富的需求請自行下載原始碼修改~
有問題要討論,可以在俱樂部裡面找到我。
深圳、廣州、長沙、上海的群等我都在,嘿嘿嘿,嘿嘿