ASP.NET Core 运行原理解剖[5]:Authentication
在現(xiàn)代應(yīng)用程序中,認(rèn)證已不再是簡(jiǎn)單的將用戶憑證保存在瀏覽器中,而要適應(yīng)多種場(chǎng)景,如App,WebAPI,第三方登錄等等。在 ASP.NET 4.x 時(shí)代的Windows認(rèn)證和Forms認(rèn)證已無(wú)法滿足現(xiàn)代化的需求,因此在ASP.NET Core 中對(duì)認(rèn)證及授權(quán)進(jìn)行了全新設(shè)計(jì),使其更加靈活,可以應(yīng)付各種場(chǎng)景。在上一章中,我們提到HttpContext中認(rèn)證相關(guān)的功能放在了獨(dú)立的模塊中,以擴(kuò)展的方式來(lái)展現(xiàn),以保證HttpContext的簡(jiǎn)潔性,本章就來(lái)介紹一下 ASP.NET Core 認(rèn)證系統(tǒng)的整個(gè)輪廓,以及它的切入點(diǎn)。
AuthenticationHttpContextExtensions
AuthenticationHttpContextExtensions 類是對(duì) HttpContext 認(rèn)證相關(guān)的擴(kuò)展,它提供了如下擴(kuò)展方法:
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個(gè)擴(kuò)展方法,其它的只是一些參數(shù)重載:
SignInAsync?用戶登錄成功后頒發(fā)一個(gè)證書(加密的用戶憑證),用來(lái)標(biāo)識(shí)用戶的身份。
SignOutAsync?退出登錄,如清除Coookie等。
AuthenticateAsync?驗(yàn)證在?SignInAsync?中頒發(fā)的證書,并返回一個(gè)?AuthenticateResult?對(duì)象,表示用戶的身份。
ChallengeAsync?返回一個(gè)需要認(rèn)證的標(biāo)識(shí)來(lái)提示用戶登錄,通常會(huì)返回一個(gè)?401?狀態(tài)碼。
ForbidAsync?禁上訪問(wèn),表示用戶權(quán)限不足,通常會(huì)返回一個(gè)?403?狀態(tài)碼。
GetTokenAsync?用來(lái)獲取?AuthenticationProperties?中保存的額外信息。
它們的實(shí)現(xiàn)都非常簡(jiǎn)單,與展示的第一個(gè)方法類似,從DI系統(tǒng)中獲取到?IAuthenticationService?接口實(shí)例,然后調(diào)用其同名方法。
因此,如果我們希望使用認(rèn)證服務(wù),那么首先要注冊(cè)?IAuthenticationService?的實(shí)例,ASP.NET Core 中也提供了對(duì)應(yīng)注冊(cè)擴(kuò)展方法:
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?中注冊(cè)了認(rèn)證系統(tǒng)的三大核心對(duì)象:IAuthenticationSchemeProvider,IAuthenticationHandlerProvider?和?IAuthenticationService,以及一個(gè)對(duì)Claim進(jìn)行轉(zhuǎn)換的 IClaimsTransformation(不常用),下面就來(lái)介紹一下這三大對(duì)象。
IAuthenticationSchemeProvider
首先來(lái)解釋一下?Scheme?是用來(lái)做什么的。因?yàn)樵?ASP.NET Core 中可以支持各種各樣的認(rèn)證方式(如,cookie, bearer, oauth, openid 等等),而 Scheme 用來(lái)標(biāo)識(shí)使用的是哪種認(rèn)證方式,不同的認(rèn)證方式其處理方式是完全不一樣的,所以Scheme是非常重要的。
IAuthenticationSchemeProvider 用來(lái)提供對(duì)Scheme的注冊(cè)和查詢,定義如下:
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?方法,用來(lái)注冊(cè)Scheme,而每一種Scheme最終體現(xiàn)為一個(gè)?AuthenticationScheme?類型的對(duì)象:
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; } }
每一個(gè)Scheme中還包含一個(gè)對(duì)應(yīng)的IAuthenticationHandler類型的Handler,由它來(lái)完成具體的處理邏輯,看一下它的默認(rèn)實(shí)現(xiàn):
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);.... }
如上,通過(guò)一個(gè)內(nèi)部的字典來(lái)保存我們所注冊(cè)的Scheme,key為Scheme名稱,然后提供一系列對(duì)該字典的查詢。它還提供了一系列的GetDefaultXXXSchemeAsync方法,所使用的Key是通過(guò)構(gòu)造函數(shù)中接收的AuthenticationOptions對(duì)象來(lái)獲取的,如果未配置,則返回為null。
對(duì)于?AuthenticationOptions?對(duì)象,大家可能會(huì)比較熟悉,在上面介紹的?AddAuthenticationCore?擴(kuò)展方法中,也是使用該對(duì)象來(lái)配置認(rèn)證系統(tǒng):
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; } }
該對(duì)象可以幫助我們更加方便的注冊(cè)Scheme,提供泛型和?AuthenticationSchemeBuilder?兩種方式配置方式。
到此,我們了解到,要想使用認(rèn)證系統(tǒng),必要先注冊(cè)Scheme,而每一個(gè)Scheme必須指定一個(gè)Handler,否則會(huì)拋出異常,下面我們就來(lái)了解一下Handler。
IAuthenticationHandlerProvider
在 ASP.NET Core 的認(rèn)證系統(tǒng)中,AuthenticationHandler 負(fù)責(zé)對(duì)用戶憑證的驗(yàn)證,它定義了如下接口:
public interface IAuthenticationHandler{ ??Task InitializeAsync(AuthenticationScheme scheme, HttpContext context); ? ?Task<AuthenticateResult> AuthenticateAsync(); ?
?
??Task ChallengeAsync(AuthenticationProperties properties); ?
??
???Task ForbidAsync(AuthenticationProperties properties); }
AuthenticationHandler的創(chuàng)建是通過(guò)?IAuthenticationHandlerProvider?來(lái)完成的:
public interface IAuthenticationHandlerProvider{ ? ?Task<IAuthenticationHandler> GetHandlerAsync(HttpContext context, string authenticationScheme); }Provider 只定義了一個(gè)?GetHandlerAsync?方法,來(lái)獲取指定的Scheme的Hander,在 ASP.NET Core 中,很多地方都使用了類似的?Provider?模式。
而HandlerProvider的實(shí)現(xiàn),我們通過(guò)對(duì)上面SchemeProvider的了解,應(yīng)該可以猜到一二,因?yàn)樵?AuthenticationScheme?中已經(jīng)包含了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?獲取到當(dāng)前Scheme,然后先從DI中查找是否有此Scheme中的Handler,如果未注冊(cè)到DI系統(tǒng)中,則使用?ActivatorUtilities?來(lái)創(chuàng)建其實(shí)例,并緩存到內(nèi)部的?_handlerMap?字典中。
IAuthenticationService
IAuthenticationService 本質(zhì)上是對(duì) IAuthenticationSchemeProvider 和 IAuthenticationHandlerProvider 封裝,用來(lái)對(duì)外提供一個(gè)統(tǒng)一的認(rèn)證服務(wù)接口:
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個(gè)方法中,都需要接收一個(gè)?scheme?參數(shù),因?yàn)橹挥邢戎付阋褂玫恼J(rèn)證方式,才能知道該如何進(jìn)行認(rèn)證。
對(duì)于上面的前三個(gè)方法,我們知道在IAuthenticationHandler中都有對(duì)應(yīng)的實(shí)現(xiàn),而SignInAsync和SignOutAsync則使用了獨(dú)立的定義接口:
public interface IAuthenticationSignInHandler : IAuthenticationSignOutHandler{ ???Task SignInAsync(ClaimsPrincipal user, AuthenticationProperties properties); }
??
??public interface IAuthenticationSignOutHandler : IAuthenticationHandler{ ? ?Task SignOutAsync(AuthenticationProperties properties); }
SignInAsync 和 SignOutAsync 之所以使用獨(dú)立的接口,是因?yàn)樵诂F(xiàn)代架構(gòu)中,通常會(huì)提供一個(gè)統(tǒng)一的認(rèn)證中心,負(fù)責(zé)證書的頒發(fā)及銷毀(登入和登出),而其它服務(wù)只用來(lái)驗(yàn)證證書,并用不到SingIn/SingOut。
而 IAuthenticationService 的默認(rèn)實(shí)現(xiàn) AuthenticationService 中的邏輯就非常簡(jiǎn)單了,只是調(diào)用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中對(duì)這5個(gè)方法的實(shí)現(xiàn)大致相同,首先會(huì)在我們傳入的scheme為null時(shí),來(lái)獲取我們所注冊(cè)的默認(rèn)scheme,然后獲取調(diào)用相應(yīng)Handler的即可。針對(duì)?SignInAsync?和?SignOutAsync?的實(shí)現(xiàn)則會(huì)判斷Handler是否實(shí)現(xiàn)了對(duì)應(yīng)的接口,若未實(shí)現(xiàn)則拋出異常。
不過(guò)在這里還涉及到如下兩個(gè)對(duì)象:
AuthenticateResult
AuthenticateResult 用來(lái)表示認(rèn)證的結(jié)果:
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) }; }
它主要包含一個(gè)核心屬性?AuthenticationTicket:
public class AuthenticationTicket{ public string AuthenticationScheme { get; private set; } ??public ClaimsPrincipal Principal { get; private set; } ?
? ?public AuthenticationProperties Properties { get; private set; } }
我們可以把AuthenticationTicket看成是一個(gè)經(jīng)過(guò)認(rèn)證后頒發(fā)的證書,
其?ClaimsPrincipal?屬性我們較為熟悉,表示證書的主體,在基于聲明的認(rèn)證中,用來(lái)標(biāo)識(shí)一個(gè)人的身份(如:姓名,郵箱等等),后續(xù)會(huì)詳細(xì)介紹一下基于聲明的認(rèn)證。
而?AuthenticationProperties?屬性用來(lái)表示證書頒發(fā)的相關(guān)信息,如頒發(fā)時(shí)間,過(guò)期時(shí)間,重定向地址等等:
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);}}}... }
在上面最開(kāi)始介紹的HttpContext中的?GetTokenAsync?擴(kuò)展方法便是對(duì)AuthenticationProperties的擴(kuò)展:
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擴(kuò)展只是對(duì)AuthenticationProperties中的?Items?屬性進(jìn)行添加和讀取。
IClaimsTransformation
IClaimsTransformation 用來(lái)對(duì)由我們的應(yīng)用程序傳入的?ClaimsPrincipal?進(jìn)行轉(zhuǎn)換,它只定義了一個(gè)?Transform?方法:
public interface IClaimsTransformation{ ??Task<ClaimsPrincipal> TransformAsync(ClaimsPrincipal principal); }
其默認(rèn)實(shí)現(xiàn),不做任何處理,直接返回。它適合于全局的為?ClaimsPrincipal?添加一些預(yù)定義的聲明,如添加當(dāng)前時(shí)間等,然后在DI中把我們的實(shí)現(xiàn)注冊(cè)進(jìn)去即可。
Usage
下面我們演示一下 ASP.NET Core 認(rèn)證系統(tǒng)的實(shí)際用法:
首先,我們要定義一個(gè)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系統(tǒng)中注冊(cè)我們的Handler和Scheme:
public void ConfigureServices(IServiceCollection services){services.AddAuthenticationCore(options => options.AddScheme<MyHandler>("myScheme", "demo scheme")); }最后,便可以通過(guò)HttpContext來(lái)調(diào)用認(rèn)證系統(tǒng)了:
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");};})); ? ?// 認(rèn)證app.Use(next =>{ ? ? ? ?
??? ?????return async (context) =>{ ? ? ? ? ? ?
??? ?????var result = await context.AuthenticateAsync("myScheme"); ? ? ? ?
??? ????? ? ?if (result?.Principal != null) context.User = result.Principal; ? ? ? ? ?
??? ????? ? ? ?await next(context);};}); ? ?// 授權(quán)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");}}); ? ?// 訪問(wèn)受保護(hù)資源app.Map("/resource", builder => builder.Run(async (context) => await context.Response.WriteAsync("Hello, ASP.NET Core!"))); }
在這里完整演示了 ASP.NET Core 認(rèn)證系統(tǒng)的基本用法,當(dāng)然,在實(shí)際使用中要比這更加復(fù)雜,如安全性,易用性等方面的完善,但本質(zhì)上也就這么多東西。
總結(jié)
本章基于?HttpAbstractions?對(duì) ASP.NET Core 認(rèn)證系統(tǒng)做了一個(gè)簡(jiǎn)單的介紹,但大多是一些抽象層次的定義,并未涉及到具體的實(shí)現(xiàn)。因?yàn)楝F(xiàn)實(shí)中有各種各樣的場(chǎng)景無(wú)法預(yù)測(cè),HttpAbstractions?提供了統(tǒng)一的認(rèn)證規(guī)范,在我們的應(yīng)用程序中,可以根據(jù)具體需求來(lái)靈活的擴(kuò)展適合的認(rèn)證方式。不過(guò)在?Security?提供了更加具體的實(shí)現(xiàn)方式,也包含了 Cookie, JwtBearer, OAuth, OpenIdConnect 等較為常用的認(rèn)證實(shí)現(xiàn)。在下個(gè)系列會(huì)來(lái)詳細(xì)介紹一下 ASP.NET Core 的認(rèn)證與授權(quán),更加偏向于實(shí)戰(zhàn),敬請(qǐng)期待!
ASP.NET Core 在GitHub上的開(kāi)源地址為:https://github.com/aspnet,包含了100多個(gè)項(xiàng)目,ASP.NET Core 的核心是?HttpAbstractions?,其它的都是圍繞著?HttpAbstractions?進(jìn)行的擴(kuò)展。本系列文章所涉及到的源碼只包含?Hosting?和?HttpAbstractions?,它們兩個(gè)已經(jīng)構(gòu)成了一個(gè)完整的 ASP.NET Core 運(yùn)行時(shí),不需要其它模塊,就可以輕松應(yīng)對(duì)一些簡(jiǎn)單的場(chǎng)景。當(dāng)然,更多的時(shí)候我們還會(huì)使用比較熟悉的?Mvc?來(lái)大大提高開(kāi)發(fā)速度和體驗(yàn),后續(xù)再來(lái)介紹一下MVC的運(yùn)行方式。
相關(guān)文章:?
.NET Core 2.0 正式發(fā)布信息匯總
.NET Standard 2.0 特性介紹和使用指南
.NET Core 2.0 的dll實(shí)時(shí)更新、https、依賴包變更問(wèn)題及解決
.NET Core 2.0 特性介紹和使用指南
Entity Framework Core 2.0 新特性
體驗(yàn) PHP under .NET Core
.NET Core 2.0使用NLog
升級(jí)項(xiàng)目到.NET Core 2.0,在Linux上安裝Docker,并成功部署
解決Visual Studio For Mac Restore失敗的問(wèn)題
ASP.NET Core 2.0 特性介紹和使用指南
.Net Core下通過(guò)Proxy 模式 使用 WCF
.NET Core 2.0 開(kāi)源Office組件 NPOI
ASP.NET Core Razor頁(yè)面 vs MVC
Razor Page–Asp.Net Core 2.0新功能 ?Razor Page介紹
MySql 使用 EF Core 2.0 CodeFirst、DbFirst、數(shù)據(jù)庫(kù)遷移(Migration)介紹及示例
.NET Core 2.0遷移技巧之web.config配置文件
asp.net core MVC 過(guò)濾器之ExceptionFilter過(guò)濾器(一)
ASP.NET Core 使用Cookie驗(yàn)證身份
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 運(yùn)行原理解剖[1]:Hosting
ASP.NET Core 運(yùn)行原理解剖[2]:Hosting補(bǔ)充之配置介紹
ASP.NET Core 運(yùn)行原理解剖[3]:Middleware-請(qǐng)求管道的構(gòu)成
ASP.NET Core 運(yùn)行原理解剖[4]:進(jìn)入HttpContext的世界
原文地址:http://www.cnblogs.com/RainingNight/p/authentication-in-asp-net-core.html
.NET社區(qū)新聞,深度好文,微信中搜索dotNET跨平臺(tái)或掃描二維碼關(guān)注
總結(jié)
以上是生活随笔為你收集整理的ASP.NET Core 运行原理解剖[5]:Authentication的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 如何理解事件溯源
- 下一篇: Docker打包 Asp.Net Cor