前言

上一篇文章中,我们学习了 CookieAuthentication 中间件,本篇的话主要看一下 Identity 本身。

最早2005年 ASP.NET 2.0 的时候开始, Web 应用程序在处理身份验证和授权有了很多的变化,多了比如手机端,平板等,所以那个时候为了适应这种变化就引入了ASP.NET Membership,但是随着时间的发展一些社交网站或者程序聚集了大量的用户,比如Facebook,Twitter,QQ等,这个时候用户希望能够使用他们在这些社交站点身份来登陆当前网站,这样可以免除注册这些琐碎而又必要的操作,用户也不必记住大量的账户密码。

又随着互联网的发展,越来越多的开发者不只是关注具体业务代码的编写,转变为开始关注应用程序代码的单元测试,这已经是开发者关注的核心。所以在2008年,ASP.NET 团队引入了 MVC 框架,这样来帮助开发者很方便的构建单元测试,同时开发者希望他们的 Membership 系统也能够做到这一点。

基于以上,ASP.NET Identity 应运而生。

Identity 要解决的问题

很多开发人员说他们不愿意使用Identity,自己实现要方便的多,OK,那么需求来了?以下就是我针对此次任务给你提出来的需求。

身份系统

  • 可以同时被所有的ASP.NET 框架使用(Web MVC,Web Forms,Web Api,SignalR)
  • 可以应用于构建 Web, 手机,存储,或者混合应用。

能够对用户资料(User Profile)很方便的扩展

  • 可以针对用户资料进行扩展。

持久化

  • 默认把用户信息存储在数据库中,可以支持使用EF进行持久化。(可以看到,EF 其实只是Identity的一个功能点而已
  • 可以控制数据库架构,更改表名或者主键的数据类型(int,string)
  • 可以使用不同的存储机制(如 NoSQL,DB2等)

单元测试

  • 使WEB 应用程序可以进行单元测试,可以针对ASP.NET Identity编写单元测试

角色机制

  • 提供角色机制,可以使用不同的角色来进行不同权限的限制,可以轻松的创建角色,向用户添加角色等。

要支持基于Claims

  • 需要支持基于 Claims 的身份验证机制,其中用户身份是一组Claims,一组Claims可以比角色拥有更强的表现力,而角色仅仅是一个bool值来表示是不是会员而已。

第三方社交登陆

  • 可以很方便的使用第三方登入,比如 Microsoft 账户,Facebook, Twitter,Google等,并且存储用户特定的数据。

封装为中间件

  • 基于中间件实现,不要对具体项目产生依赖
  • 基于 Authorzation 中间件实现,而不是使用 FormsAuthentication 来存储cookie。

NuGet包提供

  • 发布为 Nuget 包,这样可以容易的进行迭代和bug修复,可以灵活的提供给使用者。

以上,就是我提出来的需求,如果让你来封装这样一个用户身份认证组件,你会不是想到以上的这些功能点,那针对于这些功能点你又会怎么样来设计呢?

下面来看一下 Identity 怎么样设计的吧。

Getting Started

抽丝剥茧,我们先从入口看一下其使用方式。 首先我们打开 Startup.cs 文件,然后添加如下代码:

public class Startup
{ public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(Configuration["Data:DefaultConnection:ConnectionString"])); services.AddIdentity<ApplicationUser, IdentityRole>(options => {
options.Cookies.ApplicationCookie.AuthenticationScheme = "ApplicationCookie";
options.Cookies.ApplicationCookie.CookieName = "Interop";
})
.AddEntityFrameworkStores<ApplicationDbContext>()
.AddDefaultTokenProviders();
} public void Configure(IApplicationBuilder app)
{
// 使用了 CookieAuthentication 中间件做身份认证
app.UseIdentity();
}
}

在 ConfigureServices 中,先是注册了数据库上下文,然后又 services.AddIdentity() 我们看一下里面都注册了哪些服务呢?

public static IdentityBuilder AddIdentity<TUser, TRole>(
this IServiceCollection services,
Action<IdentityOptions> setupAction)
where TUser : class
where TRole : class
{
// 这个就是被 Identity 使用的
services.AddAuthentication(options =>
{
// This is the Default value for ExternalCookieAuthenticationScheme
options.SignInScheme = new IdentityCookieOptions().ExternalCookieAuthenticationScheme;
}); // 注册 IHttpContextAccessor ,会用到
services.TryAddSingleton<IHttpContextAccessor, HttpContextAccessor>(); // Identity services
services.TryAddSingleton<IdentityMarkerService>();
services.TryAddScoped<IUserValidator<TUser>, UserValidator<TUser>>();
services.TryAddScoped<IPasswordValidator<TUser>, PasswordValidator<TUser>>();
services.TryAddScoped<IPasswordHasher<TUser>, PasswordHasher<TUser>>();
services.TryAddScoped<ILookupNormalizer, UpperInvariantLookupNormalizer>();
services.TryAddScoped<IRoleValidator<TRole>, RoleValidator<TRole>>(); // 错误描述信息
services.TryAddScoped<IdentityErrorDescriber>();
services.TryAddScoped<ISecurityStampValidator, SecurityStampValidator<TUser>>(); //身份当事人工厂
services.TryAddScoped<IUserClaimsPrincipalFactory<TUser>, UserClaimsPrincipalFactory<TUser, TRole>>(); //三大对象
services.TryAddScoped<UserManager<TUser>, UserManager<TUser>>();
services.TryAddScoped<SignInManager<TUser>, SignInManager<TUser>>();
services.TryAddScoped<RoleManager<TRole>, RoleManager<TRole>>(); if (setupAction != null)
{
services.Configure(setupAction);
} return new IdentityBuilder(typeof(TUser), typeof(TRole), services);
}

看了以上代码后,基本上知道了 Identity 他的设计的一个架构了,通过此结构我们也能够学习到我们自己封装一个中间件的时候,该怎么样来组织我们的代码结构,怎么样的利用 ASP.NET Core 给我们提供的依赖注入来更好的解耦,下面我们来看一下通过以上代码我们能够学到什么东西:

1、 在 public static IdentityBuilder AddIdentity<TUser, TRole>(this IServiceCollection services, Action<IdentityOptions> setupAction) 这个扩展方法中,提供了一个参数 Action<IdentityOptions>,这个是干什么用的呢? 这个是我们在设计一个中间件的时候,有需要外部提供的参数,就会设计一个 Options 的类用来接受外部的参数,然后封装为一个Action委托的方式提供。在使用到的地方就可以以 IOption xxx 的形式注入进来使用了。

2、services.TryAddScoped<Interface,Implement>(),这个注册方式就是说,如果检测到DI容器里面已经有了当前要注册的Interface或者Service,就不会再次注册,没有才会注册进去。 那么为什么此处要这样用呢? 这是因为如果用户已经实现了此接口并且已经注册的容器当中的话,就使用用户注册的,而不是中间件自身的。用户就能很好的对中间件提供的功能进行自定义了,这就是OO中的多态性,这就是里氏替换原则。

3、如果你能理解第2条的话,那么你应该知道为什么会在服务中注册 错误描述信息 那个IdentityErrorDescriber 了吧,也能够解决你想提示账号密码错误,无奈Identity输出是英文问题的提示。

4、三大对象,这个是 Identity 的核心了,所以学习 Identity 的话,在看完博客 ASP.NET Core 之 Identity 入门()之后,学这三个对象就够了。

SignInManager: 主要处理注册登录相关业务逻辑。

UserManager: 处理用户相关添加删除,修改密码,添加删除角色等。

RoleManager:角色相关添加删除更新等。

有些同学可能很好奇,都没有依赖具体的数据库或者是EF,是怎么样做到的增删改查的呢?

这个时候,就需要几个 Store 接口派上用场了。以下是Identity中定义的Store接口:

  • IQueryableRoleStore
  • IQueryableUserStore
  • IRoleClaimStore
  • IRoleStore
  • IUserAuthenticationTokenStore
  • IUserClaimStore
  • IUserEmailStore
  • IUserLockoutStore
  • IUserLoginStore
  • IUserPasswordStore
  • IUserPhoneNumberStore
  • IUserRoleStore
  • IUserSecurityStampStore
  • IUserStore
  • IUserTwoFactorStore

有了这些接口之后,是不是豁然开朗了,原来 Identity 是通过这种方式实现的持久化机制,依赖抽象接口而不是依赖具体的细节实现,这就是面向对象中的依赖倒置原则呀。

Identity 和 EntityFramework

Identity 和 EntityFramework的关系,相信上个章节看懂了之后,就很容易明白了,对的,EF 只是针对于上述 Store 接口的实现,不信你看截图的源码:

Identity 打头的那些类文件都是定义的需要持久化的Entity对象,Store结尾的那些就是接口的实现啦。

第三方的 Identity 实现

除了 EF 是官方默认提供的持久化库之外,还有一些第三方的库,当然你也可以自己使用 ADO.NET 或者 Drapper 实现。

MangoDb 针对于 Identity 提供的实现: https://github.com/tugberkugurlu/AspNetCore.Identity.MongoDB

LinqToDB 针对于 Identity 提供的实现:https://github.com/linq2db/LinqToDB.Identity

总结

这篇博文写了蛮久的时间的,一方面是因为在构思怎么样的思路来让大家更好的理解,而不仅仅是使用。因为有太多的文章介绍Identity 的使用方式以及代码了,但是最后大家还是不会用。后来想到如果让别人想要理解你的库也好代码也好,让其知道诞生的背景是很重要的,因为这才是设计的初衷。另一方面是因为Connect() 2016 大会上,.NET Core 发布了 1.1 版本,除了把项目升级到1.1之外,也在学习1.1新的一些东西,以便更好给大家分享。

授人以鱼不如授人以渔, 一篇好的博文确实要花费作者更多的心血和时间。如果你觉得这篇博文对您有帮助,感谢您的【推荐】。

同时,ASP.NET Core 之 Identity 系列也结束了,感谢支持的朋友们。如果你对 .NET Core 感兴趣可以关注我,我会定期在博客分享关于 .NET Core 的学习心得。


本文地址:http://www.cnblogs.com/savorboard/p/aspnetcore-identity3.html

作者博客:Savorboard

欢迎转载,请在明显位置给出出处及链接

ASP.NET Core 之 Identity 入门(三)的更多相关文章

  1. [转]ASP.NET Core 之 Identity 入门(三)

    本文转自:http://www.cnblogs.com/savorboard/p/aspnetcore-identity3.html 前言 在上一篇文章中,我们学习了 CookieAuthentica ...

  2. ASP.NET Core 之 Identity 入门(一)

    前言 在 ASP.NET Core 中,仍然沿用了 ASP.NET里面的 Identity 组件库,负责对用户的身份进行认证,总体来说的话,没有MVC 5 里面那么复杂,因为在MVC 5里面引入了OW ...

  3. ASP.NET Core 之 Identity 入门(二)

    前言 在 上篇文章 中讲了关于 Identity 需要了解的单词以及相对应的几个知识点,并且知道了Identity处在整个登入流程中的位置,本篇主要是在 .NET 整个认证系统中比较重要的一个环节,就 ...

  4. [转]ASP.NET Core 之 Identity 入门(二)

    本文转自:http://www.cnblogs.com/savorboard/p/aspnetcore-identity2.html 前言 在 上篇文章 中讲了关于 Identity 需要了解的单词以 ...

  5. [转]ASP.NET Core 之 Identity 入门(一)

    本文转自:http://www.cnblogs.com/savorboard/p/aspnetcore-identity.html 前言 在 ASP.NET Core 中,仍然沿用了 ASP.NET里 ...

  6. ASP.NET Core 之 Identity 入门(转载)

    原文地址:https://www.cnblogs.com/gongap/p/9504562.html 前言 在 ASP.NET Core 中,仍然沿用了 ASP.NET里面的 Identity 组件库 ...

  7. Asp.Net Core之Identity应用(上篇)

    一.前言 在前面的篇章介绍中,简单介绍了IdentityServer4持久化存储机制相关配置和操作数据,实现了数据迁移,但是未对用户实现持久化操作说明.在总结中我们也提到了, 因为IdentitySe ...

  8. asp.net core使用identity+jwt保护你的webapi(三)——refresh token

    前言 上一篇已经介绍了identity的注册,登录,获取jwt token,本篇来完成refresh token. 开始 开始之前先说明一下为什么需要refresh token. 虽然jwt toke ...

  9. ASP.NET Core 1.0 入门——了解一个空项目

    var appInsights=window.appInsights||function(config){ function r(config){t[config]=function(){var i= ...

随机推荐

  1. 《Django By Example》第四章 中文 翻译 (个人学习,渣翻)

    书籍出处:https://www.packtpub.com/web-development/django-example 原作者:Antonio Melé (译者注:祝大家新年快乐,这次带来<D ...

  2. 开源:Taurus.MVC 框架

    为什么要创造Taurus.MVC: 记得被上一家公司忽悠去负责公司电商平台的时候,情况是这样的: 项目原版是外包给第三方的,使用:WebForm+NHibernate,代码不堪入目,Bug无限,经常点 ...

  3. 初步了解nodejs

    什么是Node.js? 很多初学者并没有真正地理解Node.js到底是什么.nodejs.org网站中的描述也没有多大帮助. 首先要清楚Node不是一个Web服务器,这十分重要.它本身并不能做任何事情 ...

  4. 微信小程序前端源码逻辑和工作流

    看完微信小程序的前端代码真的让我热血沸腾啊,代码逻辑和设计一目了然,没有多余的东西,真的是大道至简. 废话不多说,直接分析前端代码.个人观点,难免有疏漏,仅供参考. 文件基本结构: 先看入口app.j ...

  5. 基于Vue2.0的单页面开发方案

    2016的最后一天,多多少少都应该总结一下这一年的得失,哪里做的好,哪里需要改进,记一笔,或许将来会用到呢. 毕业差不多半年了,一直是一个人在负责公司项目的前端开发与维护,当时公司希望前后端分离,提高 ...

  6. 【SAP业务模式】之ICS(三):前台操作

    本片博文开始讲解SAP前台是如何实现ICS业务模式的. 一.VA01开立销售订单 我这里为了方便,创建了一个订单类型ZMIV作为公司间销售的订单类型,其实公司间销售订单跟标准的销售订单是一致的.同时, ...

  7. Git 学习看这篇就够了!

    Git是一个开源的分布式版本控制系统,可以有效.高速的处理从很小到非常大的项目版本管理. 可能新手会问"git和github有什么关系啊?" git是一个版本控制工具: githu ...

  8. 记录在Windows上安装和使用Oracle数据库过程中的坑

    1.安装Oracle Oracle软件是免费的,可以去官网下载相应的安装包.但是如果用于商业用途需要购买License.官网上针对各种平台,32位和64位都有,如果在Windows一般会下载到两个文件 ...

  9. 我的MYSQL学习心得(十一) 视图

    我的MYSQL学习心得(十一) 视图 我的MYSQL学习心得(一) 简单语法 我的MYSQL学习心得(二) 数据类型宽度 我的MYSQL学习心得(三) 查看字段长度 我的MYSQL学习心得(四) 数据 ...

  10. Mono 3.2.3 TCP吞吐性能测试报告

    在前几天简单地测试了一下Mono 3.2.3 TCP处理的稳定性,有同学问Mono 3.2.3的TCP处理性有怎样,以下是针对Mono 3.2.3TCP在吞吐方面的性能测试.主要测试分两种场分别是连接 ...