ASP.NET没有魔法——ASP.NET Identity与授权
一个完整的ASP.NET的请求中会存在身份验证(Authentication)阶段以及授权(Authorization)阶段,英文单词Authentication和Authorization非常相似,所以很多时候会混淆这两个概念。身份验证(Authentication)的目的是知道“你”是谁,而授权(Authorization)则是当“你”访问一个资源时是否符合访问条件,符合就将访问权限授权给你进行访问,否则拒绝访问。
本文将从以下几点介绍ASP.NET MVC如何使用Identity完成资源访问的限制:
● 资源访问的限制方式
● ASP.NET中的访问限制
● ASP.NET MVC中基的访问限制
● ASP.NET MVC中的用户信息
● ASP.NET Identity用户身份信息填充
● ASP.NET MVC访问限制的实现
● ASP.NET MVC基于用户声明的访问限制及自定义限制
资源访问的限制方式
什么是资源?在Web中通过URI(Uniform Resource Identifier,统一资源标识符)来对HTML文档、图片、图像等内容定位,反过来说在Web中HTML文档、图片、图像等内容就是资源,而资源访问的限制就是对在用户通过URI访问Web资源时,判断该用户是否有权限访问该资源,如果有则继续访问,否则拒绝访问。
资源访问有以下几种限制方式:
● 匿名访问限制:所有人都可以访问资源。
● 根据用户名访问限制:指定特定的用户,让其能够或者不能访问资源。
● 根据用户角色访问限制:指定特定角色,让拥有该角色的用户能够访问资源。
● 根据用户声明(Claim)访问限制:指定特定的声明(Claim),让身份信息中含有该声明的用户能够访问资源。
● 使用其它用户信息进行访问限制:根据用户身份的其它信息来判断用户是否能够访问资源。
从上面几点可以看出资源访问的限制或者说授权,实际上就是通过用户信息来判断用户是否有访问资源的权限,而常用的信息是用户名、用户角色以及用户声明。
注:对于授权来说,它处于身份验证的后续阶段,所以可以认为在授权阶段时已经存在用户信息,所以可以直接使用用户信息来完成访问限制。
ASP.NET中的访问限制
ASP.NET中通过HTTPModule的方式实现了FileAuthorizationModule以及UrlAuthorizationModule来对用户访问文件以及其它资源进行权限控制,其中UrlAuthorizationModule可以通过在web.config中添加如下配置来通过用户名或者用户角色限制访问:

在ASP.NET中可以通过Forms验证+UrlAuthorizationModule来实现用户身份验证和访问授权,更多信息可参考文档:https://docs.microsoft.com/en-us/aspnet/web-forms/overview/older-versions-security/membership/user-based-authorization-cs
ASP.NET MVC中的访问限制
Forms验证+UrlAuthorizationModule的方式是用于基于ASP.NET Web Form的应用程序,而ASP.NET MVC虽然也可以使用Forms验证,但是ASP.NET MVC的授权方式是不一样的,它是通过过滤器的方式实现,下面代码为之前文章中用于限制后台管理页面需要登录的代码:

通过在Controller上使用了一个名为Authorize的特性来实现的,这个特性的定义如下:

它用于当用户访问Controller或Action方法时可以通过用户信息对其访问进行限制。
在Authorize特性的定义中可以看到名为Roles以及Users的属性,其作用就是设置可以访问该资源的用户或者角色:

使用方法如下所示:

ASP.NET MVC中的用户信息
通过前面的介绍可以知道用户的授权是根据用户信息来的,无论是基于角色的、用户的、声明的甚至是自定义的,都需要依赖用户信息进行权限判断,那么ASP.NET MVC中到底包含什么用户信息?
1. HttpContextBase与IPrincipal:
首先可以知道的是在ASP.NET中有一个最核心的HTTP上下文对象HttpContextBase,它保存了整个Http请求到响应过程的所有相关数据,其定义如下:

其中就包含了一个名为User的IPrincipal类型:

该类型中的Identity属性就包含了用户的信息:

从接口中可以看到它仅仅包含了用户名、身份验证反射以及是否验证通过三个属性。
注:IPrincipal的实现有多种而本例中使用的是ClaimPrincipal。

2. ClaimsIdentity与AuthenticationTicket:
通过前面的文章分析得知Identity基于Cookie的身份验证方式实际上是对一个AuthenticationTicket对象序列化加密、反序列化解密的过程,而这个AuthenticationTicket就携带了所有用户的信息,在AuthenticationTicket的定义中可以看到两个重要的对象,其中AuthenticationProperties保存了身份验证的会话信息,如过期时间、是否允许刷新等。而另一个ClaimsIdentity属性就是以声明(Claim)的方式实现的用户信息。

ClaimsIdentity的部分定义如下:

其中除了实现IIdentity接口的属性外,还有一个重要的属性是Claims,它用于以声明的方式来保存用户信息,那么Identity是如何完成用户数据填充的?
ASP.NET Identity用户身份信息填充
用户的获取填充主要是在用户登录(注册用户后会自动登录)的时候完成的,因为在后续的请求中Identity仅需通过解析加密后的用户信息字符串即可获得用户信息(注:会存在重新生成刷新该信息的情况,如身份信息的滑动过期等)。
通过前面文章的分析知道了在Identity中用户的登录是通过SignInManager对象完成的以下是用户登录的代码及注册代码:

以下是注册代码,实际上是创建完成用户后执行了登录操作:

注:通过对源码分析,SignInManager.PasswordSignInAsync方法实际上最后也是调用SignInAsync方法完成的登录。
那么SignInAsync到底做了什么?

从代码中可以看到,该方法调用了一个名为CreateUserIdentityAsync的方法,根据其方法名、参数以及返回值类型来看就已经可以确定该方法就是通过用户对象生成上面提到的用于以声明的方式保存用户信息的方法。从它的实现中可以看出它实际上是通过UserManager生成的:

而UserManager又是通过ClaimsIdentityFactory完成的:

ClaimsIdentityFactory.CreateAsync方法的实现:

注:实际上身份信息的刷新也是通过UserManager完成的:

方法的实现仍然是UserManager的CreateIdentityAsync方法:

从实现中可以看出如果UserManager支持角色、声明等功能,它会从数据库中加载对应的信息以声明(Claim)的形式保存在ClaimsIdentity对象中。
在数据库中添加以下数据(为了演示功能直接在数据库中添加角色、声明信息并于用户数据进行关联,如果要开发此功能可基于UserManager完成):
角色信息:

用户声明(Claim):

角色与用户信息关联:

注:由于此处Identity EF与MySQL,对象与表映射存在问题,所以多了一些ID列,暂时不管这个问题,关于Identity与MySQL用法可以参考这篇文档:https://docs.microsoft.com/en-us/aspnet/identity/overview/getting-started/aspnet-identity-using-mysql-storage-with-an-entityframework-mysql-provider
运行程序并登录后,在用户信息(ClaimsIdentity)中可找到添加的角色和声明信息:

ASP.NET MVC访问限制的实现
上面介绍了用户信息的填充,那么访问的限制实际上就是对用户信息比较而已,下面是Authorize特性的核心方法:

其中核心的三个判断为:
● user.Identity.IsAuthenticated:必须通过身份验证。
● (this._usersSplit.Length <= 0 || this._usersSplit.Contains(user.Identity.Name, StringComparer.OrdinalIgnoreCase)):特性没有指定用户或者当前用户存在于指定的用户列表中。
● (this._rolesSplit.Length <= 0 || this._rolesSplit.Any(new Func<string, bool>(user.IsInRole)):特性没有指定角色或者当前用户拥有指定的角色。
以上三个条件必须全部符合才能够访问。
ASP.NET MVC基于用户声明的访问限制及自定义访问限制
ASP.NET MVC中虽然用户信息是基于声明的方式保存的,但是却没有实际的实现,所以需要自己动手实现一个(注:也可以参考ASP.NET Core中的实现https://docs.microsoft.com/en-us/aspnet/core/security/authorization/claims)。
实现一个自定义的授权特性(注:之前分析过HttpContext中的User是一个IPrincipal类型,实际上MVC使用的是与ClaimsIdentity对应的ClaimsPrincipal)代码如下,该过滤器只是在原有的授权方式基础上添加了声明的检查:

使用方式如下:

登录后访问上面action得到下面结果,验证通过:

注:需要注意的是以上介绍的授权方式,无论是通过角色、用户名还是声明,它都需要以硬编码的形式写在代码中,换句话说就是在开发时必须确定该功能或者Controller/Action访问需求。但是一些时候也会出现访问需求不确定的情况,访问权限的配置会在运行时通过配置文件或者数据库来动态配置,这样的话自定义的授权过滤器就需要依赖一些业务组件来实现自定义的授权流程。
小结
本章介绍了授权与身份验证的关系以及在ASP.NET中的实现,并详细介绍了ASP.NET MVC中的Identity是如何使用身份验证数据来完成授权的。常见的授权方式一般是基于用户名、角色以及声明,但是它们使用的场景边界是不那么明确的,就是说用什么都行实际情况需要根据需求来看,一般权限控制较简单的使用基于角色的授权即可。但无论基于什么来对用户授权,这些信息都属于用户信息,所以在拓展用户的授权时首先要考虑的是用户的特征信息,其次是用户身份验证时如何获取填充这些信息,最后才是考虑如何使用这些信息来进行授权。
参考:
https://msdn.microsoft.com/en-us/library/wce3kxhd.aspx
https://stackoverflow.com/questions/21645323/what-is-the-claims-in-asp-net-identity
https://www.codeproject.com/Articles/98950/ASP-NET-authentication-and-authorization
本文链接:http://www.cnblogs.com/selimsong/p/7828326.html
ASP.NET没有魔法——ASP.NET Identity与授权的更多相关文章
- ASP.NET没有魔法——ASP.NET 身份验证与Identity
前面的文章中为My Blog加入了文章的管理功能(ASP.NET没有魔法——ASP.NET MVC使用Area开发一个管理模块),但是管理功能应该只能由“作者”来访问,那么要如何控制用户的访问权限?也 ...
- ASP.NET没有魔法——ASP.NET Identity 的“多重”身份验证
ASP.NET Identity除了提供基于Cookie的身份验证外,还提供了一些高级功能,如多次输入错误账户信息后会锁定用户禁止登录.集成第三方验证.账户的二次验证等,并且ASP.NET MVC的默 ...
- ASP.NET没有魔法——ASP.NET MVC 过滤器(Filter)
上一篇文章介绍了使用Authorize特性实现了ASP.NET MVC中针对Controller或者Action的授权功能,实际上这个特性是MVC功能的一部分,被称为过滤器(Filter),它是一种面 ...
- ASP.NET没有魔法——ASP.NET MVC使用Oauth2.0实现身份验证
随着软件的不断发展,出现了更多的身份验证使用场景,除了典型的服务器与客户端之间的身份验证外还有,如服务与服务之间的(如微服务架构).服务器与多种客户端的(如PC.移动.Web等),甚至还有需要以服务的 ...
- ASP.NET没有魔法——ASP.NET OAuth、jwt、OpenID Connect
上一篇文章介绍了OAuth2.0以及如何使用.Net来实现基于OAuth的身份验证,本文是对上一篇文章的补充,主要是介绍OAuth与Jwt以及OpenID Connect之间的关系与区别. 本文主要内 ...
- ASP.NET没有魔法——ASP.NET MVC 模型绑定解析(下篇)
上一篇<ASP.NET没有魔法——ASP.NET MVC 模型绑定解析(上篇)>文章介绍了ASP.NET MVC模型绑定的相关组件和概念,本章将介绍Controller在执行时是如何通过这 ...
- ASP.NET没有魔法——ASP.NET MVC 与数据库大集合
ASP.NET没有魔法——ASP.NET与数据库 ASP.NET没有魔法——ASP.NET MVC 与数据库之MySQL ASP.NET没有魔法——ASP.NET MVC 与数据库之ORM ASP.N ...
- ASP.NET没有魔法——ASP.NET MVC 路由的匹配与处理
ASP.NET MVC的路由是MVC应用的一个核心也是MVC应用处理的入口,作为一个开发者,在正常情况下仅仅需要做的就是根据需求去定义实体.业务逻辑,然后在MVC的Controller中去调用.Vie ...
- ASP.NET没有魔法——ASP.NET MVC IoC
之前的文章介绍了MVC如何通过ControllerFactory及ControllerActivator创建Controller,而Controller又是如何通过ControllerBase这个模板 ...
- ASP.NET没有魔法——ASP.NET MVC Razor与View渲染
对于Web应用来说,它的界面是由浏览器根据HTML代码及其引用的相关资源进行渲染后展示给用户的结果,换句话说Web应用的界面呈现工作是由浏览器完成的,Web应用的原理是通过Http协议从服务器上获取到 ...
随机推荐
- 入坑IT十年(二)技术以外
上一篇博客里提到:技术越来越简单,发布后不久,就看到<技术并不是越来越简单>,这显然是打擂台来了. 技术究竟是不是越来越简单?其实这个问题,要看你究竟是以什么角度来思考这个问题.我们可以举 ...
- 神奇的版本库—————GIT
表示是第一次接触这个东东,然后疯狂百度了一波资料,然而=-=,完全不敢相信居然百度出了,GIT是全球最大同性交友网站...... 简直有点毁三观呐..好吧,其实按道理来说,这么解释也没有错欸,官方说明 ...
- python中ConfigParse模块的用法
ConfigParser 是Python自带的模块, 用来读写配置文件, 用法及其简单. 配置文件的格式是: [...]包含的叫section section 下有option=value这样的键值 ...
- wampserver里面出现403错误的问题解决方法
今天再装wampserver的时候,不能进入localhost和phpmyadmin,提示403错误,我自己是win10系统,已通过以下方法解决了: 1.第一个问题,就是wampserver没有切换到 ...
- iOS多线程基本使用
大家都知道,在开发过程中应该尽可能减少用户等待时间,让程序尽可能快的完成运算.可是无论是哪种语言开发的程序最终往往转换成汇编语言进而解释成机器码来执行.但是机器码是按顺序执行的,一个复杂的多步操作只能 ...
- typescript 的 polyfill 学习1-Class 继承篇
Class 继承 js 是多范式的编程语言,同样也是支持面向对象编程的,类 是面向对象中是很重要的概念. 区别于传统的java,c#基于模板的类,js是基于原型的. 类继承一般是通过原型链的方式来实现 ...
- Android Annotations(2)
AndroidAnnotations是一个开源框架,旨在加快Android开发的效率.通过使用它开放出来的注解api,你几乎可以使用在任何地方, 大大的减少了无关痛痒的代码量,让开发者能够抽身其外,有 ...
- C语言第一次实验报告————PTA实验1.2.3内容
一.PTA实验作业 题目1.温度转换 本题要求编写程序,计算华氏温度100°F对应的摄氏温度.计算公式:C=5×(F−32)/9,式中:C表示摄氏温度,F表示华氏温度,输出数据要求为整型. 1.实验代 ...
- Ubuntu软件中心卡在正在应用更改的解决办法
http://forum.ubuntu.org.cn/viewtopic.php?t=374037 http://forum.ubuntu.org.cn/viewtopic.php?p=2743994 ...
- JavaFX引入资源问题
描述 - 使用javafx 引入资源的时候 抛出异常 在swing引入资源 采取相对路径即可,而javafx不是 ImageView imageNode = (ImageView) root.look ...