前面的文章中为My Blog加入了文章的管理功能(ASP.NET没有魔法——ASP.NET MVC使用Area开发一个管理模块),但是管理功能应该只能由“作者”来访问,那么要如何控制用户的访问权限?也就是当用户访问管理功能时需要对用户进行身份验证,对于用户来说身份验证也就是登录,即提供一个登录界面,通过账号密码的形式登录后就可以访问受限制的内容。
本文将从以下几个方面介绍ASP.NET MVC是如何实现用户身份验证的:
● Web中的身份验证
● ASP.NET的Identity组件介绍
● ASP.NET MVC中使用Identity——组件安装
● ASP.NET MVC中使用Identity——EntityFramework
● ASP.NET MVC中使用Identity——注册功能的实现
● ASP.NET MVC中使用Identity——登录功能的实现
● ASP.NET MVC中使用Identity——身份验证功能的实现
注:本文的目的是介绍在已存在的项目中如何使用Identity组件来添加用户注册、登录和验证的功能,所以内容会比较细琐,后续会对Identity中比较关键的点进行介绍,如用户密码的加解密、Cookie的生成与验证,Identity与Owin等等方面进行深入分析介绍。
Web中的身份验证
Web应用作为一种特殊的应用软件系统,它是基于HTTP协议的,由于HTTP的独特性(无状态)所以每一次访问都是独立的、不携带上一次请求信息的,所以现在常用的身份验证方式都是通过Cookie或者url查询字符串的形式来保存“状态”信息,达到每次访问服务器,服务器都能“知道”用户身份的目的。
ASP.NET作为一个Web程序的开发框架,提供了一些身份验证的方式如From验证,它通过用户提到的服务器的用户特征(如用户名、密码等)生成一个加密的Cookie信息,后续请求中将带着该Cookie信息证明用户身份。下图是博客园中的Cookie信息:
随着软件系统的发展,普通的身份验证已经不能满足系统的需求,如登录时候的二次验证、第三方账号登录、用户授权等。所以ASP.NET又针对这些需求开发了Identity组件(Identity的前身为MemberShip)。
ASP.NET的Identity组件介绍
Identity用来快速为ASP.NET应用程序搭建一个完善的身份验证系统。它可以支持ASP.NET框架下的所有程序身份验证,并通过EF Code First来支持用户数据的持久化,并集成OWIN来解耦不再依赖System.Web。另外它还支持第三方账号登录、短信/邮件二次验证等高级功能。
Identity主要的组件如下:
● Microsoft.AspNet.Identity.Core:Identity的核心类库,实现了身份验证的核心功能,并提供了拓展接口。
● Microsoft.AspNet.Identity.EntityFramework:Identity数据持久化的EF实现。
● Microsoft.AspNet.Identity.OWIN:基于Identity的OWIN身份验证插件,它代替了原有的Form验证。
● Microsoft.Owin.Host.SystemWeb:Owin的IIS宿主,将IIS的接收到的请求转入Owin处理。
ASP.NET MVC中使用Identity——组件安装
1. 通过Nuget来安装Microsoft.AspNet.Identity.EntityFramework(已包含Microsoft.AspNet.Identity.Core):
2. 安装Microsoft.AspNet.Identity.OWIN:
3. 安装Microsoft.Owin.Host.SystemWeb:
ASP.NET MVC中使用Identity——EntityFramework
上面介绍过Identity支持EF的code first,那么自然就会想到实体与DBContext,那么在Identity中它们是怎么实现的呢?
1. Identity中的实体:
以User信息为例,Microsoft.AspNet.Identity.Core类库中提供了User的核心接口:
它的具体实现则位于Microsoft.AspNet.Identity.EntityFramework中:
除了User外,Identity还定义了Role、UserClaim、UserLogin以及UserRole这些实体,如下图:
2. Identity中的DBContext:
在Microsoft.AspNet.Identity.EntityFramework中提供了一个IdentityDbContext类型(注:其它IdentityDbContext的泛型实现是用于对实体进行拓展的,如果没有拓展需求,那么使用非泛型类型即可)。
3. 在ASP.NET MVC项目中使用Identity提供的DbContext(注:本例中的代码大部分参考ASP.NET MVC默认模板代码):
1). 继承IdentityDbContext<TUser>类型,实现自己的DbContext(注:通过继承来使用Identity的DbContext可以灵活的根据需求来改变DbContext及其实体的配置)。
2). 使用enable-migrations命令启用自动迁移,并在BlogIdentityDbContext中设置自动将数据库更新至模型最新版本:
自动迁移(即无需使用add-migration命令来添加数据库结构变更):
自动将数据库更新到模型最新版本:
注:本例基于My Blog的MySQL数据库实现,在更新数据库时为避免一下错误,所以在OnModelCreating中加入了两个对象的主键。
3). 在web.config中加入MySQL的EF配置以及一个名称为"DefaultConnection"的连接字符串(因为上面的DbContext构造方法中指定了参数DefaultConnection):
连接字符串:与BlogContext共用同一个数据库:
注:此处要说明两点,第一是使用配置文件的形式来配置EF的MySQL配置原因是,MyBlog中没有引用EF MySQL的组件,无法使用代码,只有等编译完成后把所有依赖的程序集复制到bin目录下,启动程序时通过配置文件解析。第二点是现在在整个解决方案中引入了两个DBContext,多个DBContext是可以共存的,只要对其进行正确的配置并提供正确的连接字符串。如果一个项目中有多个DBContext时,对其进行迁移操作就需要通过参数指定被操作的DbContext,可以参考这篇文章:http://www.cnblogs.com/Jack-Blog/p/4699596.html
4). 可以执行update-database命令将DbContext同步到数据库中(因为设置了数据库自动同步,所以也可以等待后面运行程序时自动同步):
ASP.NET MVC中使用Identity——注册功能的实现
在ASP.NET MVC中实现注册功能之前,先要了解一下Identity组件提供的业务逻辑“层”(注:这里说“层”仅仅是为了对应现有的项目结构,有数据层和逻辑层,其实在Identity中也是这样划分的,虽然它们都在同一个程序集中)。
Identity中提供了RoleManager、UserManager等业务逻辑的实现类型,下图是UserManager的定义:
从图中可以看出,它已经具有创建用户、添加角色等逻辑的实现,所以对于注册功能来说仅需要调用UserManager的对应方法即可。下面就介绍如何添加注册功能:
1. 添加注册使用的ViewModel:
2. 创建AccountController以及Register Action方法:
注:UserManager依赖UserStore,UserStore又依赖于DbContext,也就是说业务逻辑依赖仓储,仓储又依赖数据库操作的实现。
3. 创建View:
4. 在布局页面中加入注册链接:
5. 运行:
数据库结果:
ASP.NET MVC中使用Identity——登录功能的实现
登录功能的目的是对用户提交到服务器的用户名和密码进行验证,验证成功后生成一个包含用户信息的加密的字符串并以Cookie的形式返回到客户端。
登录功能的实现方法与注册差不多就是添加视图模型、Action和View,然后在Action中调用Identity的用户验证方法即可:
1. 创建ViewModel:
2. 添加登录Action(注:sigInManager封装了登录的业务逻辑包括写Cookie):
3. 添加View并在布局页面加入登录链接:
4. 运行效果:
注:现在还未添加访问的限制,所以登录与不登录其实上是一样的。
ASP.NET MVC中使用Identity——身份验证功能的实现
用户完成登录操作后仅仅是在Cookie中多了一个用户信息,如果不对该信息进行验证那么这个信息是没有作用的,ASP.NET中没有魔法,它任何的操作都是有代码在后面支撑,那用于支撑Identity的身份验证的代码是什么呢?之前在介绍Identity时提到过它是通过Owin来与Web服务器解耦的,Owin它是Web服务器处理HTTP请求的一个规范,而它在IIS中是一个httpModule扩展(关于Owin后续会进行详细介绍)。总的来说在IIS中Owin以HttpModule的拓展方式,为HTTP的请求处理又添加了一个处理管道。
那么Identity与Owin的集成实际上是在Owin的处理管道中,来读取请求数据中的登录后生成的Cookie并验证,实现的具体方式如下:
1. 创建一个Owin Startup类文件:
2. 在Configuration方法中添加Cookie验证的中间件,当未登录访问受限内容时自动跳转登录页面:
3. 为需要限制访问的Controller添加Authorize特性:
4. 在布局文件中添加逻辑判断,当登录成功后显示用户名,未登录时显示登录链接:
5. 运行:
访问受限页面admin/home/index(未登录将跳转):
登录后能够访问被限制的内容:
登录后的首页(由于样式问题”欢迎 admin“字符串与背景同色( ╯□╰ )):
小结
本章主要内容是对ASP.NET 身份验证以及Identity进行了简要的介绍,然后解释了在ASP.NET MVC中是如何通过Identity实现用户的注册、登录和身份验证的。本例的代码主要参考并简化了默认的ASP.NET MVC带有独立身份验证的模板代码,所以如有需要可对照模板代码进行对比。
另外要注意的是通过模板建立的注册、登录都是带有模型数据验证的,但本例中没有加入,关于模型的验证会在后续介绍。
参考:
http://johnatten.com/2014/04/20/asp-net-mvc-and-identity-2-0-understanding-the-basics/
https://docs.microsoft.com/en-us/aspnet/identity/overview/getting-started/adding-aspnet-identity-to-an-empty-or-existing-web-forms-project
https://msdn.microsoft.com/zh-cn/library/azure/ms789031(v=vs.90).aspx
http://www.cnblogs.com/dinglang/archive/2012/06/03/2532664.html
http://www.cnblogs.com/xzwblog/archive/2017/05/10/6834663.html