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 创建表示用户、用户管理器、数据库上下文的类,以及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. |
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
{
}
}
Name 名称 | Description 描述 |
Claims |
Returns the collection of claims for the user. 返回用户的声明集合 |
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 返回变更用户标识时被修改的值,例如被口令修改的值 |
UserName | Returns 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实例。
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(""))
{
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 = ,
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之旅,探索身份验证和授权的使用,谢谢 。
最新文章
- python列表模拟堆栈和队列
- springMVC基础controller类
- 简易的轮廓边生成(N和V dot点乘方式)(surface方式和vs ps 方式的分别实现)
- Redis学习笔记四:独立功能之发布与订阅
- 快销品 车销批发管理手持终端PDA系统 打印开单 入库 库存 盘点多功能一体
- Rhel6-piranha配置文档
- js对select动态添加和删除OPTION
- Sqoop2常用命令介绍
- OGG问题 ORA-01403的处理办法
- 安装ImageMagick扩展出现configure: error: not found. Please provide a path to MagickWand-config or Wand- config program
- Invitation Cards(邻接表+逆向建图+SPFA)
- InstallShield: cannot extract icon with index 0错误解决方案
- ssh端口映射,本地转发
- poj1064 二分,注意精度!
- USB数据流模型
- 私有云Mariadb集群搭建
- 几个APP开发的创意
- Gradle 1.12用户指南翻译——第四十八章. Wrapper 插件
- java中求质数(素数)的问题
- swoole 版本查看
热门文章
- TurboLinux系统管理习题一
- Android Studio 点击两次返回键,退出APP
- The ADB instructions
- 使用sklearn机器学习库实现线性回归
- datagridview 行高列宽的自动设置
- [20171211][转载]如何实现dbms_output输出没有打开serveroutput on.txt
- 转:IIS 应用程序池 内存 自动回收
- Windows 10忘记登录密码不用怕,系统U盘/光盘轻松重置
- 如何开启win10的上帝模式
- cobaltstrike3.8服务器搭建及使用