ASP.NET Core 运行原理解剖[5]:Authentication
在現代應用程序中,認證已不再是簡單的將用戶憑證保存在瀏覽器中,而要適應多種場景,如App,WebAPI,第三方登錄等等。在 ASP.NET 4.x 時代的Windows認證和Forms認證已無法滿足現代化的需求,因此在ASP.NET Core 中對認證及授權進行了全新設計,使其更加靈活,可以應付各種場景。在上一章中,我們提到HttpContext中認證相關的功能放在了獨立的模塊中,以擴展的方式來展現,以保證HttpContext的簡潔性,本章就來介紹一下 ASP.NET Core 認證系統的整個輪廓,以及它的切入點。
AuthenticationHttpContextExtensions
AuthenticationHttpContextExtensions 類是對 HttpContext 認證相關的擴展,它提供了如下擴展方法:
public static class AuthenticationHttpContextExtensions{ ??public static Task<AuthenticateResult> AuthenticateAsync(this HttpContext context, string scheme) =>context.RequestServices.GetRequiredService<IAuthenticationService>().AuthenticateAsync(context, scheme); ? ?public static Task ChallengeAsync(this HttpContext context, string scheme, AuthenticationProperties properties) { } ? ?public static Task ForbidAsync(this HttpContext context, string scheme, AuthenticationProperties properties) { } ? ?public static Task SignInAsync(this HttpContext context, string scheme, ClaimsPrincipal principal, AuthenticationProperties properties) {} ? ?public static Task SignOutAsync(this HttpContext context, string scheme, AuthenticationProperties properties) { } ? ?public static Task<string> GetTokenAsync(this HttpContext context, string scheme, string tokenName) { } }
主要包括如上6個擴展方法,其它的只是一些參數重載:
SignInAsync?用戶登錄成功后頒發一個證書(加密的用戶憑證),用來標識用戶的身份。
SignOutAsync?退出登錄,如清除Coookie等。
AuthenticateAsync?驗證在?SignInAsync?中頒發的證書,并返回一個?AuthenticateResult?對象,表示用戶的身份。
ChallengeAsync?返回一個需要認證的標識來提示用戶登錄,通常會返回一個?401?狀態碼。
ForbidAsync?禁上訪問,表示用戶權限不足,通常會返回一個?403?狀態碼。
GetTokenAsync?用來獲取?AuthenticationProperties?中保存的額外信息。
它們的實現都非常簡單,與展示的第一個方法類似,從DI系統中獲取到?IAuthenticationService?接口實例,然后調用其同名方法。
因此,如果我們希望使用認證服務,那么首先要注冊?IAuthenticationService?的實例,ASP.NET Core 中也提供了對應注冊擴展方法:
public static class AuthenticationCoreServiceCollectionExtensions{ ? ?public static IServiceCollection AddAuthenticationCore(this IServiceCollection services) ? ?{services.TryAddScoped<IAuthenticationService, AuthenticationService>();services.TryAddSingleton<IClaimsTransformation, NoopClaimsTransformation>(); // Can be replaced with scoped ones that use DbContextservices.TryAddScoped<IAuthenticationHandlerProvider, AuthenticationHandlerProvider>();services.TryAddSingleton<IAuthenticationSchemeProvider, AuthenticationSchemeProvider>(); ? ?? ?return services;} ? ?
? ?
? ?public static IServiceCollection AddAuthenticationCore(this IServiceCollection services, Action<AuthenticationOptions> configureOptions) ? ?{services.AddAuthenticationCore();services.Configure(configureOptions); ? ? ?
? ? ?return services;} }
如上,AddAuthenticationCore?中注冊了認證系統的三大核心對象:IAuthenticationSchemeProvider,IAuthenticationHandlerProvider?和?IAuthenticationService,以及一個對Claim進行轉換的 IClaimsTransformation(不常用),下面就來介紹一下這三大對象。
IAuthenticationSchemeProvider
首先來解釋一下?Scheme?是用來做什么的。因為在 ASP.NET Core 中可以支持各種各樣的認證方式(如,cookie, bearer, oauth, openid 等等),而 Scheme 用來標識使用的是哪種認證方式,不同的認證方式其處理方式是完全不一樣的,所以Scheme是非常重要的。
IAuthenticationSchemeProvider 用來提供對Scheme的注冊和查詢,定義如下:
public interface IAuthenticationSchemeProvider{ ??void AddScheme(AuthenticationScheme scheme); ? ?
?Task<IEnumerable<AuthenticationScheme>> GetAllSchemesAsync(); ? ?
?Task<AuthenticationScheme> GetSchemeAsync(string name); ? ?
?Task<IEnumerable<AuthenticationScheme>> GetRequestHandlerSchemesAsync(); ?
?Task<AuthenticationScheme> GetDefaultAuthenticateSchemeAsync(); ?
?Task<AuthenticationScheme> GetDefaultChallengeSchemeAsync(); ? ?Task<AuthenticationScheme> GetDefaultForbidSchemeAsync(); ? ?Task<AuthenticationScheme> GetDefaultSignInSchemeAsync(); ?
?Task<AuthenticationScheme> GetDefaultSignOutSchemeAsync(); }
其?AddScheme?方法,用來注冊Scheme,而每一種Scheme最終體現為一個?AuthenticationScheme?類型的對象:
public class AuthenticationScheme{ ??public AuthenticationScheme(string name, string displayName, Type handlerType) ? ?{ ? ? ?
? ?if (!typeof(IAuthenticationHandler).IsAssignableFrom(handlerType)){ ? ? ? ? ?
? ??throw new ArgumentException("handlerType must implement IAuthenticationSchemeHandler.");}...} ? ?public string Name { get; } ? ?public string DisplayName { get; } ? ?public Type HandlerType { get; } }
每一個Scheme中還包含一個對應的IAuthenticationHandler類型的Handler,由它來完成具體的處理邏輯,看一下它的默認實現:
public class AuthenticationSchemeProvider : IAuthenticationSchemeProvider{ ??private IDictionary<string, AuthenticationScheme> _map = new Dictionary<string, AuthenticationScheme>(StringComparer.Ordinal); ?
?
? ?public AuthenticationSchemeProvider(IOptions<AuthenticationOptions> options) ? ?{_options = options.Value; ? ?
? ?? ?foreach (var builder in _options.Schemes){ ? ? ? ? ?
? ?? ? ?var scheme = builder.Build();AddScheme(scheme);}} ?
? ?
? ??private Task<AuthenticationScheme> GetDefaultSchemeAsync() ? ? ? ?=> _options.DefaultScheme != null? GetSchemeAsync(_options.DefaultScheme): Task.FromResult<AuthenticationScheme>(null);.... }
如上,通過一個內部的字典來保存我們所注冊的Scheme,key為Scheme名稱,然后提供一系列對該字典的查詢。它還提供了一系列的GetDefaultXXXSchemeAsync方法,所使用的Key是通過構造函數中接收的AuthenticationOptions對象來獲取的,如果未配置,則返回為null。
對于?AuthenticationOptions?對象,大家可能會比較熟悉,在上面介紹的?AddAuthenticationCore?擴展方法中,也是使用該對象來配置認證系統:
public class AuthenticationOptions{ ??private readonly IList<AuthenticationSchemeBuilder> _schemes = new List<AuthenticationSchemeBuilder>(); ? ?
?
?public IEnumerable<AuthenticationSchemeBuilder> Schemes => _schemes; ?
?
? ?public IDictionary<string, AuthenticationSchemeBuilder> SchemeMap { get; } = new Dictionary<string, AuthenticationSchemeBuilder>(StringComparer.Ordinal); ?
? ?
? ??public void AddScheme(string name, Action<AuthenticationSchemeBuilder> configureBuilder) ? ?{ ? ? ?
? ???if (SchemeMap.ContainsKey(name)){ ? ? ? ? ?
? ????throw new InvalidOperationException("Scheme already exists: " + name);} ? ? ?
? ?????var builder = new AuthenticationSchemeBuilder(name);configureBuilder(builder);_schemes.Add(builder);SchemeMap[name] = builder;} ? ?
? ?
? ?public void AddScheme<THandler>(string name, string displayName) where THandler : IAuthenticationHandler=> AddScheme(name, b =>{b.DisplayName = displayName;b.HandlerType = typeof(THandler);}); ?
? ?
? ?public string DefaultScheme { get; set; } ?
? ?
? ? ?public string DefaultAuthenticateScheme { get; set; } ?
? ? ? ?public string DefaultSignInScheme { get; set; } ?
? ? ? ?
? ? ? ??public string DefaultSignOutScheme { get; set; }
? ? ? ??
? ?? ?public string DefaultChallengeScheme { get; set; } ?
? ?? ?
? ? ?public string DefaultForbidScheme { get; set; } }
該對象可以幫助我們更加方便的注冊Scheme,提供泛型和?AuthenticationSchemeBuilder?兩種方式配置方式。
到此,我們了解到,要想使用認證系統,必要先注冊Scheme,而每一個Scheme必須指定一個Handler,否則會拋出異常,下面我們就來了解一下Handler。
IAuthenticationHandlerProvider
在 ASP.NET Core 的認證系統中,AuthenticationHandler 負責對用戶憑證的驗證,它定義了如下接口:
public interface IAuthenticationHandler{ ??Task InitializeAsync(AuthenticationScheme scheme, HttpContext context); ? ?Task<AuthenticateResult> AuthenticateAsync(); ?
?
??Task ChallengeAsync(AuthenticationProperties properties); ?
??
???Task ForbidAsync(AuthenticationProperties properties); }
AuthenticationHandler的創建是通過?IAuthenticationHandlerProvider?來完成的:
public interface IAuthenticationHandlerProvider{ ? ?Task<IAuthenticationHandler> GetHandlerAsync(HttpContext context, string authenticationScheme); }Provider 只定義了一個?GetHandlerAsync?方法,來獲取指定的Scheme的Hander,在 ASP.NET Core 中,很多地方都使用了類似的?Provider?模式。
而HandlerProvider的實現,我們通過對上面SchemeProvider的了解,應該可以猜到一二,因為在?AuthenticationScheme?中已經包含了Hander:
public class AuthenticationHandlerProvider : IAuthenticationHandlerProvider{ ? ?public AuthenticationHandlerProvider(IAuthenticationSchemeProvider schemes) ? ?{Schemes = schemes;} ?
?public IAuthenticationSchemeProvider Schemes { get; } ?
?
??private Dictionary<string, IAuthenticationHandler> _handlerMap = new Dictionary<string, IAuthenticationHandler>(StringComparer.Ordinal); ? ?
??
??public async Task<IAuthenticationHandler> GetHandlerAsync(HttpContext context, string authenticationScheme) ? ?{ ?
??? ? ?if (_handlerMap.ContainsKey(authenticationScheme)){ ? ? ? ? ?
??? ? ??return _handlerMap[authenticationScheme];} ? ? ?
??? ? ???var scheme = await Schemes.GetSchemeAsync(authenticationScheme); ? ? ? ?if (scheme == null){ ? ? ? ? ?
??? ? ??? ?return null;} ? ? ? ?var handler = (context.RequestServices.GetService(scheme.HandlerType) ??ActivatorUtilities.CreateInstance(context.RequestServices, scheme.HandlerType)) ? ? ? ? ? ?as IAuthenticationHandler; ? ? ? ?if (handler != null){ ? ? ? ? ? ?await handler.InitializeAsync(scheme, context);_handlerMap[authenticationScheme] = handler;} ? ? ? ?return handler;} }
可以看到,AuthenticationHandlerProvider?首先使用?IAuthenticationSchemeProvider?獲取到當前Scheme,然后先從DI中查找是否有此Scheme中的Handler,如果未注冊到DI系統中,則使用?ActivatorUtilities?來創建其實例,并緩存到內部的?_handlerMap?字典中。
IAuthenticationService
IAuthenticationService 本質上是對 IAuthenticationSchemeProvider 和 IAuthenticationHandlerProvider 封裝,用來對外提供一個統一的認證服務接口:
public interface IAuthenticationService{ ??Task<AuthenticateResult> AuthenticateAsync(HttpContext context, string scheme); ? ?
?
?Task ChallengeAsync(HttpContext context, string scheme, AuthenticationProperties properties); ?
?
?Task ForbidAsync(HttpContext context, string scheme, AuthenticationProperties properties); ?
?
?Task SignInAsync(HttpContext context, string scheme, ClaimsPrincipal principal, AuthenticationProperties properties);
?
?Task SignOutAsync(HttpContext context, string scheme, AuthenticationProperties properties); }
這5個方法中,都需要接收一個?scheme?參數,因為只有先指定你要使用的認證方式,才能知道該如何進行認證。
對于上面的前三個方法,我們知道在IAuthenticationHandler中都有對應的實現,而SignInAsync和SignOutAsync則使用了獨立的定義接口:
public interface IAuthenticationSignInHandler : IAuthenticationSignOutHandler{ ???Task SignInAsync(ClaimsPrincipal user, AuthenticationProperties properties); }
??
??public interface IAuthenticationSignOutHandler : IAuthenticationHandler{ ? ?Task SignOutAsync(AuthenticationProperties properties); }
SignInAsync 和 SignOutAsync 之所以使用獨立的接口,是因為在現代架構中,通常會提供一個統一的認證中心,負責證書的頒發及銷毀(登入和登出),而其它服務只用來驗證證書,并用不到SingIn/SingOut。
而 IAuthenticationService 的默認實現 AuthenticationService 中的邏輯就非常簡單了,只是調用Handler中的同名方法:
public class AuthenticationService : IAuthenticationService{ ??public IAuthenticationSchemeProvider Schemes { get; } ? ?
?
?public IAuthenticationHandlerProvider Handlers { get; } ?
?
?public IClaimsTransformation Transform { get; } ?
?
?public virtual async Task<AuthenticateResult> AuthenticateAsync(HttpContext context, string scheme) ? ?{ ? ?
? ? ?if (scheme == null){ ? ? ? ? ?
? ? ? ? ??var defaultScheme = await Schemes.GetDefaultAuthenticateSchemeAsync();scheme = defaultScheme?.Name; ? ? ?
? ? ? ? ???if (scheme == null){ ? ? ? ? ? ? ?
? ? ? ? ??? ? ?throw new InvalidOperationException($"No authenticationScheme was specified, and there was no DefaultAuthenticateScheme found.");}} ? ? ?
? ? ??var handler = await Handlers.GetHandlerAsync(context, scheme);
? ? ??var result = await handler.AuthenticateAsync(); ? ? ?
? ? ??if (result != null && result.Succeeded){ ? ? ? ? ?
? ? ?? ? ??var transformed = await Transform.TransformAsync(result.Principal); ? ? ? ? ?
? ? ?? ? ?return AuthenticateResult.Success(new AuthenticationTicket(transformed, result.Properties, result.Ticket.AuthenticationScheme));} ? ? ?
? ? ???return result;} }
AuthenticationService中對這5個方法的實現大致相同,首先會在我們傳入的scheme為null時,來獲取我們所注冊的默認scheme,然后獲取調用相應Handler的即可。針對?SignInAsync?和?SignOutAsync?的實現則會判斷Handler是否實現了對應的接口,若未實現則拋出異常。
不過在這里還涉及到如下兩個對象:
AuthenticateResult
AuthenticateResult 用來表示認證的結果:
public class AuthenticateResult{ ??public AuthenticationTicket Ticket { get; protected set; } ?
? ? ? public bool Succeeded => Ticket != null; ?
? ? ??public ClaimsPrincipal Principal => Ticket?.Principal; ?
? ?? ?public AuthenticationProperties Properties => Ticket?.Properties; ? ? ? ? ? ? ? ? ?
? ?? ?public Exception Failure { get; protected set; }
? ?? ?public bool None { get; protected set; } ?
? ?? ?
? ?? ?public static AuthenticateResult Success(AuthenticationTicket ticket) => new AuthenticateResult() { Ticket = ticket };
? ??
? ?? ?public static AuthenticateResult NoResult() => new AuthenticateResult() { None = true };
? ?? ?
? ?? ?public static AuthenticateResult Fail(Exception failure) => new AuthenticateResult() { Failure = failure };
? ?? ?
? ?? ?public static AuthenticateResult Fail(string failureMessage) => new AuthenticateResult() { Failure = new Exception(failureMessage) }; }
它主要包含一個核心屬性?AuthenticationTicket:
public class AuthenticationTicket{ public string AuthenticationScheme { get; private set; } ??public ClaimsPrincipal Principal { get; private set; } ?
? ?public AuthenticationProperties Properties { get; private set; } }
我們可以把AuthenticationTicket看成是一個經過認證后頒發的證書,
其?ClaimsPrincipal?屬性我們較為熟悉,表示證書的主體,在基于聲明的認證中,用來標識一個人的身份(如:姓名,郵箱等等),后續會詳細介紹一下基于聲明的認證。
而?AuthenticationProperties?屬性用來表示證書頒發的相關信息,如頒發時間,過期時間,重定向地址等等:
public class AuthenticationProperties{ ??public IDictionary<string, string> Items { get; } ?
?
? ?public string RedirectUri{ ? ? ?
? ??get{ ? ? ? ? ?
? ???string value; ? ? ? ? ?
? ??? ?return Items.TryGetValue(RedirectUriKey, out value) ? value : null;} ? ? ?
? ??? ??set{ ? ? ? ? ?
? ??? ?? ?if (value != null) Items[RedirectUriKey] = value; ?
? ??? ?? ??else{ ? ? ? ? ? ?
? ??? ?? ?? ? ?if (Items.ContainsKey(RedirectUriKey)) Items.Remove(RedirectUriKey);}}}... }
在上面最開始介紹的HttpContext中的?GetTokenAsync?擴展方法便是對AuthenticationProperties的擴展:
public static class AuthenticationTokenExtensions{ ??private static string TokenNamesKey = ".TokenNames"; ?
??private static string TokenKeyPrefix = ".Token."; ? ?
??public static void StoreTokens(this AuthenticationProperties properties, IEnumerable<AuthenticationToken> tokens) {} ?
??public static bool UpdateTokenValue(this AuthenticationProperties properties, string tokenName, string tokenValue) {} ?
??public static IEnumerable<AuthenticationToken> GetTokens(this AuthenticationProperties properties) { } ?
??public static string GetTokenValue(this AuthenticationProperties properties, string tokenName) ? ?{ ? ? ?
???var tokenKey = TokenKeyPrefix + tokenName; ?
??? ? ? ?return properties.Items.ContainsKey(tokenKey) ? properties.Items[tokenKey] : null;} ?
?public static Task<string> GetTokenAsync(this IAuthenticationService auth, HttpContext context, string tokenName) ? ? ? ?=> auth.GetTokenAsync(context, scheme: null, tokenName: tokenName); ? ?
?public static async Task<string> GetTokenAsync(this IAuthenticationService auth, HttpContext context, string scheme, string tokenName) ?
?{ ?
?? ? ?var result = await auth.AuthenticateAsync(context, scheme); ? ? ? ?return result?.Properties?.GetTokenValue(tokenName);} }
如上,Token擴展只是對AuthenticationProperties中的?Items?屬性進行添加和讀取。
IClaimsTransformation
IClaimsTransformation 用來對由我們的應用程序傳入的?ClaimsPrincipal?進行轉換,它只定義了一個?Transform?方法:
public interface IClaimsTransformation{ ??Task<ClaimsPrincipal> TransformAsync(ClaimsPrincipal principal); }
其默認實現,不做任何處理,直接返回。它適合于全局的為?ClaimsPrincipal?添加一些預定義的聲明,如添加當前時間等,然后在DI中把我們的實現注冊進去即可。
Usage
下面我們演示一下 ASP.NET Core 認證系統的實際用法:
首先,我們要定義一個Handler:
public class MyHandler : 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["mycookie"]; ?
? ? ? ?if (string.IsNullOrEmpty(cookie)){ ? ? ? ?
? ?return AuthenticateResult.NoResult();} ? ? ?
? ??return AuthenticateResult.Success(Deserialize(cookie));} ? ?
public Task ChallengeAsync(AuthenticationProperties properties) ? ?{Context.Response.Redirect("/login"); ? ? ? ?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("myCookie", Serialize(ticket)); ? ? ? ?return Task.CompletedTask;} ? ?
?
?public Task SignOutAsync(AuthenticationProperties properties) ? ?{Context.Response.Cookies.Delete("myCookie"); ? ?
? ? ?return Task.CompletedTask;} }
如上,在?SignInAsync?中將用戶的Claim序列化后保存到Cookie中,在?AuthenticateAsync?中從Cookie中讀取并反序列化成用戶Claim。
然后在DI系統中注冊我們的Handler和Scheme:
public void ConfigureServices(IServiceCollection services){services.AddAuthenticationCore(options => options.AddScheme<MyHandler>("myScheme", "demo scheme")); }最后,便可以通過HttpContext來調用認證系統了:
public void Configure(IApplicationBuilder app){ ??// 登錄app.Map("/login", builder => builder.Use(next =>{ ? ? ?
??return async (context) =>{ ? ? ? ?
??? ?var claimIdentity = new ClaimsIdentity(); ? ? ? ? ?
??? ??claimIdentity.AddClaim(new Claim(ClaimTypes.Name, "jim")); ? ? ? ? ? ?await context.SignInAsync("myScheme", new ClaimsPrincipal(claimIdentity));};})); ?
??? ???// 退出app.Map("/logout", builder => builder.Use(next =>{ ? ? ?
??? ????return async (context) =>{ ? ? ? ? ?
??? ?????await context.SignOutAsync("myScheme");};})); ? ?// 認證app.Use(next =>{ ? ? ? ?
??? ?????return async (context) =>{ ? ? ? ? ? ?
??? ?????var result = await context.AuthenticateAsync("myScheme"); ? ? ? ?
??? ????? ? ?if (result?.Principal != null) context.User = result.Principal; ? ? ? ? ?
??? ????? ? ? ?await next(context);};}); ? ?// 授權app.Use(async (context, next) =>{ ?
?
??? ??? ?var user = context.User; ?
??? ????if (user?.Identity?.IsAuthenticated ?? false){ ? ? ?
??? ????if (user.Identity.Name != "jim") await context.ForbidAsync("myScheme"); ? ?
?? ? ? ?else await next();} ? ? ? ?
?? ? ? ?else{ ? ? ? ? ?
?? ? ? ??await context.ChallengeAsync("myScheme");}}); ? ?// 訪問受保護資源app.Map("/resource", builder => builder.Run(async (context) => await context.Response.WriteAsync("Hello, ASP.NET Core!"))); }
在這里完整演示了 ASP.NET Core 認證系統的基本用法,當然,在實際使用中要比這更加復雜,如安全性,易用性等方面的完善,但本質上也就這么多東西。
總結
本章基于?HttpAbstractions?對 ASP.NET Core 認證系統做了一個簡單的介紹,但大多是一些抽象層次的定義,并未涉及到具體的實現。因為現實中有各種各樣的場景無法預測,HttpAbstractions?提供了統一的認證規范,在我們的應用程序中,可以根據具體需求來靈活的擴展適合的認證方式。不過在?Security?提供了更加具體的實現方式,也包含了 Cookie, JwtBearer, OAuth, OpenIdConnect 等較為常用的認證實現。在下個系列會來詳細介紹一下 ASP.NET Core 的認證與授權,更加偏向于實戰,敬請期待!
ASP.NET Core 在GitHub上的開源地址為:https://github.com/aspnet,包含了100多個項目,ASP.NET Core 的核心是?HttpAbstractions?,其它的都是圍繞著?HttpAbstractions?進行的擴展。本系列文章所涉及到的源碼只包含?Hosting?和?HttpAbstractions?,它們兩個已經構成了一個完整的 ASP.NET Core 運行時,不需要其它模塊,就可以輕松應對一些簡單的場景。當然,更多的時候我們還會使用比較熟悉的?Mvc?來大大提高開發速度和體驗,后續再來介紹一下MVC的運行方式。
相關文章:?
.NET Core 2.0 正式發布信息匯總
.NET Standard 2.0 特性介紹和使用指南
.NET Core 2.0 的dll實時更新、https、依賴包變更問題及解決
.NET Core 2.0 特性介紹和使用指南
Entity Framework Core 2.0 新特性
體驗 PHP under .NET Core
.NET Core 2.0使用NLog
升級項目到.NET Core 2.0,在Linux上安裝Docker,并成功部署
解決Visual Studio For Mac Restore失敗的問題
ASP.NET Core 2.0 特性介紹和使用指南
.Net Core下通過Proxy 模式 使用 WCF
.NET Core 2.0 開源Office組件 NPOI
ASP.NET Core Razor頁面 vs MVC
Razor Page–Asp.Net Core 2.0新功能 ?Razor Page介紹
MySql 使用 EF Core 2.0 CodeFirst、DbFirst、數據庫遷移(Migration)介紹及示例
.NET Core 2.0遷移技巧之web.config配置文件
asp.net core MVC 過濾器之ExceptionFilter過濾器(一)
ASP.NET Core 使用Cookie驗證身份
ASP.NET Core MVC – Tag Helpers 介紹
ASP.NET Core MVC – Caching Tag Helpers
ASP.NET Core MVC – Form Tag Helpers
ASP.NET Core MVC – 自定義 Tag Helpers
ASP.NET Core MVC – Tag Helper 組件
ASP.NET Core 運行原理解剖[1]:Hosting
ASP.NET Core 運行原理解剖[2]:Hosting補充之配置介紹
ASP.NET Core 運行原理解剖[3]:Middleware-請求管道的構成
ASP.NET Core 運行原理解剖[4]:進入HttpContext的世界
原文地址:http://www.cnblogs.com/RainingNight/p/authentication-in-asp-net-core.html
.NET社區新聞,深度好文,微信中搜索dotNET跨平臺或掃描二維碼關注
總結
以上是生活随笔為你收集整理的ASP.NET Core 运行原理解剖[5]:Authentication的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 如何理解事件溯源
- 下一篇: Docker打包 Asp.Net Cor