AuthorizeAttribute是asp.net MVC的几大过滤器之一,俗称认证和授权过滤器,也就是判断登录与否,授权与否。当为某一个Controller或Action附加该特性时,没有登录或授权的账户是不能访问这些Controller或Action的。

在进入一个附加了Authorize特性的Controller或Action之前,首先执行的是AuthorizeAttribute类的OnAuthorization(AuthorizationContext filterContext)方法,接着OnAuthorization方法会去调用其他的诸如AuthorizeCore方法。

在将AuthorizeAttribute类反编译之后得到如下代码,一看就清晰明了:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Principal;
using System.Web.Mvc.Properties;
namespace System.Web.Mvc
{
/// <summary>Specifies that access to a controller or action method is restricted to users who meet the authorization requirement.</summary>
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, Inherited = true, AllowMultiple = true)]
public class AuthorizeAttribute : FilterAttribute, IAuthorizationFilter
{
private static readonly char[] _splitParameter = new char[]
{
','
};
private readonly object _typeId = new object();
private string _roles;
private string[] _rolesSplit = new string[];
private string _users;
private string[] _usersSplit = new string[];
/// <summary>Gets or sets the user roles that are authorized to access the controller or action method.</summary>
/// <returns>The user roles that are authorized to access the controller or action method.</returns>
public string Roles
{
get
{
return this._roles ?? string.Empty;
}
set
{
this._roles = value;
this._rolesSplit = AuthorizeAttribute.SplitString(value);
}
}
/// <summary>Gets the unique identifier for this attribute.</summary>
/// <returns>The unique identifier for this attribute.</returns>
public override object TypeId
{
get
{
return this._typeId;
}
}
/// <summary>Gets or sets the users that are authorized to access the controller or action method.</summary>
/// <returns>The users that are authorized to access the controller or action method.</returns>
public string Users
{
get
{
return this._users ?? string.Empty;
}
set
{
this._users = value;
this._usersSplit = AuthorizeAttribute.SplitString(value);
}
}
/// <summary>When overridden, provides an entry point for custom authorization checks.</summary>
/// <returns>true if the user is authorized; otherwise, false.</returns>
/// <param name="httpContext">The HTTP context, which encapsulates all HTTP-specific information about an individual HTTP request.</param>
/// <exception cref="T:System.ArgumentNullException">The <paramref name="httpContext" /> parameter is null.</exception>
protected virtual bool AuthorizeCore(HttpContextBase httpContext)
{
if (httpContext == null)
{
throw new ArgumentNullException("httpContext");
}
IPrincipal user = httpContext.User;
return user.Identity.IsAuthenticated && (this._usersSplit.Length <= || this._usersSplit.Contains(user.Identity.Name, StringComparer.OrdinalIgnoreCase)) && (this._rolesSplit.Length <= || this._rolesSplit.Any(new Func<string, bool>(user.IsInRole)));
}
private void CacheValidateHandler(HttpContext context, object data, ref HttpValidationStatus validationStatus)
{
validationStatus = this.OnCacheAuthorization(new HttpContextWrapper(context));
}
/// <summary>Called when a process requests authorization.</summary>
/// <param name="filterContext">The filter context, which encapsulates information for using <see cref="T:System.Web.Mvc.AuthorizeAttribute" />.</param>
/// <exception cref="T:System.ArgumentNullException">The <paramref name="filterContext" /> parameter is null.</exception>
public virtual void OnAuthorization(AuthorizationContext filterContext)
{
if (filterContext == null)
{
throw new ArgumentNullException("filterContext");
}
if (OutputCacheAttribute.IsChildActionCacheActive(filterContext))
{
throw new InvalidOperationException(MvcResources.AuthorizeAttribute_CannotUseWithinChildActionCache);
}
bool flag = filterContext.ActionDescriptor.IsDefined(typeof(AllowAnonymousAttribute), true) || filterContext.ActionDescriptor.ControllerDescriptor.IsDefined(typeof(AllowAnonymousAttribute), true);
if (flag)
{
return;
}
if (this.AuthorizeCore(filterContext.HttpContext))
{
HttpCachePolicyBase cache = filterContext.HttpContext.Response.Cache;
cache.SetProxyMaxAge(new TimeSpan(0L));
cache.AddValidationCallback(new HttpCacheValidateHandler(this.CacheValidateHandler), null);
return;
}
this.HandleUnauthorizedRequest(filterContext);
}
/// <summary>Processes HTTP requests that fail authorization.</summary>
/// <param name="filterContext">Encapsulates the information for using <see cref="T:System.Web.Mvc.AuthorizeAttribute" />. The <paramref name="filterContext" /> object contains the controller, HTTP context, request context, action result, and route data.</param>
protected virtual void HandleUnauthorizedRequest(AuthorizationContext filterContext)
{
filterContext.Result = new HttpUnauthorizedResult();
}
/// <summary>Called when the caching module requests authorization.</summary>
/// <returns>A reference to the validation status.</returns>
/// <param name="httpContext">The HTTP context, which encapsulates all HTTP-specific information about an individual HTTP request.</param>
/// <exception cref="T:System.ArgumentNullException">The <paramref name="httpContext" /> parameter is null.</exception>
protected virtual HttpValidationStatus OnCacheAuthorization(HttpContextBase httpContext)
{
if (httpContext == null)
{
throw new ArgumentNullException("httpContext");
}
if (!this.AuthorizeCore(httpContext))
{
return HttpValidationStatus.IgnoreThisRequest;
}
return HttpValidationStatus.Valid;
}
internal static string[] SplitString(string original)
{
if (string.IsNullOrEmpty(original))
{
return new string[];
}
IEnumerable<string> source =
from piece in original.Split(AuthorizeAttribute._splitParameter)
let trimmed = piece.Trim()
where !string.IsNullOrEmpty(trimmed)
select trimmed;
return source.ToArray<string>();
}
}
}

通过以上描述,我们可以在自己的MVC程序中使用AuthorizeAttribute类或者其派生的子类来达到用户认证和授权的目的。

一、新建一个MVC项目

新建一个MVC项目,选择不用身份验证。

二、添加Model

为了达到演示目的,添加了一个简单的StudentViewModel,用来显示业务数据:

public class StudentViewModel {
public int ID { get; set; }
public string Name { get; set; }
public string Tel { get; set; }
}

添加一个UserViewModel,用来进行登录和角色授权:

    public class UserViewModel {
[DisplayName("用户名")]
public string UserName { get; set; }
[DisplayName("密 码")]
public string Pwd { get; set; }
public Role Role { get; set; }
} public enum Role {
Admin=,
Normal=,
System=
}

OK,本篇讲述就不去访问数据库了,直接用业务数据。然后我们再新建一个Data类,存放业务数据:

    public class Data {
public static List<StudentViewModel> students = new List<StudentViewModel> {
new StudentViewModel { ID=, Name="张三", Tel="" },
new StudentViewModel { ID=, Name="李四", Tel="" },
new StudentViewModel { ID=, Name="王五", Tel="" },
new StudentViewModel { ID=, Name="赵六", Tel="" },
new StudentViewModel { ID=, Name="孙琦", Tel="" }
}; public static List<UserViewModel> users = new List<UserViewModel> {
new UserViewModel { UserName="admin", Pwd="", Role=Role.Admin },
new UserViewModel { UserName="normal", Pwd="", Role=Role.Normal},
new UserViewModel { UserName="system", Pwd="", Role=Role.System}
};
}

三、创建Controller控制器和视图

1、新建StudentController

Index视图是显示全部的Student数据,FindOne是显示一条Student数据。:

    public class StudentController : Controller {

        public ActionResult Index() {
return View(Data.Data.students);
}
public ActionResult FindOne(int? id) {
return View("Index", Data.Data.students.Where(s => s.ID == id));
}
}

现在没有对Student控制器做任何的身份和授权访问限制。接下来我们创建一个登陆用的控制器。

2、新建AccountController

    public class AccountController : Controller {
//用于获取登录页面
[HttpGet]
public ActionResult Login() {
return View();
} //用于登录验证
[HttpPost]
public ActionResult Login(LoginViewModel login) {
if (ModelState.IsValid) {
UserViewModel clogin = Data.Data.users.Where(u => u.UserName == login.UserName && u.Pwd == login.Pwd).FirstOrDefault();
if (clogin != null) {
//FormsAuthentication.SetAuthCookie有两个参数
//第一个参数:设置当前登录用户的标识,可以自定义
//第二个参数:
//true:设置永久的。可以在web.config中设置过期时间,喏,下面的timeout就是过期时间,以分钟为单位。如下所示:
// < system.web >
// < authentication mode = "Forms" >
// < forms loginUrl = "~/Account/Login" timeout = "2" />
// </ authentication >
// </ system.web >
//false:设置当前会话时间为有效时间,浏览器或页面已关闭就需要重新登录。
FormsAuthentication.SetAuthCookie(clogin.UserName, false);
return RedirectToAction("FindOne", "Student", new { id = });
}
}
return View();
} //用于注销登录
[Authorize]
[NoCache]
public ActionResult LoginOut() {
FormsAuthentication.SignOut();
return RedirectToAction("Login", "Account");
} //用于显示无权限消息
public ActionResult NoPemission() {
return View();
}
}

登陆页面如下:

@model MVCDemo2.ViewModels.UserViewModel
@{
Layout = null;
} <!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title>Login</title>
<link href="~/Content/w3.css" rel="stylesheet" />
<style>
html, body {
margin: ;
padding: ;
} .main {
width: 400px;
height: 300px;
position: absolute;
left: ;
top: ;
right: ;
bottom: ;
margin: auto;
}
</style>
</head>
<body> <div class="main">
<h3 class="w3-teal w3-center w3-margin-0" style="width:100%;">权限管理系统</h3>
@using (Html.BeginForm("Login", "Account", FormMethod.Post, new { @class = "w3-container w3-card-4 w3-light-grey", style = "width:100%;" })) {
<table class="w3-table">
<tr>
<td style="width:20%; vertical-align:middle;text-align:right;">@Html.LabelFor(x => x.UserName)</td>
<td style="width:80%;">@Html.TextBoxFor(x => x.UserName, new { @class = "w3-input w3-border", placeholder = "用户名" })</td>
</tr>
<tr class="w3-padding">
<td style="vertical-align:middle;text-align:right;">@Html.LabelFor(x => x.Pwd)</td>
<td>@Html.PasswordFor(x => x.Pwd, new { @class = "w3-input w3-border", placeholder = "密 码" })</td>
</tr>
<tr style="display:none;">
<td><input type="text" id="returnUrl" name="returnUrl" value="@Url.Encode(Request.Url.ToString())"/></td>
</tr>
<tr>
<td></td>
<td style="text-align:left;">
<div style="width:30%;"><button type="submit" class="w3-btn-block w3-teal">登录</button></div>
</td>
</tr>
</table>
}
</div>
@*<script>
function login() { }
</script>*@
</body>
</html>

无权限提示页面如下,也就是NoPemission:

@{
Layout = null;
}
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title>NoPemission</title>
</head>
<body>
<div>
您无权限操作该页面!
</div>
</body>
</html>

四、创建MyAuthorizeAttribute权限验证类

创建MyAuthorizeAttribute类的目的是需要做身份验证和权限验证所用,通过继承AuthorizeAttribute类来达到目的:

    public class MyAuthorizeAttribute : AuthorizeAttribute {
//设置是否拥有某些权限的状态码
private int _status = ;
protected override bool AuthorizeCore(HttpContextBase httpContext) {
//切记:一定要在设置_status的方法的第一行初始化该字段,否则会出现问题。因为针对同一Action来说,上一次访问后完成后,下一次不同权限的账户又来访问,如果不初始化该_status字段,那么该_status字段值还是上一次的旧值。
//估计绑定的Action的Controller实例还存在,没有被回收,等到下一次调用该Controller时,就是会激活该Controller实例
_status=0;
//判断当前登录的用户 是否已经登录过且身份认证被通过
if (httpContext.User.Identity.IsAuthenticated) {
//判断当前的Roles字段是否是空,是空的,则说明该控制器或Action不需要角色权限控制
if (string.IsNullOrWhiteSpace(Roles)) return true;
//通过半角逗号去获取一个权限数组
string[] roles = Roles.Split(new string[] { "," }, StringSplitOptions.RemoveEmptyEntries);
if (roles.Length <= ) return true;
//找到当前登录的用户
//httpContext.User.Identity.Name就是我们在Login验证登录时候设置的clogin.UserName。设置代码为FormsAuthentication.SetAuthCookie(clogin.UserName, false);
UserViewModel user = Data.Data.users.Where(u => u.UserName == httpContext.User.Identity.Name).SingleOrDefault();
//判断当前登录用户是否拥有访问该控制器或Action的权限,如果有,则返回true,可以访问;如果没有,则返回false,需要在OnAuthorization方法中设置该滤过器的Result。
if (roles.Contains(((int)user.Role).ToString()))
return true;
else {
//无角色权限 通过设置_status,可以在OnAuthorization方法中做出相应的判断,并设置相应的filterContext.Result
_status = ;
//授权失败
return false;
}
}
//没有登录,则返回false,表明身份认证未通过,程序直接会跳转到登录页面,登录页面可在web.config中设置
// < system.web >
// < authentication mode = "Forms" >
// < forms loginUrl = "~/Account/Login" timeout = "2" />
// </ authentication >
// </ system.web >
else return false;
} public override void OnAuthorization(AuthorizationContext filterContext) {
//执行父类的OnAuthorization方法
base.OnAuthorization(filterContext);
//如果当前的_status有状态变化,则返回相应的ActionResult。当前返回的是无权限查看的提示界面。
if (_status == )
filterContext.Result = new RedirectToRouteResult(new System.Web.Routing.RouteValueDictionary(new {
//需要跳转的Controller
controller = "Account",
//需要跳转的Action
action = "NoPemission",
//返回的字段说明
returnUrl = filterContext.HttpContext.Request.Url,
returnMessage = "您无权限查看!"
}));
}
}

接着将MyAuthorizeAttribute特性类附加到StudentController的Action中,代码如下:

    public class StudentController : Controller {

        //只要登录成功,即可访问
[MyAuthorize]
public ActionResult Index() {
return View(Data.Data.students);
} //需要登录成功,且该用户角色为 1:Admin 3:System 的才可访问
[MyAuthorize(Roles = "1,3")]
public ActionResult FindOne(int? id) {
return View("Index", Data.Data.students.Where(s => s.ID == id));
}
}

五、测试结果

首先我们将该站点的默认页面设置为Student\Index,设置方法如下:

    public class RouteConfig {
public static void RegisterRoutes(RouteCollection routes) {
routes.IgnoreRoute("{resource}.axd/{*pathInfo}"); routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Student", action = "Index", id = UrlParameter.Optional }
);
}
}

接着运行程序:

因为站点启动的时候,默认访问的是Student控制器的Index行为方法,但我们在该Index行为方法附加了一个需要授权的特性MyAuthorize。因此,在没有登录之前去访问Index行为方法的话,就会跳转到我们在web.config配置文件中设置的登录地址。因此,第一次出现在我们眼前的是一个登录界面:

当我们输入正确的用户名和密码,并点击“登录”按钮后,就能进入Student控制器的Index行为方法了。如下图所示:

因为我们是用admin账号登录的,该账户的权限是Admin,因此我们也可以访问Student的FindOne行为方法。如下图所示:

接下来让我们点击“退出”,注销权限为Admin的admin账户,改用权限为Normal的normal账户试一下,看能否访问被权限控制的Student的FindOne行为方法。

输入账户名为normal,密码为1的账户信息,并点击“登录”按钮:

因为我们在Account的登录验证方法Login行为方法中标注登录验证成功后,就会跳转到Student的FindOne行为方法,因此只要账户名和密码输入正确就能验证是否能成功访问FindOne。请看如下结果:

果然,因为我们在FindOne方法中标注了只有1(Admin)和3(System)权限才能访问该方法,而Normal不在这些访问权限之内。

由此可见,我们可以通过继承AuthorizeAttribute类轻松地来达到身份验证和授权的目的,如果不在权限之内,就是不让你操作。

asp.net MVC之AuthorizeAttribute浅析的更多相关文章

  1. ASP.NET MVC自定义AuthorizeAttribute篇知识点讲解—登录限制

    1.前言 a.微软对ASP.NET的开发从WebForm到MVC的转变,已经正式过去5,6个年头,现在WebForm和MVC也都越来越完善,小小算来我也已经工作了将近三年,从大学的时候学习ASP.NE ...

  2. asp.net mvc 使用AuthorizeAttribute做授权验证

    授权验证,比如登陆验证 1.自定义属性继承AuthorizeAttribute 2.重写OnAuthorization方法 3.通过AllowAnonymousAttribute特性处理无需授权的Ac ...

  3. ASP.NET MVC 基于角色的权限控制系统的示例教程

    上一次在 .NET MVC 用户权限管理示例教程中讲解了ASP.NET MVC 通过AuthorizeAttribute类的OnAuthorization方法讲解了粗粒度控制权限的方法,接下来讲解基于 ...

  4. Asp.Net Mvc: 浅析TempData机制(转发 作者: Tristan G )

    Asp.Net Mvc: 浅析TempData机制 环境: Windows 2008, VS 2008 SP1, Asp.Net Mvc 1.0 --------------------------- ...

  5. 在ASP.NET MVC 3 中自定义AuthorizeAttribute时需要注意的页面缓存问题

    一.ASP.NET MVC中使用OutputCache实现服务器端页面级缓存 在ASP.NET MVC中,假如我们想要将某个页面(即某个Action)缓存在服务器端,可以在Action上标上以下特性: ...

  6. asp.net MVC之Result过滤器浅析

    在asp.net MVC中,每一个Action方法完成之后都会返回一个结果,而我们可以在Result过滤器中根据需要修改这个结果.例如可以根据UserAgent来判断客户端的来源是手机还是PC端,从而 ...

  7. asp.net MVC之Action过滤器浅析

    在asp.net MVC中,Action过滤器是一大利器,它可以在以下两个步骤执行相关的代码: 1.执行Action方法之前:OnActionExecuting 2.Action方法执行完毕后:OnA ...

  8. Asp.Net Mvc: 浅析TempData机制

    一. Asp.Net Mvc中的TempData 在Asp.Net Mvc框架的ControllerBase中存在一个叫做TempData的Property,它的类型为TempDataDictiona ...

  9. ASP.NET MVC开发:Web项目开发必备知识点

    最近加班加点完成一个Web项目,使用Asp.net MVC开发.很久以前接触的Asp.net开发还是Aspx形式,什么Razor引擎,什么MVC还是这次开发才明白,可以算是新手. 对新手而言,那进行A ...

随机推荐

  1. 算法笔记--字典树(trie 树)&& ac自动机 && 可持久化trie

    字典树 简介:字典树,又称单词查找树,Trie树,是一种树形结构,是哈希树的变种. 优点:利用字符串的公共前缀来减少查询时间,最大限度地减少无谓的字符串比较. 性质:根节点不包含字符,除根节点外每一个 ...

  2. 【转】 C语言文件操作详解

    转自:http://www.cnblogs.com/likebeta/archive/2012/06/16/2551780.html C语言中没有输入输出语句,所有的输入输出功能都用 ANSI C提供 ...

  3. Linux中符号总结

    常用符号~   登陆用户当前的家目录 .   当前目录..   当前目录的上一级目录cd -   返回上一次的目录;   命令分隔符#   表示注释 ?   通配符中表示任意一个字符*   通配符中表 ...

  4. LeetCode--342--4的幂

    问题描述: 给定一个整数 (32 位有符号整数),请编写一个函数来判断它是否是 4 的幂次方. 示例 1: 输入: 16 输出: true 示例 2: 输入: 5 输出: false 进阶:你能不使用 ...

  5. Prime Ring Problem HDU - 1016

    A ring is compose of n circles as shown in diagram. Put natural number 1, 2, ..., n into each circle ...

  6. re正则表达式方法

    目录 1.python正则匹配 1.1 re.search 正则表达式 1.2 re.match() 正则表达式 1.3 re.match与re.search的区别 1.4 检索和替换 1.5 正则表 ...

  7. 直播 APP 的直播实现流程

    直播平台搭建所涉及的事项非常的广泛, 不仅需要直播源码. 直播系统开发. 后台服务 器.专门的运维人员等, 还需要技术团队切实的把控.下面, 小编就给大家确切的说下直播 平台搭建需要用到哪些步骤. 1 ...

  8. using强制对象清理资源 【转】

    转 http://www.cnblogs.com/Legolas/p/detail-of-using.html using肯定所有人都用过,最简单的就是使用using引入命名空间,然后就是引入别名,简 ...

  9. 函数使用十二:BAPI_MATERIAL_BOM_GROUP_CREATE(CS61)

    REPORT ZSM_CREATE_SIMPLEBOM.* This code will create a material BoM for the material* MAINMATERIAL wi ...

  10. WDA基础二:界面,元素介绍

    1.ELEMENTS: BUTTON  按钮 CAPTION  标题 DROPDOWN_BY_IDX   带序号的下拉 DROPDOWN_BY_KEY   带键值的下拉 FILE_UPLOAD    ...