ASP.NET Core [4]: Authentication(笔记)
原文连接:http://www.cnblogs.com/RainingNight/p/authentication-in-asp-net-core.html
在现代应用程序中,认证已不再是简单的将用户凭证保存在浏览器中,而要适应多种场景,如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 DbContext
services.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的注册和查询,其 AddScheme 方法,用来注册Scheme,而每一种Scheme最终体现为一个 AuthenticationScheme 类型的对象: 每一个Scheme中还包含一个对应的IAuthenticationHandler类型的Handler,由它来完成具体的处理逻辑,看一下它的默认实现:
对于 AuthenticationOptions 对象,大家可能会比较熟悉,在上面介绍的 AddAuthenticationCore 扩展方法中,也是使用该对象来配置认证系统:
public class AuthenticationOptions
{
public AuthenticationOptions(); public IEnumerable<AuthenticationSchemeBuilder> Schemes { get; } public IDictionary<string, AuthenticationSchemeBuilder> SchemeMap { get; } 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; } public void AddScheme(string name, Action<AuthenticationSchemeBuilder> configureBuilder); public void AddScheme<THandler>(string name, string displayName) where THandler : IAuthenticationHandler;
}
services.AddAuthentication(option =>{
option.DefaultAuthenticateScheme=JwtBearerDefaults.AuthenticationScheme;
option.DefaultChallengeScheme=JwtBearerDefaults.AuthenticationScheme;
})
该对象可以帮助我们更加方便的注册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);
}
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中的同名方法:
AuthenticationService中对这5个方法的实现大致相同,首先会在我们传入的scheme为null时,来获取我们所注册的默认scheme,然后获取调用相应Handler的即可。针对 SignInAsync 和 SignOutAsync 的实现则会判断Handler是否实现了对应的接口,若未实现则抛出异常。
AuthenticateResult
AuthenticateResult 用来表示认证的结果:
它主要包含一个核心属性 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 属性用来表示证书颁发的相关信息,如颁发时间,过期时间,重定向地址等等: 在上面最开始介绍的HttpContext中的 GetTokenAsync 扩展方法便是对AuthenticationProperties的扩展: oken扩展只是对AuthenticationProperties中的 Items 属性进行添加和读取。
下面我们演示一下 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 = ;
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的运行方式。
ASP.NET Core [4]: Authentication(笔记)的更多相关文章
- sql server 关于表中只增标识问题 C# 实现自动化打开和关闭可执行文件(或 关闭停止与系统交互的可执行文件) ajaxfileupload插件上传图片功能,用MVC和aspx做后台各写了一个案例 将小写阿拉伯数字转换成大写的汉字, C# WinForm 中英文实现, 国际化实现的简单方法 ASP.NET Core 2 学习笔记(六)ASP.NET Core 2 学习笔记(三)
sql server 关于表中只增标识问题 由于我们系统时间用的过长,数据量大,设计是采用自增ID 我们插入数据的时候把ID也写进去,我们可以采用 关闭和开启自增标识 没有关闭的时候 ,提示一下错 ...
- Asp.Net Core WebApi学习笔记(四)-- Middleware
Asp.Net Core WebApi学习笔记(四)-- Middleware 本文记录了Asp.Net管道模型和Asp.Net Core的Middleware模型的对比,并在上一篇的基础上增加Mid ...
- ASP.NET Core 2 学习笔记(七)路由
ASP.NET Core通过路由(Routing)设定,将定义的URL规则找到相对应行为:当使用者Request的URL满足特定规则条件时,则自动对应到相符合的行为处理.从ASP.NET就已经存在的架 ...
- ASP.NET Core 2 学习笔记(十三)Swagger
Swagger也算是行之有年的API文件生成器,只要在API上使用C#的<summary />文件注解标签,就可以产生精美的线上文件,并且对RESTful API有良好的支持.不仅支持生成 ...
- ASP.NET Core 2 学习笔记(十二)REST-Like API
Restful几乎已算是API设计的标准,通过HTTP Method区分新增(Create).查询(Read).修改(Update)和删除(Delete),简称CRUD四种数据存取方式,简约又直接的风 ...
- ASP.NET Core 2 学习笔记(十)视图
ASP.NET Core MVC中的Views是负责网页显示,将数据一并渲染至UI包含HTML.CSS等.并能痛过Razor语法在*.cshtml中写渲染画面的程序逻辑.本篇将介绍ASP.NET Co ...
- ASP.NET Core 2 学习笔记(一)开始
原文:ASP.NET Core 2 学习笔记(一)开始 来势汹汹的.NET Core似乎要取代.NET Framework,ASP.NET也随之发布.NET Core版本.虽然名称沿用ASP.NET, ...
- Asp .Net core 2 学习笔记(2) —— 中间件
这个系列的初衷是便于自己总结与回顾,把笔记本上面的东西转移到这里,态度不由得谨慎许多,下面是我参考的资源: ASP.NET Core 中文文档目录 官方文档 记在这里的东西我会不断的完善丰满,对于文章 ...
- ASP.NET Core 2 学习笔记(六)MVC
ASP.NET Core MVC跟ASP.NET MVC观念是一致的,使用上也没有什么太大的变化.之前的ASP.NET MVC把MVC及Web API的套件分开,但在ASP.NET Core中MVC及 ...
随机推荐
- 【CCPC-Wannafly Winter Camp Day4 (Div1) H】命命命运(概率DP)
点此看题面 大致题意: 有\(6\)个人玩大富翁,共有\(n\)块地,进行\(500\)轮,已知每个人掷骰子掷出\(1\sim6\)的概率.当某人到达一块未被占领的地时,他可以占领它.求最后每个人占有 ...
- [Pytorch] pytorch笔记 <三>
pytorch笔记 optimizer.zero_grad() 将梯度变为0,用于每个batch最开始,因为梯度在不同batch之间不是累加的,所以必须在每个batch开始的时候初始化累计梯度,重置为 ...
- 从Internet下载一个文件
使用的.net框架下的System.Net.WebClient类的DownloadFile()方法以图片为例:
- 2018.7.19 . php复习
PHP程序设计 1.请写出HTML标记meta的完整英文单词:metadata 2.相当于http文件头作用(向浏览器传回正确和精确地显示网页内容的消息)的meta标记的属性是http-equiv 3 ...
- AI-Info-Micron-Insight:5G、人工智能和即将到来的移动革命
ylbtech-AI-Info-Micron-Insight:5G.人工智能和即将到来的移动革命 1.返回顶部 1. 5G.人工智能和即将到来的移动革命 人们都说自己的手机“智能”,但究竟有多智能?凡 ...
- Adobe Photoshop CS6下载安装
下载链接 http://yunpan.cn/cACgP6Lv5ygit (提取码:f37a) 第一步关键是断开网络,拔掉网线或断开无线 点击登录后,如果没有进入下面的界面,检查是否断网成功 至于选择安 ...
- iOS 检测版本更新(02)
iOS 检测版本更新 如果我们要检测app版本的更新,那么我们必须获取当前运行app版本的版本信息和appstore 上发布的最新版本的信息. 当前运行版本信息可以通过info.plist文件中的bu ...
- 时间复杂度 log n
时间复杂度 O(log n) 意味着什么? 预先知道算法的复杂度是一回事,了解其后的原理是另一件事情. 不管你是计算机科班出身还是想有效解决最优化问题,如果想要用自己的知识解决实际问题,你都必须理解时 ...
- JavaScript数组常用的方法
改变原数组: ※ push,pop,shif,unshift,sort,reverse ※ splice 不改变原数组: ※ concat,join→split,toString,slice push ...
- float 浮动详解
浮动(float):浮动原先设定时主要是用于文本环绕图像设定的,后来发现其在css布局中有很大的帮助,故渐渐使用浮动. 浮动后的元素脱离了文档的普通流,使得浮动的元素不占据文档的位置,其他元素可以覆盖 ...