Identity(三)


本文摘自木宛城主的 ASP.NET MVC 随想录——开始使用ASP.NET Identity,初级篇 - 木宛城主 - 博客园

由于ASP.NET Membership、ASP.NET Simple Membership 、ASP.NET Universal Providers 设计上的不足,微软在接受了大量反馈后,于.NET Framework 4.5 中推出了ASP.NET Identity,如果用一句话概括——ASP.NET Identity 为ASP.NET 应用程序提供了一系列的API用来管理和维护用户 ,它包括如下新特性:

•    One ASP.NET Identity

  • ASP.NET Identity 可以用在所有的 ASP.NET 框架上,例如 ASP.NET MVC, Web Forms,Web Pages,ASP.NET Web API 和SignalR
  • ASP.NET Identity 可以用在各种应用程序中,例如Web 应用程序、移动应用、商店应用或者混合架构应用

•    易于管理用户信息

  • ASP.NET Identity提供了丰富的API ,可以方便的管理用户

•    持久化控制

  • 默认情况下,ASP.NET Identity将用户所有的数据存储在数据库中。ASP.NET Identity 使用 Entity Framework 实现其所有的检索和持久化机制。
  • 通过Code First,你可以对数据库架构的完全控制,一些常见的任务例如改变表名称、改变主键数据类型等都可以很轻易地完成。
  • 能够很容易地引入其他不同的存储机制,例如 SharePoint, Windows Azure 存储表服务, NoSQL 数据库等。不必再抛出 System.NotImplementedException 异常了。

•    单元测试能力

  • ASP.NET Identity 能让 Web 应用程序能够更好地进行单元测试。你可以为你应用程序使用了 ASP.NET Identity 的部分编写单元测试。

•    角色Provider

  • ASP.NET Identity 中的角色Provider配合ASP.NET MVC Authorize,可以让你基于角色来限制对应用程序某个部分的访问。你可以很容易地创建Admin之类的角色,并将用户加入其中。

•    基于声明的

  • ASP.NET Identity 支持基于声明的身份验证,它使用一组"声明"来表示用户的身份标识。相对于"角色","声明"能使开发人员能够更好地描述用户的身份标识。"角色"本质上只是一个布尔类型(即"属于"或"不属于"特定角色),而一个"声明"可以包含更多关于用户标识和成员资格的信息。

•    社交账号登录Provider

  • 你可以很容易的为你的应用程序加入社交账号登录功能(例如 Microsoft 账户,Facebook,,Twitter,Google 等),并将用户特定的数据存入你的应用程序。

•    Windows Azure Active Directory

  • 你还可以加入使用 Windows Azure Active Directory 进行登录的功能,并将用户特定的数据存入你的应用程序。

•    OWIN 集成

  • ASP.NET 身份验证现在是基于 OWIN 中间件实现,并且可以在任何基于 OWIN 的宿主上使用。ASP.NET Identity 不依赖System.Web程序集,与此同时,它完全兼容于 OWIN 框架,并且能被用在任何基于OWIN 的Host和Server 之上。
  • ASP.NET Identity使用OWIN Authentication来登录、登出操作。这意味着应用程序使用CookieAuthentication 生成 cookie 而非FormsAuthentication 。

•    NuGet 包

  • ASP.NET Identity 作为一个 NuGet 包进行发布,并且安装在ASP.NET MVC,Web Forms 和 ASP.NET Web API 项目模板中。当然,你也可以从 NuGet 库中下载它。
  • ASP.NET Identity以NuGet包的形式发布,这样能让ASP.NET 团队更好的Bug修复和迭代新功能,与此同时,开发人员可以在第一时间获取到最新版本。

 

问题解决方案步骤
Install ASP.NET Identity. 安装ASP.NET Identity

Add the NuGet packages and define a connection string and an OWIN start class in the Web.config file.

添加NuGet包,并在Web.config文件中定义一个链接字符串和一个OWIN 启动类

1-4

Prepare to use ASP.NET Identity.

使用ASP.NET Identity的准备

Create classes that represent the user, the user manager, the database
context, and the OWIN start class.

创建表示用户、用户管理器、数据库上下文的类,以及OWIN类

5-8

Enumerate user accounts.

枚举用户账号

Use the Users property defined by the user manager class.

使用由用户管理器类定义的Users属性

9-10

Create user accounts.

创建用户账号

Use the CreateAsync method defined by the user manager class.

使用由用户管理器类定义的CreateAsync方法

11-13

Enforce a password policy.

强制口令策略

Set the PasswordValidator property defined by the user manager class, either using the built-in PasswordValidator class or using a custom derivation.

设置由用户管理器类定义的PasswordValidator属性,既可以使用内 建的PasswordValidator类,也可以使用自定义的派生类

14-16

Validate new user accounts.

验证新的用户账号

Set the UserValidator property defined by the user manager class, either using the built-in UserValidator class or using a custom derivation.
设置由用户管理器类定义的UserValidator属性,既可以使用内建的 UserValidator类,也可以使用自定义的派生类

17-19
Delete user accounts. 删除用户账号

Use the DeleteAsync method defined by the user manager class.

使用由用户管理器定义的DeleteAsync方法

20-22
Modify user accounts. 修改用户账号

Use the UpdateAsync method defined by the user manager class.

使用由用户管理器类定义的UpdateAsync方法

23-24

 

 

 


一、建立ASP.NET Identity

创建 ASP.NET Identity数据库

ASP.NET Identity并不像ASP.NET Membership那样依赖SQL Server架构,但关系型存储仍然是默认和最简单的实现方式,尽管近些年来NoSQL发展迅猛,但关系型数据库易于理解,仍旧是开发团队内部主流的存储选择。

ASP.NET Identity使用Entity Framework Code First来自动创建数据库架构。在此示例中,我使用localdb来创建一个空的数据库IdentityDb,然后交由Code First管理数据库架构。

localdb内置在Visual Studio中而且它是轻量级的SQL Server,能让开发者简单快速操作数据库。

添加ASP.NET Identity 包

Identity以包的形式发布在NuGet上,这能够很方便的将它安装到任意项目中,通过在Package Manger Console输入如下命令来安装Identity:

  • Install-Package Microsoft.AspNet.Identity.EntityFramework
  • Install-Package Microsoft.AspNet.Identity.OWIN
  • Install-Package Microsoft.Owin.Host.SystemWeb

在 Visual Studio中选择创建一个完整的ASP.NET MVC项目时,默认情况下该模板会使用ASP.NET Identity API自动添加通用的用户管理模块。对于初学者,我建议学习它里面API的使用,但我不推荐将它使用在正式环境中,因为它产生了过多的通用和冗余代码,有时候我们只想让它简单工作。

更新Web.config文件

若要将ASP.NET Identity使用在项目里,除了添加相应的包之外,还需要在Web.config中添加如下配置信息:

  • 数据库连接字符串
  • 指定的OWIN Startup启动项,用作初始化Middleware至Pipeline
<connectionStrings> 
  <add name="IdentityDb" providerName="System.Data.SqlClient" 
       connectionString="Data Source=(localdb)\v11.0;Initial Catalog=IdentityDb;Integrated Security=True;Connect Timeout=15;Encrypt=False;TrustServerCertificate=False; MultipleActiveResultSets=True" /> 
</connectionStrings> 
<appSettings> 
  <add key="owin:AppStartup" value="UsersManagement.IdentityConfig" /> 
</appSettings> 

 
创建Entity Framework 类

如果大家使用过ASP.NET Membership,对比过后你会发现在ASP.NET Identity扩展User信息是多么的简单和方便。

1.创建 User 类

第一个要被创建的类它代表用户,我将它命名为AppUser,继承自Microsoft.AspNet.Identity.EntityFramework 名称空间下IdentityUser,IdentityUser 提供了基本的用户信息,如Email、PasswordHash、UserName、PhoneNumber、Roles等,当然我们也可以在其派生类中添加额外的信息,代码如下:

using Microsoft.AspNet.Identity.EntityFramework; 
namespace UsersManagement.Models 
{ 
    public class AppUser:IdentityUser 
    { 
 
    } 
}
IdentityUser类定义的属性
Name 名称Description 描述
Claims

Returns the collection of claims for the user.

返回用户的声明集合

Email

Returns the user's e-mail address

返回用户的E-mail地址

Id

Returns the unique ID for the user

返回用户的唯一ID

Logins

Returns a collection of logins for the user.

返回用户的登录集合

PasswordHash

Returns a hashed form of the user password, which I use in the “Implementing the Edit Feature” section.

返回哈希格式的用户口令,在“实现Edit特性”中会用到它

Roles

Returns the collection of roles that the user belongs to.

返回用户所属的角色集

PhoneNumber

Returns the user's phone number

返回用户的电话号码

SecurityStamp

Returns a value that is changed when the user identity is altered, such as by a password change

返回变更用户标识时被修改的值,例如被口令修改的值

UserNameReturns the username

 

2.创建 Database Context 类

接下来的步骤就是创建EF Database Context 来操作AppUser。ASP.NET Identity将使用Code First 来创建和管理数据库架构。值得注意的是,Database Context必须继承自IdentityDbContext<T>,而且T为User类(在此示例即AppUser),代码如下所示:

public class AppIdentityDbContext : IdentityDbContext<AppUser> 
   { 
       public AppIdentityDbContext() : base("IdentityDb") 
       { 
       } 
 
       static AppIdentityDbContext() 
       { 
           Database.SetInitializer<AppIdentityDbContext>(new IdentityDbInit()); 
       } 
 
       public static AppIdentityDbContext Create() 
       { 
           return new AppIdentityDbContext(); 
       } 
   } 
   public class IdentityDbInit : DropCreateDatabaseIfModelChanges<AppIdentityDbContext> 
   { 
       protected override void Seed(AppIdentityDbContext context) 
       { 
           PerformInitialSetup(context); 
           base.Seed(context); 
       } 
       public void PerformInitialSetup(AppIdentityDbContext context) 
       { 
           //初始化 
       } 
   } 

上述代码中,AppIdentityDbContext 的构造函数调用基类构造函数并将数据库连接字符串的Name作为参数传递,它将用作连接数据库。同时,当Entity Framework Code First成功创建数据库架构后,AppIdentityDbContext的静态构造函数调用Database.SetInitializer方法Seed 数据库而且只执行一次。在这儿,我的Seed 类IdentityDbInit。

最后,AppIdentityDbContext 定义了 Create方法,它将被 OWIN Middleware回掉然后返回AppIdentityDbContext实例,这个实例被存储在OwinContext中。

3.创建User Manger 类

User Manager类作为ASP.NET Identity中最为重要的类之一,用来管理User。同样,自定义的User Manger类必须继承自UserManager<T >,此处T就为AppUser。UserManager<T>提供了创建和操作用户的一些基本方法并且全面支持C# 异步编程,所以你可以使用CreateAsync(Create),FindAsync(Find)、DeleteAsync(Delete)、UpdateAsync(Update)来进行用户管理,值得注意的是,它并不通过Entity Framework 来直接操作用户,而是间接调用UserStore来实现。UserStore<T>是Entity Framework 类并实现了IUserStore<T>接口,并且实现了定义在UserManger中操作用户的方法。代码如下所示:

/// <summary> 
/// 用户管理 
/// </summary> 
 public class AppUserManager : UserManager<AppUser> { 
 
     public AppUserManager(IUserStore<AppUser> store) 
         : base(store) { 
     } 
 
     public static AppUserManager Create( 
             IdentityFactoryOptions<AppUserManager> options, 
             IOwinContext context) { 
 
         AppIdentityDbContext db = context.Get<AppIdentityDbContext>(); 
         //UserStore<T> 是 包含在 Microsoft.AspNet.Identity.EntityFramework 中,它实现了 UserManger 类中与用户操作相关的方法。 
         //也就是说UserStore<T>类中的方法(诸如:FindById、FindByNameAsync...)通过EntityFramework检索和持久化UserInfo到数据库中 
         AppUserManager manager = new AppUserManager(new UserStore<AppUser>(db)); 
 
         return manager; 
     } 
 }

上述代码中,静态的Create方法将返回AppUserManger实例,它用来操作和管理用户,值得注意的是,它需要传入OwinContext对象,通过该上下文对象,获取到存储在Owin环境字典中的Database Context实例。

UserManager<T>类所定义的基本方法和操作
Name 名称Description 描述
ChangePasswordAsync(id, old, new)Changes the password for the specified user. 为指定用户修改口令
CreateAsync(user)Creates a new user without a password. 创建一个不带口令的新用户
CreateAsync(user, pass)Creates a new user with the specified password. 创建一个带有指定口令的新用户。
DeleteAsync(user)Deletes the specified user.  删除指定用户。
FindAsync(user, pass)Finds the object that represents the user and authenticates their password.
查找代表该用户的对象,并认证其口令
FindByIdAsync(id)

Finds the user object associated with the specified ID. See the “Implementing the Delete Feature” section.

查找与指定ID相关联的用户对象,参见“实现Delete特性”

FindByNameAsync(name)

Finds the user object associated with the specified name.

查找与指定名称相关联的用户对象。

UpdateAsync(user)

Pushes changes to a user object back into the database. 

将用户对象的修改送入数据库。

Users Returns an enumeration of the users.返回用户枚举

 

4.创建OWIN Startup 类

最后,通过Katana(OWIN的实现)提供的API,将Middleware 中间件注册到Middleware中,如下所示:

public class IdentityConfig 
    { 
        public void Configuration(IAppBuilder app) 
        { 
            //1.使用app.Use方法将IdentityFactoryMiddleware和参数callback回掉函数注册到Owin Pipeline中 
            //app.Use(typeof(IdentityFactoryMiddleware<T, IdentityFactoryOptions<T>>), args); 
            //2.当IdentityFactoryMiddleware中间件被Invoke执行时,执行callback回掉函数,返回具体实例Instance 
            //TResult instance = ((IdentityFactoryMiddleware<TResult, TOptions>) this).Options.Provider.Create(((IdentityFactoryMiddleware<TResult, TOptions>) this).Options, context); 
            //3.将返回的实例存储在Owin Context中 
            //context.Set<TResult>(instance); 
 
            app.CreatePerOwinContext<AppIdentityDbContext>(AppIdentityDbContext.Create); 
            app.CreatePerOwinContext<AppUserManager>(AppUserManager.Create); 
 
            app.UseCookieAuthentication(new CookieAuthenticationOptions 
            { 
                AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie, 
                LoginPath = new PathString("/Account/Login"), 
            }); 
        } 
    } 

上述代码中,通过CreatePerOwinContext方法将AppIdentityDbContext和 AppUserManager的实例注册到OwinContext中,这样确保每一次请求都能获取到相关ASP.NET Identity对象,而且还能保证全局唯一。

UseCookieAuthentication 方法指定了身份验证类型为ApplicationCookie,同时指定LoginPath属性,当Http请求内容认证不通过时重定向到指定的URL。

 


 

二、使用ASP.NET Identity

成功建立ASP.NET Identity之后,接下来就是如何去使用它了,让我们再回顾一下ASP.NET Identity的几个重要知识点:

 

  • 大多数应用程序需要用户、角色管理,ASP.NET Identity提供了API用来管理用户和身份验证
  • ASP.NET Identity 可以运用到多种场景中,通过对用户、角色的管理,可以联合ASP.NET MVC Authorize 过滤器 来实现授权功能。
获取所有的Users对象

在上一小节中,通过CreatePerOwinContext方法将AppIdentityDbContext和 AppUserManager的实例注册到OwinContext中,我们可以通过OwinContext对象的Get方法来获取到他们,将下面代码放在Controller中,方便供Action获取对象:

private AppUserManager UserManager 
{ 
      get { return HttpContext.GetOwinContext().GetUserManager<AppUserManager>(); } 
 }

在上述代码中,通过Microsoft.Owin.Host.SystemWeb 程序集,为HttpContext增加了扩展方法GetOwinContext,返回的 OwinContext对象是对Http请求的封装,所以GetOwinContext方法可以获取到每一次Http请求的内容。接着通过IOwinContext的扩展方法GetUserManager获取到存储在OwinContext中的UserManager实例。

然后,通过UserManager的Users属性,可以获取到所有的User集合,如下所示:

public ActionResult Index() 
{ 
    return View(UserManager.Users); 
} 
创建User对象

通过UserManager的CreateAsync方法,可以快速的创建User对象,如下代码创建了User ViewModel:

public class UserViewModel 
{ 
    [Required] 
    public string Name { get; set; } 
    [Required] 
    public string Email { get; set; } 
    [Required] 
    public string Password { get; set; } 
} 

使用UserManager对象的CreateAsync方法将AppUser对象将它持久化到数据库:

[HttpPost] 
public async Task<ActionResult> Create(UserViewModel model) 
{ 
    if (ModelState.IsValid) 
    { 
        var user = new AppUser {UserName = model.Name, Email = model.Email}; 
        //传入Password并转换成PasswordHash 
        IdentityResult result = await UserManager.CreateAsync(user, 
            model.Password); 
        if (result.Succeeded) 
        { 
            return RedirectToAction("Index"); 
        } 
        AddErrorsFromResult(result); 
    } 
    return View(model); 
} 

CreateAsync返回IdentityResult 类型对象,它包含如下了两个重要属性:

  • Succeeded : 如果操作成功返回True
  • Errors:返回一个字符串类型的错误集合

通过AddErrorsFromResult 方法将错误集合展示在页面上 @Html.ValidationSummary 处,如下所示:

private void AddErrorsFromResult(IdentityResult result) 
{ 
    foreach (string error in result.Errors) 
    { 
        ModelState.AddModelError("", error); 
    } 
} 
添加自定义密码验证策略

有时候,我们需要实现密码策略,如同AD中控制那样,密码复杂度越高,那么它被破译的概率就越低。

ASP.NET Identity 提供了PasswordValidator类,提供了如下属性来配置密码策略:

RequiredLength

指定有效的密码最小长度

RequireNonLetterOrDigit

当为True时,有效的密码必须包含一个字符,它既不是数字也不是字母

RequireDigit

当为True时,有效密码必须包含数字

RequireLowercase

当为True时,有效密码必须包含一个小写字符

RequireUppercase

当为True时,有效密码必须包含一个大写字符

 如果这些预定义属性无法满足我们的需求时,我们可以添加自定义的密码验证策略,只要继承PasswordValidator 并且Override ValidateAsync方法即可,如下代码所示:

public class CustomPasswordValidator : PasswordValidator 
   { 
       public override async Task<IdentityResult> ValidateAsync(string password) 
       { 
           IdentityResult result = await base.ValidateAsync(password); 
           if (password.Contains("12345")) 
           { 
               List<string> errors = result.Errors.ToList(); 
               errors.Add("密码不能包含连续数字"); 
               result = new IdentityResult(errors); 
           } 
           return result; 
       } 
   }

上述代码中,值得注意的是,IdentityResult 对象的 Errors是只读的,所以无法直接赋值,只能通过实例化IdentityResult 类并通过构造函数传入Errors。

自定义的密码策略创建完毕过后,接着就将它附加到UserManager对象的PasswordValidator 属性上,如下代码所示:

//自定义的Password Validator 
manager.PasswordValidator = new CustomPasswordValidator 
{ 
    RequiredLength = 6, 
    RequireNonLetterOrDigit = false, 
    RequireDigit = false, 
    RequireLowercase = true, 
    RequireUppercase = true 
};
更多用户验证策略

UserManager 除了PasswordValidator之外,还提供了一个更加通用的属性:UserValidator ,它包含如下两个策略属性:

AllowOnlyAlphanumericUserNames

当为True时,UserName只能包含字母数字

RequireUniqueEmail

当为True时,Email地址必须唯一

 当然这两种策略如果不满足我们的需求的话,我们也可以像Password那样去定制化,只要 继承UserValidator<T> 然后 Override ValidateAsync 方法,如下所示:

public class CustomUserValidator : UserValidator<AppUser> 
{ 
    public CustomUserValidator(AppUserManager mgr) 
        : base(mgr) 
    { 
    } 
 
    public override async Task<IdentityResult> ValidateAsync(AppUser user) 
    { 
        IdentityResult result = await base.ValidateAsync(user); 
 
        if (!user.Email.ToLower().EndsWith("@jkxy.com")) 
        { 
            List<string> errors = result.Errors.ToList(); 
            errors.Add("Email 地址只支持jkxy域名"); 
            result = new IdentityResult(errors); 
        } 
        return result; 
    } 
} 

上述代码增强了对Email的验证,必须为@jkxy域名,然后将自定义的UserValidator 附加到User Manger 对象上:

//自定义的User Validator 
manager.UserValidator = new CustomUserValidator(manager) { 
    AllowOnlyAlphanumericUserNames = true, 
    RequireUniqueEmail = true 
}; 

 


三、ASP.NET Identity 其他API介绍

在上一小节中,介绍了CreateAsync 的使用,接下来一鼓作气,继续ASP.NET Identity之旅。

实现Delete 用户功能

按照我们的经验,若要删除一个用户,首先需要Find 它。通过UserManager 对象的 FindByIdAsync来找到要被删除的对象,如果该对象不为null,那么再调用UserManager对象的DeleteAsync来删除它,如下所示:

[HttpPost] 
public async Task<ActionResult> Delete(string id) 
{ 
    AppUser user = await UserManager.FindByIdAsync(id); 
    if (user != null) 
    { 
        IdentityResult result = await UserManager.DeleteAsync(user); 
        if (result.Succeeded) 
        { 
            return RedirectToAction("Index"); 
        } 
        return View("Error", result.Errors); 
    } 
    return View("Error", new[] {"User Not Found"}); 
} 
实现编辑用户操作

因为编辑操作UpdateAsync 只接受一个参数,而不像CreateAsync那样可以传入Password,所以我们需要手动的去校验并给PasswordHash属性赋值,当密码策略验证通过时再去验证Email策略,这样确保没有脏数据,如下所示:

[HttpPost] 
public async Task<ActionResult> Edit(string id, string email, string password) 
{ 
    //根据Id找到AppUser对象 
    AppUser user = await UserManager.FindByIdAsync(id); 
 
    if (user != null) 
    { 
        IdentityResult validPass = null; 
        if (password != string.Empty) 
        { 
           //验证密码是否满足要求 
            validPass = await UserManager.PasswordValidator.ValidateAsync(password); 
            if (validPass.Succeeded) 
            { 
                user.PasswordHash = UserManager.PasswordHasher.HashPassword(password); 
            } 
            else 
            { 
                AddErrorsFromResult(validPass); 
            } 
        } 
        //验证Email是否满足要求 
        user.Email = email; 
        IdentityResult validEmail = await UserManager.UserValidator.ValidateAsync(user); 
        if (!validEmail.Succeeded) 
        { 
            AddErrorsFromResult(validEmail); 
        } 
 
        if ((validEmail.Succeeded && validPass == null) || (validEmail.Succeeded && validPass.Succeeded)) 
        { 
            IdentityResult result = await UserManager.UpdateAsync(user); 
 
            if (result.Succeeded) 
            { 
                return RedirectToAction("Index"); 
            } 
            AddErrorsFromResult(result); 
        } 
    } 
    else 
    { 
        ModelState.AddModelError("", "无法找到改用户"); 
    } 
    return View(user); 
} 

  

总结:

在这篇文章中,我为大家介绍了什么是ASP.NET Identity以及怎样配置和创建它的基础类,然后演示使用API 进行用户的管理。在下一篇文章中,继续ASP.NET Identity之旅,探索身份验证和授权的使用,谢谢 。

 

转载于:https://www.cnblogs.com/Pinapple/p/6824785.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值