ASP.NET Identity

原文地址:http://www.cnblogs.com/jesse2013/p/membership-part3.html
ASP.NET Identity

基础示例

  ASP.NET Identity是在.NET Framework4.5中引入的,从Membership发布以来,我想微软已经从开发者以及企业客户那里面得到了足够的反馈信息来帮助他们打造这样一套新的框架。他所拥有的特点大多也是前面所不能满足的,至少我们看到的是进步,不是么?

  1. 一套ASP.NET Identity,可以用于ASP.NET下的web form, MVC, web pages, web API等
  2. 和Simple Membership Provider,可以灵活订制用户信息,同样采用EF Code First来完成数据操作
  3. 完全自定义数据结构
  4. 单元测试的支持
  5. 与Role Provider集成 
  6. 支持面向Clamis的认证
  7. 支持社交账号的登录
  8. OWIN 集成
  9. 通过NuGet发布来实现快速迭代

  瞟一眼好处还真不少,但是至少对于开发者来说,好用,能满足需求,灵活才是王道,那我们下面就来看看如何使用ASP.NET Identity来完成我们的用户授权和认证模块。其实我们已经不用写任何示例代码,因为我们只要使用VS创建一个.NET Framework 4.5 的 MVC站点,所有的代码都已经包括了。

默认创建的IdentityModels.cs

1
2
3
4
5
6
7
8
9
public  class  ApplicationUser : IdentityUser
{    }
public  class  ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
     public  ApplicationDbContext()
         base ( "DefaultConnection" )
     {
     }
}

  我们需要在ApplicaitonUser实体中添加我们的用户字段就可以了,同时我们还可以很简单的更改表名。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public  class  ApplicationUser : IdentityUser
{
     public  string  FirstName {  get set ; }
     public  string  LastName {  get set ; }
     public  int  Age {  get set ; }
     public  string  City {  get set ; }
}
public  class  ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
     public  ApplicationDbContext()
         base ( "DefaultConnection" ){}
     protected  override  void  OnModelCreating(DbModelBuilder modelBuilder)
     {
         base .OnModelCreating(modelBuilder);
 
         // 默认表名是AspNetUsers,我们可以把它改成任意我们想要的
         modelBuilder.Entity<IdentityUser>()
             .ToTable( "Users" );
         modelBuilder.Entity<ApplicationUser>()
             .ToTable( "Users" );
     }
}

  接下来,你就可以run一下你的网站,来体验一把ASP.NET Identity了,别忘了先把web.config里面的连接字符串改一下,方便我们自己去查看数据库,只要设置一下数据库就可以了,创建工作就交给EF吧。

ASP.NET Identity_第1张图片

  我们可以在AccountController中找到所有的相关代码。

初始化UserManager对象

1
2
3
4
5
6
7
8
9
public  AccountController()
     this ( new  UserManager<ApplicationUser>( new  UserStore<ApplicationUser>( new  ApplicationDbContext())))
{
}
public  AccountController(UserManager<ApplicationUser> userManager)
{
     UserManager = userManager;
}
public  UserManager<ApplicationUser> UserManager {  get private  set ; }

登录核心代码

1
2
3
4
5
6
var  user = await UserManager.FindAsync(model.UserName, model.Password);
if  (user !=  null )
{
     await SignInAsync(user, model.RememberMe);
     return  RedirectToLocal(returnUrl);
}

注册核心代码

1
2
var  user =  new  ApplicationUser() { UserName = model.UserName };
var  result = await UserManager.CreateAsync(user, model.Password);

 框架设计

  我们上面是直接利用VS帮助我们创建好了一些初始代码,我们也可以创建一个空白的站点,然后再把ASP.NET Identity引用进来。所需要的类库可以直接从Nuget上下载就可以了。

  • Microsoft.AspNet.Identity.EntityFramework

  主要包括ASP.NET Identity 的EF 部分的实现,有了EF的帮助我们就可以完全自定义数据结构,当然我们也只需要定义一个实体类就可以了。  

  • Microsoft.AspNet.Identity.Core 

  名字就已经告诉大家了,这是ASP.NET Identity的核心了,所以主要的功能在这里面。上面那个包是ASP.NET Identity EF的实现,那么我们可以在这个核心包的基础上扩展出基于No SQL, Azure Storage 的 ASP.NET Identity实现。

  • Microsoft.AspNet.Identity.OWIN

  ASP.NET Identity对OWIN 认证的支持。

ASP.NET Identity_第2张图片

  最上面两个就是我们自己创建的代码,分别继承自己Microsoft.AspNet.Identity.EntityFramework的IdentityUser和IdentityDbContext。但是最后别忘了,我们与用户相关的操作实际上是通过Microsoft.AspNet.Identity.Core的 UserManager类来完成的。通过这样一种设计,可以把具体定义和实现交给上层,但是最后的核心却完全由自己掌控,实现松耦合,高内聚(一不小心我竟然说出了这么专业的解释,小心脏砰砰跳呀!)。

框架实现剖析

  上面只是一张粗略的类图,下面我们就来看一下这些类之间是如何关联起来协作的。我们通过上面基础示例的代码可以发现,用用户相关的功能是通过调用UserManager的方法来完成的。 我们可以在AccountController中找到UserManager的初始代码:

1
new  UserManager<ApplicationUser>( new  UserStore<ApplicationUser>( new  ApplicationDbContext()));

  虽然所说有的方法通过UserManager来调用,但是最后实现的还是UserStore,并且如果我们找到UserManager的定义,会发现实际上它所接收的正是在Microsoft.AspNet.Identity.Core中定义的IUserStore接口。

1
2
3
4
public  UserManager(IUserStore<TUser> store)
{
     this .Store = store;
}

  我们现在使用的是ASP.NET Identity EF的实现,所以在UserStore中,直接调用传进来的DbContext的Save操作就可以了。

ASP.NET Identity_第3张图片

  有没有发现这张图和我们第二篇中讲的Provider模式有那么点点的神似? 在Membership中,我们所有的操作通过调用Membership来过多成,但是Membership本身只是一个包装类,内部的操作实际上是通过Provider的实际类来完成的,这就是策略模式的典型案例。只不过Membership的Provider通过web.config配置完成,而UserManager通过构造函数注入完成。

扩展ASP.NET Identity - 将用户信息写入文件 

  为了熟悉AspNet.Identity的结构,我们来扩展实现一个将用户信息写入文件的组件,然后实现登录注册功能,我们就给它命名AspNet.Identity.File吧。

  1. 创建一个自己的用户类(UserIdentity)实现Microsoft.AspNet.Identity.IUser接口
  2. 创建一个自己的UserStore类实现Microsoft.AspNet.Identity.IUserStore<TUser>接口
  3. 作为演示,我们的用户类就尽量简单,只有id,用户名,和密码三个属性
  4. 我们的UserStore,也只重写了Get和Create几个基本的方法,没有重写Update。

UserIdentity.cs 代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
public  class  IdentityUser : IUser
{
     public  string  Id {  get set ; }
 
     public  string  UserName {  get set ;}
 
     public  string  PasswordHash{  get set ; }
 
     public  override  string  ToString()
     {
         return  string .Format( "{0},{1},{2}" this .Id,  this .UserName,  this .PasswordHash);
     }
 
     public  static  IdentityUser FromString( string  strUser)
     {
         if  ( string .IsNullOrWhiteSpace(strUser))
         {
             throw  new  ArgumentNullException( "user" );
         }
 
         var  arr = strUser.Split( ',' );
         if  (arr.Length != 3)
         {
             throw  new  InvalidOperationException( "user is not valid" );
         }
         var  user =  new  IdentityUser();
         user.Id = arr[0];
         user.UserName = arr[1];
         user.PasswordHash = arr[2];
 
         return  user;
     }
}

UserStore.cs的核心代码

// 创建用户
public  async Task CreateAsync(IdentityUser user)
{
     user.Id = Guid.NewGuid().ToString();
     using  ( var  stream = new  IO.StreamWriter(_filePath, true , Encoding.UTF8))
     {
         await stream.WriteLineAsync(user.ToString());
     }
}
 
// 根据用户名找用户
public  async Task<IdentityUser> FindByNameAsync( string  userName)
{
     using  ( var  stream = new  IO.StreamReader(_filePath))
     {
         string  line;
         IdentityUser result = null ;
         while  ((line = await stream.ReadLineAsync()) != null )
         {
             var  user = IdentityUser.FromString(line);
             if  (user.UserName == userName)
             {
                 result = user;
                 break ;
             }
         }
         return  result;
     }
}

AccountController.cs核心代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 初始化 UserManager
public  AccountController()
     this ( new  UserManager<IdentityUser>( new  UserStore(System.Web.HttpContext.Current.Server.MapPath( "~/App_Data/user.txt" ))))
{ }
// 检查用用户名密码是否正确
var  user = await UserManager.FindAsync(model.UserName, model.Password);
if  (user !=  null )
{
     // Forms 登录代码
}
// 注册用户
var  user =  new  IdentityUser() { UserName = model.UserName };
var  result = await UserManager.CreateAsync(user, model.Password);
if  (result.Succeeded)
{
     // 创建用户成功
}

   保存到txt中的用户信息

 小结

  Membership系列这三篇,从入门到精通到这里就算是结束了,不知道能不能算是园满。因为这三篇的关注度都不是很高,可能没有从多少人在乎这个玩意。不过还是要感谢@好玩一人的催促,让我坚持把这三篇写完了。可能Membership不是.NET里面非常成功的一部份,但是这并不能说它不好,而是因为像这种需求的东西如果要做成类库本身就是一项比较困难的事情,因为几乎很少有一模一样的需求。

  但是我们更应该关注的是微软是如何面对复杂多变的需求来设计框架的,如何从一大堆的零散需求中找出最核心的部份, 他们如何解耦,如何提高可扩展性和维护性的。从Membersihp引入.NET的时候给我们带来了Provider,于是我们会发现.NET2.0开始就出现了各种Provider,web.config里面各种配置。而最新的ASP.NET Identity已经不再用那样的Provider模式了,但是思想却大致相同,只不过换成了用范型来实现,用构造函数注入,这也是从MVC以来微软框架的一些特色。而我们,在追求微软技术的同时,更应该理解其内在的一些思想和本质,这样才不致于被淹没在无尽的新技术中,因为很多其实只是换汤不换药,或者我们可以用积极的话来说,微软在不断的提高开发人员的效率,并且让你写代码的时候有更好的心情。 请相信我,理解了本质,再去学习新技术,能让你效率翻倍。

  最后,还是谢谢大家一直的关注和陪伴。 

  下面的demo的链接下载,包括一个ProfileProvider的例子,和后面将用户信息写入txt文件的例子。

      AspNet.Identity.File:  http://pan.baidu.com/s/1dD5SZ1v

      ProfileProvider Demo:  http://pan.baidu.com/s/1bnnakZt

 

你可能感兴趣的:(ASP.NET Identity)