在线时间:8:00-16:00
迪恩网络APP
随时随地掌握行业动态
扫描二维码
关注迪恩网络微信公众号
原文链接: https://www.cnblogs.com/RainingNight/p/introduce-basic-authentication-in-asp-net-core.html
在ASP.NET 4.X 中,我们最常用的是Forms认证,它既可以用于局域网环境,也可用于互联网环境,有着非常广泛的使用。但是它很难进行扩展,更无法与第三方认证集成,因此,在 ASP.NET Core 中对认证与授权进行了全新的设计,并使用基于声明的认证(claims-based authentication),以适应现代化应用的需求。在运行原理解剖[5]:Authentication中介绍了一下HttpContext与认证系统的集成,本系列文章则来详细介绍一下 ASP.NET Core 中认证与授权。 基于声明的认证有两个主要的特点:
ASP.NET Core 中的用户身份Claim在 ASP.NET Core 中,使用 一个Claim可以是“用户的姓名”,“邮箱地址”,“电话”,等等,而多个Claim构成一个用户的身份,使用 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; } } } 如上,其 // 创建一个用户身份,注意需要指定AuthenticationType,否则IsAuthenticated将为false。 var claimIdentity = new ClaimsIdentity("myAuthenticationType"); // 添加几个Claim claimIdentity.AddClaim(new Claim(ClaimTypes.Name, "bob")); claimIdentity.AddClaim(new Claim(ClaimTypes.Email, "[email protected]")); claimIdentity.AddClaim(new Claim(ClaimTypes.MobilePhone, "18888888888")); 如上,我们可以根据需要添加任意个的Claim,最后我们还需要再将用户身份放到 ClaimsPrincipalASP.NET Core 中,HttpContext直接使用的就是 public abstract class HttpContext { public abstract ClaimsPrincipal User { get; set; } } ClaimsPrincipal的创建非常简单,只需传入我们上面创建的用户身份即可: var principal = new ClaimsPrincipal(claimIdentity); 由于HTTP是无状态的,我们通常使用Cookie,请求头或请求参数等方式来附加用户的信息,在网络上进行传输,这就涉及到序列化和安全方面的问题。因此,还需要将
AuthenticationTicket我们创建完 用户票据除了包含上面创建的 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 项目中,它包含 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(); } 如上,我们的系统便支持了 Microsoft.AspNetCore.Authentication,是所有认证实现的公共抽象类,它定义了实现认证Handler的规范,并包含一些共用的方法,如令牌加密,序列化等, 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; } 如上,它首先会调用上一章中介绍的 AddScheme在上面的 AddAuthentication 中返回的是一个 AddRemoteScheme看到 Remote 我们应该就可以猜到它是一种远程验证方式 UseAuthentication在上面,注册认证中间件时,我们只需调用一个 public static IApplicationBuilder UseAuthentication(this IApplicationBuilder app) { return app.UseMiddleware<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是在HttpAbstractions中定义的,只是在运行原理解剖[5]:Authentication中没有介绍到它:
它多了一个 继续分析上面代码,通过调用 最后,调用
认证Handler上文中多次提到认证Handler,它由统一的 在本地验证中,身份令牌的发放与认证通常是由同一个服务器来完成,这也是我们比较熟悉的场景,对于Cookie, JwtBearer等认证来说,都属于是本地验证。而当我们使用OAuth, OIDC等验证方式时,身份令牌的发放则是由独立的服务或是第三方(QQ, Weibo 等)认证来提供,此时在我们的应用程序中获取身份令牌时需要请求远程服务器,因此称之为远程验证。
AuthenticationHandlerAuthenticationHandler是所有认证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; } ... } 如上,它定义一个抽象方法 而对于
RemoteAuthenticationHandler
在上面介绍的 而
也就是说,远程Hander会在用户未登录时,指引用户跳转到认证服务器,登录成功后,解析认证服务器传回的凭证,最终依赖于本地Handler来保存身份令牌。当用户再次访问则无需经过远程Handler,直接交给本地Handler来处理。 由此也可以知道,远程认证中本身并不具备SignIn的能力,所以必须通过指定其它 对于其父类的 总结基于声明的认证并不是微软所特有的,它在国外被广泛的使用,如微软的ADFS,Google,Facebook,Twitter等等。在基于声明的认证中,对认证和授权进行了明确的区分,认证用来颁发一个用户的身份标识,其包含这个用户的基本信息,而对于这个身份的颁发则由我们信任的第三方机构来(STS)颁发(当然,你也可以自己来颁发)。而授权,则是通过获取身份标识中的信息,来判断该用户能做什么,不能做什么。 本文对 ASP.NET Core 中认证系统的整个流程做了一个简要的介绍,可能会比较苦涩难懂,不过没关系,大致有个印象就好,下一章则详细介绍一下最常用的本地认证方式:Cookie认证,后续也会详细介绍 OIDC 的用法与实现,到时再回头来看本文或许会豁然开朗。
|
请发表评论