.NET CORE 鑒權
基礎資訊
1.什麼是鑒權授權?
- 鑒權是驗證用戶是否擁有訪問系統的權利,授權是判斷用戶是否有許可權做一些其他操作。
2.傳統的Session 和Cookie
- 主要用於無狀態請求下的的用戶身份識別,只不過Session將資訊存儲在服務端,Cookie將資訊存儲在客戶端。
Session
-
在客戶端第一次進行訪問時,服務端會生成一個Session id返回到客戶端
-
客戶端將Session id存儲在本地後續每一次請求都帶上這個id
-
服務端從接收到的請求中根據Session id在自己存儲的資訊中識別客戶端
Cookie
-
在客戶端訪問伺服器時,服務端會在響應中頒發一個Cookie
-
客戶端會把cookie存儲,當再訪問服務端時會將cookie和請求一併提交
-
服務端會檢查cookie識別客戶端,並也可以根據需要修改cookie的內容
3.存在的問題
在分散式或集群系統中使用Session
假設現在伺服器為了更好的承載和容災將系統做了分散式和集群,也就是有了N個服務端,那是不是每一個服務端都要具有對每一個客戶端的Session或者Cookie的識別能力呢?
這個可以使用Session共享的方式用於Session的識別,但是這並不能解決分散式系統下依然存在這個問題,因為通常每一個分散式系統都由不同的人負責或者跨網路,甚至不同的公司,不可能全部都做session共享吧?這個時候就誕生了一個新的方式,使用Token
4.Token
- Token是服務端生成的一串字元串,以作客戶端進行請求的一個令牌。
執行步驟
-
用戶向統一的鑒權授權系統發起用戶名和密碼的校驗
-
校驗通過後會頒發一個Token,用戶就拿著頒發的Token去訪問其他三方系統
-
三方系統可以直接請求鑒權授權系統驗證當前Token的合法性,也可以根據對稱加密使用秘鑰解密Token以驗證合法性
.NET Core中鑒權
-
Authentication:
鑒定身份資訊,例如用戶有沒有登錄,用戶基本資訊 -
Authorization:
判定用戶有沒有許可權
1.NET Core鑒權授權基本概念
在NETCORE中鑒權授權是通過AuthenticationHttpContextExtensions擴展類中的實現的HttpContext的擴展方法來完成的
public static class AuthenticationHttpContextExtensions
{
public static Task SignInAsync(this HttpContext context, string scheme, ClaimsPrincipal principal, AuthenticationProperties properties)
{
context.RequestServices.GetRequiredService<IAuthenticationService>().SignInAsync(context, scheme, principal, properties);
}
}
它真正的核心在
Microsoft.AspNetCore.Authorization模組,整個流程處理主要包含如下幾個關鍵類
IAuthenticationHandlerProvider
負責對用戶憑證的驗證,提供IAuthenticationHandler處理器給IAuthenticationService用於處理鑒權請求,當然可以自定義處理器
IAuthenticationSchemeProvider
選擇標識使用的是哪種認證方式
IAuthenticationService
提供鑒權統一認證的5個核心業務介面
public interface IAuthenticationService
{
//查詢鑒權
Task<AuthenticateResult> AuthenticateAsync(HttpContext context, string scheme);
//登錄寫入鑒權憑證
Task SignInAsync(HttpContext context, string scheme, ClaimsPrincipal principal, AuthenticationProperties properties);
//退出登錄清理憑證
Task SignOutAsync(HttpContext context, string scheme, AuthenticationProperties properties);
Task ChallengeAsync(HttpContext context, string scheme, AuthenticationProperties properties);
Task ForbidAsync(HttpContext context, string scheme, AuthenticationProperties properties);
}
在它的實現類AuthenticationService中的SignInAsync方法
配合IAuthenticationHandlerProvider 和IAuthenticationSchemeProvider得到一個IAuthenticationHandler,最終將鑒權寫入和讀取都由它完成
public virtual async Task SignInAsync(HttpContext context, string scheme, ClaimsPrincipal principal, AuthenticationProperties properties)
{
if (scheme == null)
{
//IAuthenticationSchemeProvider實例
var defaultScheme = await Schemes.GetDefaultSignInSchemeAsync();
scheme = defaultScheme?.Name;
}
//IAuthenticationHandlerProvider實例獲取處理器
var handler = await Handlers.GetHandlerAsync(context, scheme);
var signInHandler = handler as IAuthenticationSignInHandler;
//各自的處理器handler
//例如使用Cookie 就會注入一個CookieAuthenticationHandler
//使用JWT 就注入一個JwtBearerHandler
await signInHandler.SignInAsync(principal, properties);
}
2.使用Cookie默認流程鑒權
- 使用中間件加入管道,用於找到鑒權HttpContext.AuthenticateAsync()
//核心源碼就是AuthenticationMiddleware中間件
app.UseAuthentication();
- 注入容器,將CookieAuthenticationHandler作為處理邏輯
services.AddAuthentication(options =>
{
//CookieAuthenticationDefaults.AuthenticationScheme == "Cookies"
options.DefaultAuthenticateScheme = "Cookies";
options.DefaultSignInScheme = "Cookies";
}).AddCookie();
- 在登錄時寫入憑證
-
Claims:一項資訊,例如工牌的姓名是一個Claims ,工牌號碼也是一個Claims
-
ClaimsIdentity:一組Claims 組成的資訊,就是一個用戶身份資訊
-
ClaimsPrincipal:一個用戶有多個身份
-
AuthenticationTicket:用戶票據,用於包裹ClaimsPrincipal
[AllowAnonymous]
public async Task<IActionResult> Login(string name, string password)
{
if(name!="Admin" && password!="000000")
{
var result = new JsonResult(new{ Result = false,Message = "登錄失敗"});
return result;
}
//Claims ⫋ ClaimsIdentity ⫋ ClaimsPrincipal
var claimIdentity = new ClaimsIdentity("ClaimsIdentity");
claimIdentity.AddClaim(new Claim(ClaimTypes.Name, name));
claimIdentity.AddClaim(new Claim(ClaimTypes.Address, "地址資訊"));
AuthenticationProperties ap = new AuthenticationProperties();
ClaimsPrincipal claimsPrincipal = new ClaimsPrincipal(claimIdentity);
await base.HttpContext.SignInAsync("Cookies",claimsPrincipal , ap)
return new JsonResult(new{ Result = false,Message = "登錄成功"});
}
4.在其他控制器上標記[Authorize]特性,在訪問介面框架會自動進行鑒權並將身份資訊寫入上下文
-
[AllowAnonymous]
:匿名可訪問 -
[Authorize]
:必須登錄才可訪問
3.自定義IAuthenticationHandler
- 實現IAuthenticationHandler, IAuthenticationSignInHandler, IAuthenticationSignOutHandler三個介面
public class CoreAuthorizationHandler : IAuthenticationHandler
,IAuthenticationSignInHandler, IAuthenticationSignOutHandler
{
public AuthenticationScheme Scheme { get; private set; }
protected HttpContext Context { get; private set; }
public Task InitializeAsync(AuthenticationScheme scheme, HttpContext context)
{
Scheme = scheme;
Context = context;
return Task.CompletedTask;
}
public async Task<AuthenticateResult> AuthenticateAsync()
{
var cookie = Context.Request.Cookies["CustomCookie"];
if (string.IsNullOrEmpty(cookie))
{
return AuthenticateResult.NoResult();
}
AuthenticateResult result = AuthenticateResult
.Success(Deserialize(cookie));
return await Task.FromResult(result);
}
public Task ChallengeAsync(AuthenticationProperties properties)
{
return Task.CompletedTask;
}
public Task ForbidAsync(AuthenticationProperties properties)
{
Context.Response.StatusCode = 403;
return Task.CompletedTask;
}
public Task SignInAsync(ClaimsPrincipal user, AuthenticationProperties properties)
{
var ticket = new AuthenticationTicket(user, properties, Scheme.Name);
Context.Response.Cookies.Append("CoreAuthorizationHandlerCookies", Serialize(ticket));
return Task.CompletedTask;
}
public Task SignOutAsync(AuthenticationProperties properties)
{
Context.Response.Cookies.Delete("CoreAuthorizationHandlerCookies");
return Task.CompletedTask;
}
private AuthenticationTicket Deserialize(string content)
{
byte[] byteTicket = System.Text.Encoding.Default.GetBytes(content);
return TicketSerializer.Default.Deserialize(byteTicket);
}
private string Serialize(AuthenticationTicket ticket)
{
//需要引入 Microsoft.AspNetCore.Authentication
byte[] byteTicket = TicketSerializer.Default.Serialize(ticket);
return Encoding.Default.GetString(byteTicket);
}
}
- 在容器中註冊自定義的Handler
services.AddAuthenticationCore(options =>
{
options.AddScheme<CoreMvcAuthenticationHandler>("AuthenticationScheme", "AuthenticationScheme");
});