ASP.NET Core 认证与授权[1]:初识认证
在ASP.NET 4.X 中,我們最常用的是Forms認證,它既可以用于局域網環境,也可用于互聯網環境,有著非常廣泛的使用。但是它很難進行擴展,更無法與第三方認證集成,因此,在 ASP.NET Core 中對認證與授權進行了全新的設計,并使用基于聲明的認證(claims-based authentication),以適應現代化應用的需求。在運行原理解剖[5]:Authentication中介紹了一下HttpContext與認證系統的集成,本系列文章則來詳細介紹一下 ASP.NET Core 中認證與授權。
目錄
- Claim
- ClaimsIdentity
- ClaimsPrincipal
- AuthenticationTicket
- Usage
- AddAuthentication
- AddScheme
- AddRemoteScheme
- UseAuthentication
- AuthenticationHandler
- RemoteAuthenticationHandler
基于聲明的認證
Claim 通常被翻譯成聲明,但是感覺過于生硬,還是使用Claim來稱呼更加自然一些。記得是在MVC5中,第一次接觸到 “Claim" 的概念。在MVC5之前,我們所熟悉的是Windows認證和Forms認證,Windows認證通常用于企業內部,我們使用最多的還是Forms認證,先來回顧一下,以前是怎么使用的:
首先我們會在web.config中配置認證模式:
<authentication mode="Forms"><forms loginUrl="~/Account/LogOn" timeout="2880" /> </authentication>認證票據的生成是使用FormsAuthentication來完成的:
FormsAuthentication.SetAuthCookie("bob", true);然后便可以通過HttpContext.User.Identity.Name獲取到當前登錄用戶的名稱:"bob",那么它是如何來完成認證的呢?
在 ASP.NET 4.x 中,我們應該都對 HttpModule 比較了解,它類似于 ASP.NET Core 中的中件間,ASP.NET 默認會在全局的 administration.config 文件中注冊一大堆HttpModule,其中就包括WindowsAuthentication和FormsAuthentication,用來實現Windows認證和Forms認證:
<moduleProviders><!-- Server Modules--><add name="Authentication" type="Microsoft.Web.Management.Iis.Authentication.AuthenticationModuleProvider, Microsoft.Web.Management.Iis, Version=10.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" /><add name="AnonymousAuthentication" type="Microsoft.Web.Management.Iis.Authentication.AnonymousAuthenticationModuleProvider, Microsoft.Web.Management.Iis, Version=10.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" /><add name="BasicAuthentication" type="Microsoft.Web.Management.Iis.Authentication.BasicAuthenticationModuleProvider, Microsoft.Web.Management.Iis, Version=10.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" /><add name="ActiveDirectoryAuthentication" type="Microsoft.Web.Management.Iis.Authentication.ActiveDirectoryAuthenticationModuleProvider, Microsoft.Web.Management.Iis, Version=10.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" /><add name="WindowsAuthentication" type="Microsoft.Web.Management.Iis.Authentication.WindowsAuthenticationModuleProvider, Microsoft.Web.Management.Iis, Version=10.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" /><add name="DigestAuthentication" type="Microsoft.Web.Management.Iis.Authentication.DigestAuthenticationModuleProvider, Microsoft.Web.Management.Iis, Version=10.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" /><!-- ASP.NET Modules--><add name="FormsAuthentication" type="Microsoft.Web.Management.AspNet.Authentication.FormsAuthenticationModuleProvider, Microsoft.Web.Management.Aspnet, Version=10.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />可能大多人都不知道有這些Module,這也是微軟技術的一大弊端,總想著封裝成傻瓜化,造成入門容易,精通太難的局面。
如上,我們可以看到生成票據時,默認只能轉入一個Name,當然也可以通過手動創建FormsAuthenticationTicket來附帶一些額外的信息,但是都太過麻煩。
在傳統的身份認證中,每個應用程序都有它自己的驗證用戶身份的方式,以及它自己的用戶數據庫。這種方式有很大的局限性,因為它很難集成多種認證方式以支持用戶使用不同的方式來訪問我們的應用程序,比如組織內的用戶(Windows-baseed 認證),其它組織的用戶(Identity federation)或者是來自互聯網的用戶(Forms-based 認證)等等。
而Claim 是關于一個人或組織的某個主題的陳述,比如:一個人的名稱,角色,個人喜好,種族,特權,社團,能力等等。它本質上就是一個鍵值對,是一種非常通用的保存用戶信息的方式,可以很容易的將認證和授權分離開來,前者用來表示用戶是/不是什么,后者用來表示用戶能/不能做什么。
因此基于聲明的認證有兩個主要的特點:
將認證與授權拆分成兩個獨立的服務。
在需要授權的服務中,不用再去關心你是如何認證的,你用Windows認證也好,Forms認證也行,只要你出示你的 Claims 就行了。
ASP.NET Core 中的用戶身份
Claim
在 ASP.NET Core 中,使用Cliam類來表示用戶身份中的一項信息,它由核心的Type和Value屬性構成:
public class Claim {private readonly string _type;private readonly string _value;public Claim(string type, string value): this(type, value, ClaimValueTypes.String, ClaimsIdentity.DefaultIssuer, ClaimsIdentity.DefaultIssuer, null, null, null){}internal Claim(string type, string value, string valueType, string issuer, string originalIssuer, ClaimsIdentity subject, string propertyKey, string propertyValue){...}public string Type => _type;public string Value => _value; }一個Claim可以是“用戶的姓名”,“郵箱地址”,“電話”,等等,而多個Claim構成一個用戶的身份,使用ClaimsIdentity類來表示:
ClaimsIdentity
public class ClaimsIdentity : IIdentity { public virtual IEnumerable<Claim> Claims {get;}public virtual string AuthenticationType => _authenticationType;public virtual bool IsAuthenticated => !string.IsNullOrEmpty(_authenticationType);public virtual string Name{get{Claim claim = FindFirst(_nameClaimType);if (claim != null) return claim.Value;return null;}}}如上,其Name屬性用來查找Claims中,第一個Type為我們創建ClaimsIdentity時指定的NameClaimType的Claim的值,若未指定Type時則使用默認的ClaimTypes.Name。而IsAuthenticated只是判斷_authenticationType是否為空,_authenticationType則對應上一章中介紹的Scheme。
下面,我們演示一下用戶身份的創建:
// 創建一個用戶身份,注意需要指定AuthenticationType,否則IsAuthenticated將為false。 var claimIdentity = new ClaimsIdentity("myAuthenticationType"); // 添加幾個Claim claimIdentity.AddClaim(new Claim(ClaimTypes.Name, "bob")); claimIdentity.AddClaim(new Claim(ClaimTypes.Email, "bob@gmail.com")); claimIdentity.AddClaim(new Claim(ClaimTypes.MobilePhone, "18888888888"));如上,我們可以根據需要添加任意個的Claim,最后我們還需要再將用戶身份放到ClaimsPrincipal對象中。
ClaimsPrincipal
那么,ClaimsPrincipal是什么呢?在 ASP.NET 4.x 中我們可能對IPrincipal接口比較熟悉,在Controller中的User屬性便是IPrincipal類型:
public interface IPrincipal {IIdentity Identity { get; }bool IsInRole(string role); }可以看到IPrincipal除了包含用戶身份外,還有一個IsInRole方法,用于判斷用戶是否屬于指定角色,在基于角色的授權當中便是調用此方法來實現的。
而在 ASP.NET Core 中,HttpContext直接使用的就是ClaimsPrincipal類型,而不再使用IPrincipal。
public abstract class HttpContext {public abstract ClaimsPrincipal User { get; set; } }而在ClaimsPrincipal中,可以包含多個用戶身份(ClaimsIdentity),除了對用戶身份的操作,還提供了針對Claims的查詢:
public class ClaimsPrincipal : IPrincipal {private readonly List<ClaimsIdentity> _identities = new List<ClaimsIdentity>();public ClaimsPrincipal(IEnumerable<ClaimsIdentity> identities) {_identities.AddRange(identities);}// 默認從_identities中查找第一個不為空的ClaimsIdentity,也可以自定義查找方式。public virtual System.Security.Principal.IIdentity Identity {}// 查找_identities中是否包含類型為RoleClaimType(在創建ClaimsIdentity時指定,或者默認的ClaimTypes.Role)的Claim。public virtual bool IsInRole(string role) {}// 獲取所有身份的Claim集合public virtual IEnumerable<Claim> Claims{get{foreach (ClaimsIdentity identity in Identities){foreach (Claim claim in identity.Claims){yield return claim;}}}} }ClaimsPrincipal的創建非常簡單,只需傳入我們上面創建的用戶身份即可:
var principal = new ClaimsPrincipal(claimIdentity);由于HTTP是無狀態的,我們通常使用Cookie,請求頭或請求參數等方式來附加用戶的信息,在網絡上進行傳輸,這就涉及到序列化和安全方面的問題。因此,還需要將principal對象包裝成AuthenticationTicket對象。
AuthenticationTicket
當我們創建完ClaimsPrincipal對象后,需要將它生成一個用戶票據并頒發給用戶,然后用戶拿著這個票據,便可以訪問受保持的資源,而在 ASP.NET Core 中,用戶票據用AuthenticationTicket來表示,如在Cookie認證中,其認證后的Cookie值便是對該對象序列化后的結果,它的定義如下:
public class AuthenticationTicket {public AuthenticationTicket(ClaimsPrincipal principal, AuthenticationProperties properties, string authenticationScheme){AuthenticationScheme = authenticationScheme;Principal = principal;Properties = properties ?? new AuthenticationProperties();}public AuthenticationTicket(ClaimsPrincipal principal, string authenticationScheme) : this(principal, properties: null, authenticationScheme: authenticationScheme) { }public string AuthenticationScheme { get; private set; }public ClaimsPrincipal Principal { get; private set; }public AuthenticationProperties Properties { get; private set; } }用戶票據除了包含上面創建的principal對象外,還需要指定一個AuthenticationScheme (通常在授權中用來驗證Scheme),并且還包含一個AuthenticationProperties對象,它主要是一些用戶票據安全方面的一些配置,如過期時間,是否持久等。
var properties = new AuthenticationProperties(); var ticket = new AuthenticationTicket(principal, properties, "myScheme"); // 加密 序列化 var token = Protect(ticket);最后,我們可以將票據(token)寫入到Cookie中,或是也可以以JSON的形式返回讓客戶端自行保存,由于我們對票據進行了加密,可以保證在網絡中安全的傳輸而不會被篡改。
最終身份令牌的結構大概是這樣的:
Microsoft.AspNetCore.Authentication
上面,我們介紹了身份票據的創建過程,下面就來介紹一下 ASP.NET Core 中的身份認證。
ASP.NET Core 中的認證系統具體實現在 Security 項目中,它包含 Cookie, JwtBearer, OAuth, OpenIdConnect 等:
認證系統提供了非常靈活的擴展,可以讓我們很容易的實現自定義認證方式。
Usage
而對于認證系統的配置,分為兩步,也是我們所熟悉的注冊服務和配置中間件:
首先,在DI中注冊服務認證所需的服務:
public void ConfigureServices(IServiceCollection services) {services.AddAuthentication(options =>{options.DefaultAuthenticateScheme = CookieAuthenticationDefaults.AuthenticationScheme;options.DefaultSignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;options.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;}).AddCookie().AddOpenIdConnect(o =>{o.ClientId = "server.hybrid";o.ClientSecret = "secret";o.Authority = "https://demo.identityserver.io/";o.ResponseType = OpenIdConnectResponseType.CodeIdToken;}); }最后,注冊認證中間件:
public void Configure(IApplicationBuilder app) {app.UseAuthentication(); }如上,我們的系統便支持了Cookie和JwtBearer兩種認證方式,是不是非常簡單,在我們的應用程序中使用認證系統時,只需要調用 上一章 介紹的 HttpContext 中認證相關的擴展方法即可。
Microsoft.AspNetCore.Authentication,是所有認證實現的公共抽象類,它定義了實現認證Handler的規范,并包含一些共用的方法,如令牌加密,序列化等,AddAuthentication 便是其提供的統一的注冊認證服務的擴展方法:
AddAuthentication
public static AuthenticationBuilder AddAuthentication(this IServiceCollection services) {services.AddAuthenticationCore();services.AddDataProtection();services.AddWebEncoders();services.TryAddSingleton<ISystemClock, SystemClock>();return new AuthenticationBuilder(services); }public static AuthenticationBuilder AddAuthentication(this IServiceCollection services, Action<AuthenticationOptions> configureOptions) {var builder = services.AddAuthentication();services.Configure(configureOptions);return builder; }如上,它首先會調用上一章中介紹的AddAuthenticationCore方法,然后注冊了DataProtection和WebEncoders兩個服務。而對 AuthenticationOptions 我們之前在IAuthenticationSchemeProvider也介紹過,它用來配置Scheme。
AddScheme
在上面的 AddAuthentication 中返回的是一個AuthenticationBuilder類型,所有認證Handler的注冊都是以它的擴展形式來實現的,它同時也提供了AddScheme擴展方法,使我們可以更加方便的來配置Scheme:
public class AuthenticationBuilder {public AuthenticationBuilder(IServiceCollection services)=> Services = services;public virtual IServiceCollection Services { get; }public virtual AuthenticationBuilder AddScheme<TOptions, THandler>(string authenticationScheme, Action<TOptions> configureOptions)where TOptions : AuthenticationSchemeOptions, new()where THandler : AuthenticationHandler<TOptions>=> AddScheme<TOptions, THandler>(authenticationScheme, displayName: null, configureOptions: configureOptions);public virtual AuthenticationBuilder AddScheme<TOptions, THandler>(string authenticationScheme, string displayName, Action<TOptions> configureOptions)where TOptions : AuthenticationSchemeOptions, new()where THandler : AuthenticationHandler<TOptions>{Services.Configure<AuthenticationOptions>(o =>{o.AddScheme(authenticationScheme, scheme => {scheme.HandlerType = typeof(THandler);scheme.DisplayName = displayName;});});if (configureOptions != null){Services.Configure(authenticationScheme, configureOptions);}Services.AddTransient<THandler>();return this;} }在這里的AddScheme 擴展方法只是封裝了對AuthenticationOptions中AddScheme的調用,如上面示例中的AddCookie便是調用該擴展方法來實現的。
AddRemoteScheme
看到 Remote 我們應該就可以猜到它是一種遠程驗證方式,先看一下它的定義:
public class AuthenticationBuilder {public virtual AuthenticationBuilder AddRemoteScheme<TOptions, THandler>(string authenticationScheme, string displayName, Action<TOptions> configureOptions)where TOptions : RemoteAuthenticationOptions, new()where THandler : RemoteAuthenticationHandler<TOptions>{Services.TryAddEnumerable(ServiceDescriptor.Singleton<IPostConfigureOptions<TOptions>, EnsureSignInScheme<TOptions>>());return AddScheme<TOptions, THandler>(authenticationScheme, displayName, configureOptions: configureOptions);}private class EnsureSignInScheme<TOptions> : IPostConfigureOptions<TOptions> where TOptions : RemoteAuthenticationOptions{private readonly AuthenticationOptions _authOptions;public EnsureSignInScheme(IOptions<AuthenticationOptions> authOptions){_authOptions = authOptions.Value;}public void PostConfigure(string name, TOptions options){options.SignInScheme = options.SignInScheme ?? _authOptions.DefaultSignInScheme ?? _authOptions.DefaultScheme;if (string.Equals(options.SignInScheme, name, StringComparison.Ordinal)){throw new InvalidOperationException(Resources.Exception_RemoteSignInSchemeCannotBeSelf);}}} }首先使用PostConfigure模式(參見:Options[1]:Configure),對RemoteAuthenticationOptions進行驗證,要求遠程驗證中指定的SignInScheme不能為自身,這是為什么呢?后文再來解釋。然后便是直接調用上面介紹的 AddScheme 方法。
關于遠程驗證相對比較復雜,在本章中并不會太過深入的來介紹,在后續其它文章中會逐漸深入。
UseAuthentication
在上面,注冊認證中間件時,我們只需調用一個UseAuthentication擴展方法,因為它會執行我們注冊的所有認證Handler:
public static IApplicationBuilder UseAuthentication(this IApplicationBuilder app) {return app.UseMiddleware<AuthenticationMiddleware>(); }咦,它的代碼好簡單,只是注冊了一個 AuthenticationMiddleware 而已,迫不及待的想看看它的實現:
public class AuthenticationMiddleware {private readonly RequestDelegate _next;public IAuthenticationSchemeProvider Schemes { get; set; }public async Task Invoke(HttpContext context){context.Features.Set<IAuthenticationFeature>(new AuthenticationFeature{OriginalPath = context.Request.Path,OriginalPathBase = context.Request.PathBase});var handlers = context.RequestServices.GetRequiredService<IAuthenticationHandlerProvider>();foreach (var scheme in await Schemes.GetRequestHandlerSchemesAsync()){var handler = await handlers.GetHandlerAsync(context, scheme.Name) as IAuthenticationRequestHandler;if (handler != null && await handler.HandleRequestAsync()){return;}}var defaultAuthenticate = await Schemes.GetDefaultAuthenticateSchemeAsync();if (defaultAuthenticate != null){var result = await context.AuthenticateAsync(defaultAuthenticate.Name);if (result?.Principal != null){context.User = result.Principal;}}await _next(context);} }很簡單,但是很強大,不管我們是使用Cookie認證,還是Bearer認證,等等,都只需要這一個中間件,因為它會解析所有的Handler來執行。
不過,在這里,這會先判斷是否具體實現了IAuthenticationRequestHandler的Hander,優先來執行,這個是什么鬼?
查了一下,發現IAuthenticationRequestHandler是在HttpAbstractions中定義的,只是在運行原理解剖[5]:Authentication中沒有介紹到它:
public interface IAuthenticationRequestHandler : IAuthenticationHandler {Task<bool> HandleRequestAsync(); }它多了一個HandleRequestAsync方法,那么它存在的意義是什么呢?其實在Cookie認證中并沒有用到它,它通常在遠程認證(如:OAuth, OIDC等)中使用,下文再來介紹。
繼續分析上面代碼,通過調用Schemes.GetDefaultAuthenticateSchemeAsync來獲取到認證的Scheme,也就是上文提到的問題,我們必須指定默認的Scheme。
最后,調用AuthenticateAsync方法進行認證,認證成功后,為HttpContext.User賦值,至于如何解析身份令牌生成ClaimsPrincipal對象,則交給相應的Handler來處理。
認證Handler
上文中多次提到認證Handler,它由統一的AuthenticationMiddleware來調用,負責具體的認證實現,并分為本地認證與遠程認證兩種方式。
在本地驗證中,身份令牌的發放與認證通常是由同一個服務器來完成,這也是我們比較熟悉的場景,對于Cookie, JwtBearer等認證來說,都屬于是本地驗證。而當我們使用OAuth, OIDC等驗證方式時,身份令牌的發放則是由獨立的服務或是第三方(QQ, Weibo 等)認證來提供,此時在我們的應用程序中獲取身份令牌時需要請求遠程服務器,因此稱之為遠程驗證。
AuthenticationHandler
AuthenticationHandler是所有認證Handler的抽象基類,對于本地認證直接實現該類即可,定義如下:
public abstract class AuthenticationHandler<TOptions> : IAuthenticationHandler where TOptions : AuthenticationSchemeOptions, new() {...public async Task InitializeAsync(AuthenticationScheme scheme, HttpContext context){...await InitializeEventsAsync();await InitializeHandlerAsync();}protected virtual async Task InitializeEventsAsync() { }protected virtual Task<object> CreateEventsAsync() => Task.FromResult(new object());protected virtual Task InitializeHandlerAsync() => Task.CompletedTask;public async Task<AuthenticateResult> AuthenticateAsync(){var result = await HandleAuthenticateOnceAsync();...}protected Task<AuthenticateResult> HandleAuthenticateOnceAsync(){if (_authenticateTask == null){_authenticateTask = HandleAuthenticateAsync();}return _authenticateTask;}protected abstract Task<AuthenticateResult> HandleAuthenticateAsync();protected virtual Task HandleForbiddenAsync(AuthenticationProperties properties){Response.StatusCode = 403;return Task.CompletedTask;}protected virtual Task HandleChallengeAsync(AuthenticationProperties properties){Response.StatusCode = 401;return Task.CompletedTask;}... }如上,它定義一個抽象方法HandleAuthenticateAsync,并使用HandleAuthenticateOnceAsync方法來保證其在每次認證只執行一次。而HandleAuthenticateAsync是認證的核心,交給具體的認證Handler負責實現。而對于 ChallengeAsync, ForbidAsync 等方法也提供了默認的實現。
而對于HandleAuthenticateAsync的實現,大致的邏輯就是從請求中獲取上面發放的身份令牌,然后解析成AuthenticationTicket,并經過一系列的驗證,最終返回ClaimsPrincipal對象。
RemoteAuthenticationHandler
RemoteAuthenticationHandler 便是所有遠程認證的抽象基類了,它繼承自AuthenticationHandler,并實現了IAuthenticationRequestHandler接口:
public abstract class RemoteAuthenticationHandler<TOptions> : AuthenticationHandler<TOptions>, IAuthenticationRequestHandlerwhere TOptions : RemoteAuthenticationOptions, new() {public virtual Task<bool> ShouldHandleRequestAsync() => Task.FromResult(Options.CallbackPath == Request.Path);public virtual async Task<bool> HandleRequestAsync(){if (!await ShouldHandleRequestAsync()){return false;}var authResult = await HandleRemoteAuthenticateAsync();...await Context.SignInAsync(SignInScheme, ticketContext.Principal, ticketContext.Properties);if (string.IsNullOrEmpty(ticketContext.ReturnUri)) ticketContext.ReturnUri = "/";Response.Redirect(ticketContext.ReturnUri);return true;}protected abstract Task<HandleRequestResult> HandleRemoteAuthenticateAsync();protected override async Task<AuthenticateResult> HandleAuthenticateAsync(){var result = await Context.AuthenticateAsync(SignInScheme);...}protected override Task HandleForbiddenAsync(AuthenticationProperties properties)=> Context.ForbidAsync(SignInScheme);protected virtual void GenerateCorrelationId(AuthenticationProperties properties) {}protected virtual bool ValidateCorrelationId(AuthenticationProperties properties) {} }在上面介紹的AuthenticationMiddleware中,提到它會先執行實現了IAuthenticationRequestHandler 接口的Handler(遠程認證),之后(若未完成認證)再執行本地認證Handler。
而RemoteAuthenticationHandler中核心的認證邏輯便是 HandleRequestAsync 方法,它主要包含2個步驟:
首先執行一個抽象方法HandleRemoteAuthenticateAsync,由具體的Handler來實現,該方法返回的HandleRequestResult對象包含驗證的結果(跳過,失敗,成功等),在成功時會包含一個ticket對象。
若上一步驗證成功,則根據返回的ticket,獲取到ClaimsPrincipal對象,并調用其它認證Handler的Context.SignInAsync方法。
也就是說,遠程Hander會在用戶未登錄時,指引用戶跳轉到認證服務器,登錄成功后,解析認證服務器傳回的憑證,最終依賴于本地Handler來保存身份令牌。當用戶再次訪問則無需經過遠程Handler,直接交給本地Handler來處理。
由此也可以知道,遠程認證中本身并不具備SignIn的能力,所以必須通過指定其它SignInScheme交給本地認證來完成 SignIn。
對于其父類的HandleAuthenticateAsync抽象方法則定義了一個默認實現:“直接轉交給本地驗證來處理”。當我們需要定義自己的遠程認證方式時,通常只需實現 HandleRemoteAuthenticateAsync 即可,而不用再去處理 HandleAuthenticateAsync 。
總結
基于聲明的認證并不是微軟所特有的,它在國外被廣泛的使用,如微軟的ADFS,Google,Facebook,Twitter等等。在基于聲明的認證中,對認證和授權進行了明確的區分,認證用來頒發一個用戶的身份標識,其包含這個用戶的基本信息,而對于這個身份的頒發則由我們信任的第三方機構來(STS)頒發(當然,你也可以自己來頒發)。而授權,則是通過獲取身份標識中的信息,來判斷該用戶能做什么,不能做什么。
本文對 ASP.NET Core 中認證系統的整個流程做了一個簡要的介紹,可能會比較苦澀難懂,不過沒關系,大致有個印象就好,下一章則詳細介紹一下最常用的本地認證方式:Cookie認證,后續也會詳細介紹 OIDC 的用法與實現,到時再回頭來看本文或許會豁然開朗。
轉載于:https://www.cnblogs.com/RainingNight/p/introduce-basic-authentication-in-asp-net-core.html
創作挑戰賽新人創作獎勵來咯,堅持創作打卡瓜分現金大獎總結
以上是生活随笔為你收集整理的ASP.NET Core 认证与授权[1]:初识认证的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Android 监听网络连接状态,判断网
- 下一篇: XML注释