ASP.NET 开发必备知识点(2):那些年追过的ASP.NET权限管理
一、前言
在前一篇文章已经为大家介绍了OWIN和Katana,有了对他们的了解之后,才能更好地去学习Asp.net Identity,因为Asp.net Identity的实现集成了Owin。其实在Asp.net 2.0的时候,微软已经对用户权限管理进行了实现,其实现为Membership。由于之前的实现有很多限制,所以微软在Asp.net 4.5推出了Asp.net Identity。接下来,本篇文章将详细介绍下Asp.net Identity的实现。
二、Asp.net中用户权限管理发展历程
在前面我们已经说过,在Asp.net 2.0的时候,Asp.net中就已经实现了用户权限管理,所以,Asp.net 用户权限管理有其发展历程。下图就是Asp.net中权限管理的发展历程:
ASP.NET Membership
Asp.net Membership是在2005年的Asp.net 2.0引入的。Membership机制引入了表单验证(Form Authentication),以及一个用于存储用户名、密码和其他用户信息的SQL Server数据库。但它同样存在一些限制:
- 数据库只能使用SQL Server,难以对SQL Server Compact、SQL Azure、NoSQL支持。并且你想为用户表添加额外字段的话,此时你只能创建一个User的附加表。对于开发者来说,不能很好地自定义用户信息。
- 由于Asp.net Membership是基于表单进行验证的,因此无法支持OWIN。
ASP.NET Simple Membership
Asp.net Simple Membership是对Asp.net Membership的一次改进,它使得你可以更容易自定义用户信息。尽管如此,由于它依然是基于Asp.net Membership之上的,所以它仍然存在以下几点限制:
- 对非关系数据库支持不好。
- 不支持OWIN
- 对于已存在的Asp.net Membership Provider支持的不是很好,不利于扩展。
ASP.NET Universal Providers
Asp.net Universal Providers解决了前两者的一些问题,例如他支持存储用户在Azure SQL和SQL Server Compact数据库中。并且它基于EF code First实现的,所以它支持EF支持的所有数据库。但由于它依然是基于Asp.net Membreship基础架构实现的,所以仍然有些问题不能很好解决。所以它只解决了前两者的部分问题,其本身还存在一些限制:
- 对非关系数据库支持不好
- 不支持OWIN
三、Asp.net Identity 详细介绍
随着互联网的快速发展,从而非关系数据库也层出不穷,但之前的三者权限管理都对非关系数据库支持的不是很好,所以微软必须要实现一种新的权限管理机制,所以在。NET Framework中推出了Asp.net Identity。该套机制解决了之前的所有问题。Asp.net 具有如下特点:
- 可用于ASP.NET所有框架上,包括Asp.net MVC、Asp.net Web Forms、Web Pages、Asp.net Web API和SignalR。
- 可用于各种应用程序,包括Web应用、移动应用,Windows Store应用和混合架构应用。
- 用户信息的自定义
- 存储易于扩展:默认使用EF Code First存储在SQL Server数据库中,但可以很好地扩展到SharePoint、Azure SQL和NoSQL 数据库中。
- 支持单元测试
- 提供了Role Provider,使创建和管理变得简单
- 支持面向Claims的身份验证(即:支持基于声明的身份验证),前面的三者都是基于表单的身份验证。
- 支持社交账号的登录,支持Facebook,Microsoft账户、Twitter,Google、QQ等社交账户。
- 支持Windows Azure Active Directory账号登录功能
- 支持OWIN。
- 通过Nuget发布,这样能让Asp.net 团队更好地修复Bug和迭代新功能,并在第一个时间进行发布。将其与System.Web.dll程序集解耦。
四、Asp.net Identity内部实现机制
从上面对Asp.net Identity的介绍可以发现,它确实解决了之前的所有问题,那它是如何做到的呢?要知道其实现机制并不难,因为Asp.net Identity已经开源,我们可以到其站点下载其源码研究即可,其开源地址为:http://aspnetidentity.codeplex.com/。这里简单的分析它注册和登录的功能的内部实现。
首先使用VS2013创建一个Asp.net MVC站点,此时网站的用户授权和认证模块的代码的实现VS已经帮我们添加好了,我们只需要找到对应的注册和登录功能对其进行分析,从而明白Asp.net Identity是如何帮完成这两个功能的。
首先,我们找到Accout控制器中注册功能的实现代码:
public async Task<ActionResult> Register(RegisterViewModel model)
{
if (ModelState.IsValid)
{
var user = new ApplicationUser { UserName = model.Email, Email = model.Email };
var result = await UserManager.CreateAsync(user, model.Password);
if (result.Succeeded)
{
await SignInManager.SignInAsync(user, isPersistent:false, rememberBrowser:false); // 有关如何启用帐户确认和密码重置的详细信息,请访问 http://go.microsoft.com/fwlink/?LinkID=320771
// 发送包含此链接的电子邮件
// string code = await UserManager.GenerateEmailConfirmationTokenAsync(user.Id);
// var callbackUrl = Url.Action("ConfirmEmail", "Account", new { userId = user.Id, code = code }, protocol: Request.Url.Scheme);
// await UserManager.SendEmailAsync(user.Id, "确认你的帐户", "请通过单击 <a href=\"" + callbackUrl + "\">這裏</a>来确认你的帐户"); return RedirectToAction("Index", "Home");
}
AddErrors(result);
} // 如果我们进行到这一步时某个地方出错,则重新显示表单
return View(model);
}
从上面代码的方法名可以看出,完成用户注册的主要实现在于UserManager.CreateAsync方法上,这个方法实现真是在Asp.net Identity帮我们实现,接下来到我们下载的源码来查看该方法的实现。具体的源码实现如下所示:
public virtual async Task<IdentityResult> CreateAsync(TUser user, string password)
{
ThrowIfDisposed();
var passwordStore = GetPasswordStore();
if (user == null)
{
throw new ArgumentNullException("user");
}
if (password == null)
{
throw new ArgumentNullException("password");
}
// UpdatePassword对密码进行Hash加密
var result = await UpdatePassword(passwordStore, user, password).WithCurrentCulture();
if (!result.Succeeded)
{
return result;
}
// 注册功能的实现
return await CreateAsync(user).WithCurrentCulture();
} public virtual async Task<IdentityResult> CreateAsync(TUser user)
{
ThrowIfDisposed();
await UpdateSecurityStampInternal(user).WithCurrentCulture();
var result = await UserValidator.ValidateAsync(user).WithCurrentCulture();
if (!result.Succeeded)
{
return result;
}
if (UserLockoutEnabledByDefault && SupportsUserLockout)
{
await GetUserLockoutStore().SetLockoutEnabledAsync(user, true).WithCurrentCulture();
} // 调用IUserStore的CreateAsync完成用户注册
await Store.CreateAsync(user).WithCurrentCulture();
return IdentityResult.Success;
} // UserStore中CreateAsync的实现
public virtual async Task CreateAsync(TUser user)
{
ThrowIfDisposed();
if (user == null)
{
throw new ArgumentNullException("user");
} // 将实体添加进DbSet<User>集合中
_userStore.Create(user);
// 调用SaveChanges将用户保存到数据库中
await SaveChanges().WithCurrentCulture();
}
看到这里是不是豁然开朗了很多的,其实Asp.net Identity内部注册功能的实现,我们完全可以自己来实现。其实现简单的说就是:
1. 对用户提交的数据进行验证
2. 对密码进行加密保存
3. 调用Microsoft.AspNet.Identity.EntityFramework命名空间下的UserStore类的CreateAsync方法将用户进行持久化。
4. UserStore类中的CreateAsync方法的实现也就是DbSet<User>.Add(entity)和SaveChanges()方法将对象持久化。
到这里还有一个问题,其实上面代码调用的是IUserStore接口中的CreateAsync方法,但具体的IUserStore对象是怎么注入进去的呢?
你带着这个疑惑去Asp.net MVC站点中去寻找其注入代码。此时,你可以发现在Startup.Auth.cs和Start.cs文件中有如下实现:
// Startup.cs 文件
public partial class Startup
{
public void Configuration(IAppBuilder app)
{
ConfigureAuth(app);
}
} // Start.Auth.cs文件
public void ConfigureAuth(IAppBuilder app)
{
// 配置数据库上下文、用户管理器和登录管理器,以便为每个请求使用单个实例
// .........
app.CreatePerOwinContext<ApplicationUserManager>(ApplicationUserManager.Create);
// ........
} // IdentityConfig.cs文件
public static ApplicationUserManager Create(IdentityFactoryOptions<ApplicationUserManager> options, IOwinContext context)
{
var manager = new ApplicationUserManager(new UserStore<ApplicationUser>(context.Get<ApplicationDbContext>()));
// ........
}
看到上面标注红色的代码了吗,这里就是将UserStore注入的地方。看到这里,你是不是没有任何疑惑了。对于登录功能的实现大家同样可以按照这样的方法去探索。本来想一起分析下的,后面想想,还是留给大家去探索吧。
五、从Asp.net Identity内部实现学会项目分层架构
其实,在我们平时工作,只要学会如何使用Asp.net Identity机制来完成对应功能。那我们为什么还要研究其源码实现呢?我觉得有两点:
- 研究源码实现,可以让你对其实现原理有一个深刻的理解,对于分析出现的问题有极大的好处。因为只有你了解其实现原理,写功能模块才能更加自信,处理出现的问题才会比别人快。
- 除了第一点之外,研究源码还有一个重要的作用就是学习源码作者的项目分层和代码分离。在现实生活中,有很多朋友抱怨出现瓶颈了,无法提高,因为平常工作中一般都是去写堆功能的代码,觉得对能力没什么提高。此时你完全可以去研究微软开源的代码,通过研究源码来学习大牛们是如何将项目做到低耦合高内聚的,学习大牛们是如何做到代码分离的。然后再讲学习到的内容应用于工作,相信这样的一个过程下来,你不想提高都不行了。渐渐地你会觉得自己也可以完成一个开源框架。
上面介绍了研究源码的两大作用,那我们从Asp.net Identity内部实现中又学到了什么呢?
通过第四部分的代码分析,Asp.net Identity中注册功能的实现主要分为的4点中,我们可以学到如下几点:
- 关注点的分离。Asp.net Identity注册功能中,将用户输入以及密码加密等代码实现都分离到具体的类中进行实现,而不是将其放在UserManager这个类中。这充分体现关注点分离原则
- 针对接口编程原则。Asp.net Identity内部实现中,都是针对于接口编程,每个类中依赖都是接口,并没有依赖与具体类。从而降低代码之间的耦合。
- 实现了依赖注入。Asp.net Identity具体实现是通过在调用端通过依赖注入的方式进行注入。
- 项目分层架构。Asp.net Identity注册功能。AccountController首先调用UserManager的CreateAsync,而UserManager的CreateAsync又调用了IUserStore中的CreateAsync方法来通过调用EF的DbContext来完成数据的持久化。从这个调用过程和类之间的关系可以看出,这真是领域驱动设计的分层体现。领域驱动设计中设计4层,分别是UI层、应用层、领域层和基础设施层。其中UI层对应的就是AccountController类,应用层对应的就是UserManager类、领域层就是具体的User实体、基础设施层对应的就是IUserStore(准确地说,基础设施层中的仓储对应着IUserStore)。上面对应的项目分层,其实每个层中代码的实现都可以按照这个模式去实现。这点在ABP Web框架中得到了很好的实现:https://github.com/aspnetboilerplate/aspnetboilerplate。
所以,如果你觉得你现在的工作得到提高的话,完全不需要去什么群里咨询其他的推荐什么书籍什么,从现在开始就开始研究源码吧。如果不知道研究什么源码的话,完全可以从微软的一些开源代码开始,例如就从Asp.net Identity源码开始,不要担心研究完之后,还是怕不能提高,只要你理解和领悟了,你不想提高都难。另外再推荐大家研究下ABP的实现,我最近就在研究它,希望理解透彻之后,再写一个小的Web框架来巩固自己的研究。到时候也会把自己的一些研究心得分享到这里。
六、总结
到这里,本篇文章的介绍就结束了,希望这篇文章可以帮助朋友对微软的用户权限管理框架有进一步的了解,以及希望哪些想提高的朋友,从现在开始就来和我一起来研究微软的开源框架和ABP框架吧。记得研究之后,分享到这里与大家共享哦。
ASP.NET 开发必备知识点(2):那些年追过的ASP.NET权限管理的更多相关文章
- ASP.NET 开发必备知识点(1):如何让Asp.net网站运行在自定义的Web服务器上
一.前言 大家都知道,在之前,我们Asp.net 的网站都只能部署在IIS上,并且IIS也只存在于Windows上,这样Asp.net开发的网站就难以做到跨平台.由于微软的各项技术的开源,所以微软自然 ...
- ASP.NET MVC开发:Web项目开发必备知识点
最近加班加点完成一个Web项目,使用Asp.net MVC开发.很久以前接触的Asp.net开发还是Aspx形式,什么Razor引擎,什么MVC还是这次开发才明白,可以算是新手. 对新手而言,那进行A ...
- 基于asp.net(C#)MVC+前端bootstrap+ztree+lodash+jquery技术-Angel工作室通用权限管理
一.Angel工作室简单通用权限系统简介 AngelRM(Asp.net MVC Web api)是基于asp.net(C#)MVC+前端bootstrap+ztree+lodash+jquery技术 ...
- ASP.NET MVC 必备知识点杂谈
一 工程结构4个程序集 Microsoft.Web.Mvc --一些可以使用的,不确定的程序包System.Web.Mvc --主程序库下面两个列入3.5的Net框架了System.Web.Abs ...
- Asp.net开发必备51种代码
1.//弹出对话框.点击转向指定页面 Response.Write("<script>window.alert('该会员没有提交申请,请重新提交!')</script> ...
- 开发必备知识点--django项目启动时,url加载之前,执行某个.py文件
django项目启动时,自定义执行某个py文件 在任意的app下的apps.py中的Config类下自定义ready()方法,并且调用autodiscover_modules. app01/apps. ...
- UWP开发必备:常用数据列表控件汇总比较
今天是想通过实例将UWP开发常用的数据列表做汇总比较,作为以后项目开发参考.UWP开发必备知识点总结请参照[UWP开发必备以及常用知识点总结]. 本次主要讨论以下控件: GridView:用于显示数据 ...
- ASP.NET MVC C#知识点提要
ASP.NET MVC C#知识点提要 本篇博文主要对asp.net mvc开发需要撑握的C#语言知识点进行简单回顾,尤其是C# 3.0才有的一些C#语言特性.对于正在学asp.net mvc的童鞋, ...
- Hybrid App 应用开发中 9 个必备知识点复习(WebView / 调试 等)
前言 我们大前端团队内部 ?每周一练 的知识复习计划继续加油,本篇文章是 <Hybrid APP 混合应用专题> 主题的第二期和第三期的合集. 这一期共整理了 10 个问题,和相应的参考答 ...
随机推荐
- Redis与Memcached的区别
传统MySQL+ Memcached架构遇到的问题 实际MySQL是适合进行海量数据存储的,通过Memcached将热点数据加载到cache,加速访问,很多公司都曾经使用过这样的架构,但随着业务数据量 ...
- 随机生成数字(ashx文件,调用上篇所写发送邮件代码)
public void ProcessRequest(HttpContext context) { //邮件标题 string Email_Title = Dsis.Core.SysCore.PubF ...
- M2事后分析汇报总结
学霸网站项目Postmortem结果 M2之于M1的改进 文档和问答的整合 完成webservice 完成数据库触发器设计与完整性约束依赖(大规模) 优化学霸UI 资源的搜索 外部问题的搜索 文档的上 ...
- JDK、JRE、JVM三者间的关系
JDK(Java Development Kit)是针对Java开发员的产品,是整个Java的核心,包括了Java运行环境JRE.Java工具和Java基础类库.Java Runtime Enviro ...
- RFC2119:表示要求的动词(转)
RFC(Request For Comments)指的关于互联网标准的正式文件,它们的内容必须写得非常清楚. 表达的时候,必须严格区分哪些是"建议"(suggestion),哪些是 ...
- java 中与 或 非 异或 和位移运算
与(&) 或(|) 异或(^) 和位移(>>,<<) 通常和符号位无关 .. 但是非比较特殊,与符号位有关,所以计算的时候要考虑符号位 先扩展为32字符,前16位为符号 ...
- 第四章 使用Docker镜像和仓库(二)
第四章 使用Docker镜像和仓库(二) 回顾: 开始学习之前,我先pull下来ubuntu和fedora镜像 [#9#cloudsoar@cloudsoar-virtual-machine ~]$s ...
- DDD领域驱动设计基本理论知识总结
领域驱动设计之领域模型 加一个导航,关于如何设计聚合的详细思考,见这篇文章. 2004年Eric Evans 发表Domain-Driven Design –Tackling Complexity i ...
- [PL/SQL] 如何规避异常ORA-01403
如果mytable表中不存在 ID = 123 的数据,那么 SELECT Flag INTO flag FROM mytable WHERE ID = 123 将抛出异常ORA-01403 SELE ...
- ActionScript 3.0 自写类整理笔记(十三)——Random类
一个简单的随机函数工具类,总共提供了9种静态方法来获取不同的随机值随便写的,如果你还有什么更好的建议,请提出来,谢谢~ index.Random类:代码:public final class Rand ...