详解ASP.NET Core 之 Identity 入门(三)

 更新时间:2016年12月13日 17:03:25   作者:Savorboard  
本篇文章主要介绍了ASP.NET Core 之 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小孔子cms中的数据添加修改

    asp.net小孔子cms中的数据添加修改

    最近都在看小孔子cms的代码,其添加与修改数据十分方便,做下笔记,代码主要提取自小孔子cms,去掉了不用的函数并把相关代码写到一个文件中
    2008-08-08
  • ASP.NET XmlHttp跨域访问实现代码

    ASP.NET XmlHttp跨域访问实现代码

    最近项目需要实现XmlHttp的POST方法到另一服务器上的页面进行数据的更新,可是IE会提出“该页正在访问其控制范围之外的信息,是否继续?”等警告信息,而在其他浏览器上直接禁止掉,GOOGLE一下原来是XmlHttp的跨域访问问题,找了很多资料,说是提供很多解决方案,可是都没有用处。
    2008-11-11
  • html+ashx 表单提交示例

    html+ashx 表单提交示例

    这篇文章主要介绍了html+ashx 表单提交的具体实现,需要的朋友可以参考下
    2014-03-03
  • .NET9 EFcore支持早期MSSQL数据库 ROW_NUMBER()分页功能

    .NET9 EFcore支持早期MSSQL数据库 ROW_NUMBER()分页功能

    文章介绍了如何在.NET 9中使用EF Core实现对早期MSSQL数据库的ROW_NUMBER()分页兼容,由于EF Core 9对底层API进行了重大更新,原有的兼容代码需要重新实现,具体实现代码跟随小编一起看看吧
    2024-11-11
  • datatable行转列示例分享

    datatable行转列示例分享

    这篇文章主要介绍了datatable行转列示例,指定需要转换的表和转换表表头对应旧表字段就可以转换了
    2014-01-01
  • 分析10个ASP.NET控件最有用的属性详解

    分析10个ASP.NET控件最有用的属性详解

    对于微软平台开发人员而言,每天都要和各种ASP.NET控件打交道,本文不打算介绍各种控件的具体使用方法,相反,我只想和大家分享一下ASP.NET控件最有用的10个属性,相信读完本文可提升你的开发技能。
    2011-08-08
  • ASP.NET MVC实现文件下载

    ASP.NET MVC实现文件下载

    这篇文章介绍了ASP.NET MVC实现文件下载的方法,文中通过示例代码介绍的非常详细。对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2022-07-07
  • .NET Core 1.0创建Self-Contained控制台应用

    .NET Core 1.0创建Self-Contained控制台应用

    这篇文章主要为大家详细介绍了.NET Core 1.0创建Self-Contained控制台应用的相关资料,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2017-04-04
  • Asp.Net实现404页面与301重定向的方法

    Asp.Net实现404页面与301重定向的方法

    这篇文章主要介绍了Asp.Net实现404页面与301重定向的方法,较为详细的分析了404页面的原理与针对404错误与301跳转的实现方法,是非常实用的技巧,需要的朋友可以参考下
    2014-11-11
  • Asp.net core中RedisMQ的简单应用实现

    Asp.net core中RedisMQ的简单应用实现

    这篇文章主要介绍了Asp.net core中RedisMQ的简单应用实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-12-12

最新评论