原文连接: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 中注册了认证系统的三大核心对象:IAuthenticationSchemeProviderIAuthenticationHandlerProvider 和 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中都有对应的实现,而SignInAsyncSignOutAsync则使用了独立的定义接口:

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(笔记)的更多相关文章

  1. sql server 关于表中只增标识问题 C# 实现自动化打开和关闭可执行文件(或 关闭停止与系统交互的可执行文件) ajaxfileupload插件上传图片功能,用MVC和aspx做后台各写了一个案例 将小写阿拉伯数字转换成大写的汉字, C# WinForm 中英文实现, 国际化实现的简单方法 ASP.NET Core 2 学习笔记(六)ASP.NET Core 2 学习笔记(三)

    sql server 关于表中只增标识问题   由于我们系统时间用的过长,数据量大,设计是采用自增ID 我们插入数据的时候把ID也写进去,我们可以采用 关闭和开启自增标识 没有关闭的时候 ,提示一下错 ...

  2. Asp.Net Core WebApi学习笔记(四)-- Middleware

    Asp.Net Core WebApi学习笔记(四)-- Middleware 本文记录了Asp.Net管道模型和Asp.Net Core的Middleware模型的对比,并在上一篇的基础上增加Mid ...

  3. ASP.NET Core 2 学习笔记(七)路由

    ASP.NET Core通过路由(Routing)设定,将定义的URL规则找到相对应行为:当使用者Request的URL满足特定规则条件时,则自动对应到相符合的行为处理.从ASP.NET就已经存在的架 ...

  4. ASP.NET Core 2 学习笔记(十三)Swagger

    Swagger也算是行之有年的API文件生成器,只要在API上使用C#的<summary />文件注解标签,就可以产生精美的线上文件,并且对RESTful API有良好的支持.不仅支持生成 ...

  5. ASP.NET Core 2 学习笔记(十二)REST-Like API

    Restful几乎已算是API设计的标准,通过HTTP Method区分新增(Create).查询(Read).修改(Update)和删除(Delete),简称CRUD四种数据存取方式,简约又直接的风 ...

  6. ASP.NET Core 2 学习笔记(十)视图

    ASP.NET Core MVC中的Views是负责网页显示,将数据一并渲染至UI包含HTML.CSS等.并能痛过Razor语法在*.cshtml中写渲染画面的程序逻辑.本篇将介绍ASP.NET Co ...

  7. ASP.NET Core 2 学习笔记(一)开始

    原文:ASP.NET Core 2 学习笔记(一)开始 来势汹汹的.NET Core似乎要取代.NET Framework,ASP.NET也随之发布.NET Core版本.虽然名称沿用ASP.NET, ...

  8. Asp .Net core 2 学习笔记(2) —— 中间件

    这个系列的初衷是便于自己总结与回顾,把笔记本上面的东西转移到这里,态度不由得谨慎许多,下面是我参考的资源: ASP.NET Core 中文文档目录 官方文档 记在这里的东西我会不断的完善丰满,对于文章 ...

  9. ASP.NET Core 2 学习笔记(六)MVC

    ASP.NET Core MVC跟ASP.NET MVC观念是一致的,使用上也没有什么太大的变化.之前的ASP.NET MVC把MVC及Web API的套件分开,但在ASP.NET Core中MVC及 ...

随机推荐

  1. 7 MSSQL数据库备份与恢复

    0 MSSQL数据库备份 1 SQLAgent配置 2 设置连接属性 3 输入SA账号密码 4 SQL备份脚本配置 5 生成SQL全量备份脚本 6 生成SQL差异备份脚本 7 修改SQL差异备份脚本 ...

  2. Linux 启动、停止、重启tomcat工具(Shell脚本)

    1.   启动 #!/bin/bash pids=`ps -ef | grep java | grep -w tomcat | awk '{print $2}'` #pids=`ps -ef | gr ...

  3. [P1363] 幻想迷宫

    题目链接 很好的一道搜索题,应该是利用了离散化的思想我好蒟蒻呀 地图是根据给定的图无限的拼接的. 所以说暴力建图是不可取的. 其实不难看出,在跨越两张图时.我们就可以看做这个点时空穿梭一般.从底下回来 ...

  4. Search in Rotated Sorted Array——LeetCode

    Suppose an array sorted in ascending order is rotated at some pivot unknown to you beforehand. (i.e. ...

  5. 批量删除xml文件中的<?xml version="1.0" ?>

    #!/bin/shcd 'home/usrname/'ls cd '/home/usrname/VOC2007/Annotations/' for file in `ls /home/usrname/ ...

  6. 基于java开发的开源代码GPS北斗位置服务监控平台

    最近在研究位置服务平台,基于全球卫星定位技术(GNSS).互联网技术.空间地理信息技术(GIS).3G/4G无线通信技术,面向全国公众用户建立大容量.实时.稳定的位置信息服务运营平台.实现管理目标的实 ...

  7. react中密码自动填充及解决火狐浏览器,360浏览器记住密码后,密码框自动填充终极解决方案

    先直接上核心代码如下: 在火狐浏览器,360浏览器,初次加载,bug长这样: 如果你想通过生命周期componentDidMounted等生命周期进行置空操作都是不行的,这可能是浏览器自带的特性记住密 ...

  8. 阿里云OSS图片上传plupload.js结合jq-weui 图片上传的插件

    项目中用到了oss上传,用的plupload,奈何样式上不敢恭维,特别是放在移动端上使用.于是自己把它移植到了jq weui的上传图片组件上. 更改:选择照片后确认即及时上传至oss服务器,不限制上传 ...

  9. P2661 信息传递 DFS

    题目链接:洛谷 P2661 信息传递 一个人要想知道自己的生日,就意味着信息的传递是成环的,因为每轮信息只能传递一个人,传递的轮数就等于环的大小 环的大小就等于环中的两个点到第三个点的距离之和加一,我 ...

  10. flask实现基于elasticsearch的关键词搜索建议

    1.实现效果 2.fuzzy模糊查询和suggest查询 fuzzy模糊查询 GET chaxun/job/_search { "query": { "fuzzy&quo ...