ASP.NET Identity是在.NET Framework4.5中引入的,从Membership发布以来,我想微软已经从开发者以及企业客户那里面得到了足够的反馈信息来帮助他们打造这样一套新的框架。他所拥有的特点大多也是前面所不能满足的,至少我们看到的是进步,不是么?
瞟一眼好处还真不少,但是至少对于开发者来说,好用,能满足需求,灵活才是王道,那我们下面就来看看如何使用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吧。
我们可以在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上下载就可以了。
主要包括ASP.NET Identity 的EF 部分的实现,有了EF的帮助我们就可以完全自定义数据结构,当然我们也只需要定义一个实体类就可以了。
名字就已经告诉大家了,这是ASP.NET Identity的核心了,所以主要的功能在这里面。上面那个包是ASP.NET Identity EF的实现,那么我们可以在这个核心包的基础上扩展出基于No SQL, Azure Storage 的 ASP.NET Identity实现。
ASP.NET Identity对OWIN 认证的支持。
最上面两个就是我们自己创建的代码,分别继承自己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操作就可以了。
有没有发现这张图和我们第二篇中讲的Provider模式有那么点点的神似? 在Membership中,我们所有的操作通过调用Membership来过多成,但是Membership本身只是一个包装类,内部的操作实际上是通过Provider的实际类来完成的,这就是策略模式的典型案例。只不过Membership的Provider通过web.config配置完成,而UserManager通过构造函数注入完成。
为了熟悉AspNet.Identity的结构,我们来扩展实现一个将用户信息写入文件的组件,然后实现登录注册功能,我们就给它命名AspNet.Identity.File吧。
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