connectionString="Data Source=(localdb)\v11.0;Initial Catalog=IdentityDb;Integrated Security=True;Connect Timeout=15;Encrypt=False;TrustServerCertificate=False; MultipleActiveResultSets=True" />
创建Entity Framework 类
如果大家使用过ASP.NET Membership,对比过后你会发现在ASP.NET Identity扩展User信息是多么的简单和方便。
1.创建 User 类
第一个要被创建的类它代表用户,我将它命名为AppUser,继承自Microsoft.AspNet.Identity.EntityFramework 名称空间下IdentityUser,IdentityUser 提供了基本的用户信息,如Email、PasswordHash、UserName、PhoneNumber、Roles等,当然我们也可以在其派生类中添加额外的信息,代码如下:
utside;max-height:400px;color:rgb(17,17,17);font-size:13px;font-family:'Courier New', courier, monospace;">
using Microsoft.AspNet.Identity.EntityFramework;
namespace UsersManagement.Models
{
public class AppUser:IdentityUser
{
}
}
2.创建 Database Context 类
接下来的步骤就是创建EF Database Context 来操作AppUser。ASP.NET Identity将使用Code First 来创建和管理数据库架构。值得注意的是,Database Context必须继承自IdentityDbContext,而且T为User类(在此示例即AppUser),代码如下所示:
utside;max-height:400px;color:rgb(17,17,17);font-size:13px;font-family:'Courier New', courier, monospace;">
public class AppIdentityDbContext : IdentityDbContext
{
public AppIdentityDbContext() : base("IdentityDb")
{
}
static AppIdentityDbContext()
{
Database.SetInitializer(new IdentityDbInit());
}
public static AppIdentityDbContext Create()
{
return new AppIdentityDbContext();
}
}
public class IdentityDbInit : DropCreateDatabaseIfModelChanges
{
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就为AppUser。UserManager提供了创建和操作用户的一些基本方法并且全面支持C# 异步编程,所以你可以使用CreateAsync(Create),FindAsync(Find)、DeleteAsync(Delete)、UpdateAsync(Update)来进行用户管理,值得注意的是,它并不通过Entity Framework 来直接操作用户,而是间接调用UserStore来实现。UserStore是Entity Framework 类并实现了IUserStore接口,并且实现了定义在UserManger中操作用户的方法。代码如下所示:
utside;max-height:400px;color:rgb(17,17,17);font-size:13px;font-family:'Courier New', courier, monospace;">
///
/// 用户管理
///
public class AppUserManager : UserManager {
public AppUserManager(IUserStore store)
: base(store) {
}
public static AppUserManager Create(
IdentityFactoryOptions options,
IOwinContext context) {
AppIdentityDbContext db = context.Get();
//UserStore 是 包含在 Microsoft.AspNet.Identity.EntityFramework 中,它实现了 UserManger 类中与用户操作相关的方法。
//也就是说UserStore类中的方法(诸如:FindById、FindByNameAsync...)通过EntityFramework检索和持久化UserInfo到数据库中
AppUserManager manager = new AppUserManager(new UserStore(db));
return manager;
}
}
上述代码中,静态的Create方法将返回AppUserManger实例,它用来操作和管理用户,值得注意的是,它需要传入OwinContext对象,通过该上下文对象,获取到存储在Owin环境字典中的Database Context实例。
4.创建OWIN Startup 类
最后,通过Katana(OWIN的实现)提供的API,将Middleware 中间件注册到Middleware中,如下所示:
utside;max-height:400px;color:rgb(17,17,17);font-size:13px;font-family:'Courier New', courier, monospace;">
public class IdentityConfig
{
public void Configuration(IAppBuilder app)
{
//1.使用app.Use方法将IdentityFactoryMiddleware和参数callback回掉函数注册到Owin Pipeline中
//app.Use(typeof(IdentityFactoryMiddleware>), args);
//2.当IdentityFactoryMiddleware中间件被Invoke执行时,执行callback回掉函数,返回具体实例Instance
//TResult instance = ((IdentityFactoryMiddleware) this).Options.Provider.Create(((IdentityFactoryMiddleware) this).Options, context);
//3.将返回的实例存储在Owin Context中
//context.Set(instance);
app.CreatePerOwinContext(AppIdentityDbContext.Create);
app.CreatePerOwinContext(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获取对象:
utside;max-height:400px;color:rgb(17,17,17);font-size:13px;font-family:'Courier New', courier, monospace;">
private AppUserManager UserManager
{
get { return HttpContext.GetOwinContext().GetUserManager(); }
}
在上述代码中,通过Microsoft.Owin.Host.SystemWeb 程序集,为HttpContext增加了扩展方法GetOwinContext,返回的 OwinContext对象是对Http请求的封装,所以GetOwinContext方法可以获取到每一次Http请求的内容。接着通过IOwinContext的扩展方法GetUserManager获取到存储在OwinContext中的UserManager实例。
然后,通过UserManager的Users属性,可以获取到所有的User集合,如下所示:
utside;max-height:400px;color:rgb(17,17,17);font-size:13px;font-family:'Courier New', courier, monospace;">
public ActionResult Index()
{
return View(UserManager.Users);
}
创建User对象
通过UserManager的CreateAsync方法,可以快速的创建User对象,如下代码创建了User ViewModel:
utside;max-height:400px;color:rgb(17,17,17);font-size:13px;font-family:'Courier New', courier, monospace;">
public class UserViewModel
{
[Required]
public string Name { get; set; }
[Required]
public string Email { get; set; }
[Required]
public string Password { get; set; }
}
使用UserManager对象的CreateAsync方法将AppUser对象将它持久化到数据库:
utside;max-height:400px;color:rgb(17,17,17);font-size:13px;font-family:'Courier New', courier, monospace;">
[HttpPost]
public async Task 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 处,如下所示:
utside;max-height:400px;color:rgb(17,17,17);font-size:13px;font-family:'Courier New', courier, monospace;">
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 ValidateAsync(string password)
-
{
-
IdentityResult result = await base.ValidateAsync(password);
-
if (password.Contains("12345"))
-
{
-
List 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 然后 Override ValidateAsync 方法,如下所示:
-
public class CustomUserValidator : UserValidator
-
{
-
public CustomUserValidator(AppUserManager mgr)
-
: base(mgr)
-
{
-
}
-
-
public override async Task ValidateAsync(AppUser user)
-
{
-
IdentityResult result = await base.ValidateAsync(user);
-
-
if (!user.Email.ToLower().EndsWith("@jkxy.com"))
-
{
-
List 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 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 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);
-
}
-
}
-
来源:https://blog.csdn.net/jiangfei009003/article/details/79504068
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!