当我们用.net MVC构建网站平台的时候,势必会对网站平台的安全性和用户的使用权限进行一个统一的构建,首先在.net MVC 架构中,系统已经将权限管理分为三个层面来进行管理,第一层面是 登录管理,也就是说在这个层面上完成用户口令的验证,即完成用户的登录,系统保存用户的登录记录。第二层面是授权,即赋予用户是否具备使用权限,是通过 AuthorizeAttribute 的特性来进行控制和管理的。第三个层面是是凭据缓存机制,系统建立System.Security.Claims.Claim来实现的,一旦登录成功系统就会在Cookies里面缓存给用户一个Claim,这样完成了用户登录信息的缓存。
那么如果对某一个模块具体进行权限控制呢?为了建立一个更加灵活的管理机制,我们采用了建立自己的 AuthorizeAttribute 来完成。首先我们来对需求进行一个简单的分析,如下图所示:一个功能模块我们可以通过指定用户,或指定权限来完成,也可能在使用时,通过角色和权限的控制来完成。
也就是说用户可以通过用户,也可以通过角色来进行控制,也可以在运行期通过角色和功能模块的指定来进行。
针对这个需求,我们建立了如下数据结构:
人员和角色是通过用户归属角色表进行关联的,而“功能权限控制”是功能直接和用户还是角色关联控制的。
为了实现这个功能设计,首先我们先在Models中建立四个基础信息类
namespace VonPortal.Web.Models { /// <summary>人员基本信息 信息类</summary> public class UserInfo : IUser<int> { public int Id { get { return ID; } } #region "Public Properties" /// <summary>序号</summary> [Required] [Display(Name = "序号")] public int ID { get; set; } /// <summary>登录名</summary> [Required] [Display(Name = "登录名")] public string UserName { get; set; } /// <summary>姓氏</summary> [Display(Name = "姓氏")] public string FirstName { get; set; } /// <summary>名字</summary> [Display(Name = "名字")] public string LastName { get; set; } /// <summary>显示名称</summary> [Display(Name = "显示名称")] public string DisplayName { get; set; } /// <summary>称谓</summary> [Display(Name = "称谓")] public string Title { get; set; } /// <summary>简称</summary> [Required] [Display(Name = "简称")] public string ShortName { get; set; } /// <summary>电子邮箱</summary> [Required] [Display(Name = "电子邮箱")] public string EMail { get; set; } /// <summary>登录口令</summary> [Display(Name = "登录口令")] public string Password { get { return _Password; } set { _Password = UserCtrl.CryptPassword(value); } } internal string _Password = ""; /// <summary>创建时间</summary> [Display(Name = "创建时间")] public DateTime CreateDate { get; set; } /// <summary>最后访问时间</summary> [Display(Name = "最后访问时间")] public DateTime LastAccess { get; set; } #endregion } }
#region "角色信息信息类声明" namespace VonPortal.Web.Models { /// <summary>角色信息 信息类</summary> public class RoleInfo : IRole<int> { public int Id { get { return ID; } } public string Name { get { return RoleName; } set { RoleName = value; } } #region "Constructors" /// <summary> /// 构造函数 /// </summary> public RoleInfo() { } /// <summary> /// 含初始化构造函数 /// </summary> /// <param name="ID">序号</param> /// <param name="GroupName">组名称</param> /// <param name="RoleName">角色名称</param> /// <param name="SiteIdx">站点序号</param> /// <param name="ParentIdx">上级节点</param> /// <param name="InheritKind">继承关系</param> /// <param name="Description">角色说明</param> public RoleInfo(int ID, string GroupName, string RoleName, int SiteIdx, int ParentIdx, int InheritKind, string Description) { this.ID = ID; this.GroupName = GroupName; this.RoleName = RoleName; this.SiteIdx = SiteIdx; this.ParentIdx = ParentIdx; this.InheritKind = InheritKind; this.Description = Description; } #endregion #region "Public Properties" /// <summary>序号</summary> [Required] [Display(Name = "序号")] public int ID { get; set; } /// <summary>组名称</summary> [Display(Name = "组名称")] public string GroupName { get; set; } /// <summary>角色名称</summary> [Display(Name = "角色名称")] public string RoleName { get; set; } /// <summary>站点序号</summary> [Required] [Display(Name = "站点序号")] public int SiteIdx { get; set; } /// <summary>上级节点</summary> [Required] [Display(Name = "上级节点")] public int ParentIdx { get; set; } /// <summary>继承关系</summary> [Display(Name = "继承关系")] public int InheritKind { get; set; } /// <summary>角色说明</summary> [Display(Name = "角色说明")] public string Description { get; set; } #endregion } } #endregion
//功能权限控制(PTL_ActionRight) #region "功能权限控制信息类声明" namespace VonPortal.Web.Models { /// <summary>功能权限控制 信息类</summary> public class ActionRightInfo { #region "Constructors" /// <summary> /// 构造函数 /// </summary> public ActionRightInfo() { } /// <summary> /// 含初始化构造函数 /// </summary> /// <param name="ID">序号</param> /// <param name="SiteIdx">站点序号</param> /// <param name="RightName">功能名称</param> /// <param name="RoleOrUser">角色或用户序号</param> /// <param name="SrcIdx">控制源序号</param> public ActionRightInfo(int ID, int SiteIdx, string RightName, bool RoleOrUser, int SrcIdx) { this.ID = ID; this.SiteIdx = SiteIdx; this.RightName = RightName; this.RoleOrUser = RoleOrUser; this.SrcIdx = SrcIdx; } #endregion #region "Public Properties" /// <summary>序号</summary> [Required] [Display(Name = "序号")] public int ID { get; set; } /// <summary>站点序号</summary> [Required] [Display(Name = "站点序号")] public int SiteIdx { get; set; } /// <summary>功能名称</summary> [Display(Name = "功能名称")] public string RightName { get; set; } /// <summary>角色或用户序号</summary> [Required] [Display(Name = "角色或用户序号")] public bool RoleOrUser { get; set; } /// <summary>控制源序号</summary> [Required] [Display(Name = "控制源序号")] public int SrcIdx { get; set; } #endregion } } #endregion
其控制与数据库存储的类,这里就省略了,首先大家知道,要实现这个方案,作为基础的信息基类,就是这三个类即,UserInfo、RoleInfo、ActionRightInfo。
我们下面就进入今天的主题,如何通过建立自己的AuthorizeAttribute特性来实现角色控制。
我们先来分析一下AuthorizeAttribute的执行过程。在特性指定到系统的某一个模块上时,即为某一个 Controller 指定一个我们自己的权限认证特性,指定后,当系统执行时,系统就会调用 AuthorizeAttribute 中的 OnAuthorization 函数来完成,在 OnAuthorization 函数的执行过程中,系统会调用 bool AuthorizeCore(HttpContextBase httpContext) 函数完成具体的验证过程,如果失败了,系统会调用 void HandleUnauthorizedRequest(AuthorizationContext context)来进行处理,由此我们就可以建立一个自己的 AuthorizeAttribute
/****************************************************************************************************************** * 权限验证单元 * RightAuthorizeAttribute 是继承 AuthorizeAttribute 的权限控制属性,他是基于功能的权限认证模式,即采用功能名称 * 进行认证,认证过程基于 AuthorizeAttribute 进行,首先系统会调用 OnAuthorization 进行验证,在这里我们主要进行 * 验证前准备, ****************************************************************************************************************/ using System; using System.Threading.Tasks; using Microsoft.AspNet.Identity; using Microsoft.AspNet.Identity.Owin; using Microsoft.Owin.Security; using System.Web.Mvc; using System.Web.Routing; using System.Web; using System.Collections.Generic; using VonPortal.Web.Operators; namespace VonPortal.Web.Models { /// <summary> /// 权限控制单元 /// </summary> public class RightAuthorizeAttribute : AuthorizeAttribute { /// <summary> /// 功能名称 /// </summary> public string ActionNames { get; set; } private string moduleName = ""; public override void OnAuthorization(AuthorizationContext filterContext) { string controllerName = filterContext.ActionDescriptor.ControllerDescriptor.ControllerName; string actionName = filterContext.ActionDescriptor.ActionName; moduleName = controllerName + '/' + actionName; base.OnAuthorization(filterContext); } protected override bool AuthorizeCore(HttpContextBase httpContext) { bool result = false; if (httpContext == null) throw new ArgumentNullException("HttpContext"); if (!httpContext.User.Identity.IsAuthenticated) return false; // 得到用户的唯一序号 int userIdx = httpContext.User.Identity.GetUserId<int>(); List<int> roles = (new UserRoleCtrl(null)).ListByROLE_REF_USER(userIdx); #region "检测 Roles 即,检测指定的 Role 是否存在,如果设定了,但不存在则表明无权访问。" if (Roles != null && Roles.Length > 0) { string authRole = "," + Roles.ToLower() + ","; result = false; foreach (int roleIdx in roles) { RoleInfo role = (new RoleCtrl(null)).GetByRole(roleIdx); result = role != null && authRole.IndexOf("," + role.RoleName.ToLower() + ",") >= 0; if (result) break; } if (!result) return false; } #endregion #region "检测 Users 即,检测指定的 Users 是否存在,如果设定了,但不存在则表明无权访问。" string userName = httpContext.User.Identity.GetUserName(); if (Users != null && Users.Length > 0) { if (("," + Users.ToLower() + ",").IndexOf("," + userName.ToLower() + ",") < 0) return false; result = true; } #endregion #region "检测 ActionNames 即,检测指定的 ActionNames 是否存在,如果设定了,但不存在则表明无权访问。" if (ActionNames != null && ActionNames.Length > 0) { string authAction = "," + ActionNames.ToLower() + ","; // 检测用户本身是否具备该功能的权限 foreach (ActionRightInfo right in (new ActionRightCtrl(null)).ListBySrcIdx(true, userIdx)) { if (ActionNames.IndexOf("," + right.RightName + ",") >= 0) return true; } // 检测用户拥有的角色本身是否具备该功能的权限 foreach (int roleIdx in roles) { foreach (ActionRightInfo right in (new ActionRightCtrl(null)).ListBySrcIdx(false, roleIdx)) { if (ActionNames.IndexOf("," + right.RightName + ",") >= 0) return true; } } } else if (!result) { string authAction = "," + moduleName.ToLower() + ","; // 检测用户本身是否具备该功能的权限 foreach (ActionRightInfo right in (new ActionRightCtrl(null)).ListBySrcIdx(true, userIdx)) { if (ActionNames.IndexOf("," + right.RightName + ",") >= 0) return true; } // 检测用户拥有的角色本身是否具备该功能的权限 foreach (int roleIdx in roles) { foreach (ActionRightInfo right in (new ActionRightCtrl(null)).ListBySrcIdx(false, roleIdx)) { if (ActionNames.IndexOf("," + right.RightName + ",") >= 0) return true; } } } #endregion return result; } protected override void HandleUnauthorizedRequest(AuthorizationContext context) { if (context == null) { throw new ArgumentNullException("filterContext"); } else { string path = context.HttpContext.Request.Path; string strUrl = "/Account/Login?returnUrl={0}"; context.HttpContext.Response.Redirect(string.Format(strUrl, HttpUtility.UrlEncode(path)), true); } } } }
使用方法:
在Controller中的Action使用前增加我们的RightAuthorize的特性就可以了。
1、指定功能名称验证用户权限:
[RightAuthorize(ActionNames = "UserManager")]
public ActionResult UserList(...){...}
2、指定角色名称验证用户权限:
[RightAuthorize(Roles = "Admin")]
public ActionResult UserDetails(...){...}
3、指定用户名称验证用户权限:
[RightAuthorize(Users = "Administrator,Host")]
public ActionResult UserEdit(...){...}
4、默认通过Controller+Action来验证用户权限:默认的功能名称是ControllerName/ActionName
[RightAuthorize()]
public ActionResult UserDelete(...){...}