Asp.net Mvc4 基於Authorize實現的模塊權限驗證方式


在MVC中,我們可以通過在action或者controller上設置Authorize[Role="xxx"] 的方式來設置用戶對action的訪問權限。顯然,這樣並不能滿足我們的需求,

對於一般的MVC系統來說,如果我們定義一個controller來處理一個模塊的話,我們大致有以下需求:

  一,單個action的訪問權限。如果刪除,列表action

  二,一個action兩種權限,如edit(int? id)如果id為null則添加,或者修改

  三,在此cotroller驗證其它模塊權限,比如,我們要在新聞模塊獲取新聞列表

  四,對於某些通過模塊,如分類,我們希望通過傳入不同的參數可以驗證不同模塊的權限

對於四種情況,我理想的想法是:

對於第一種,直接制定controller的moduleid和action的權限

 [Module(ModuleId=6)]
public class Controller: Controller{
        [SysAuthorize(Permission.List)] //設置action要驗證的權限
        public ActionResult List()
 }

 

對於第二種情況,我們希望通過參數來達到驗證那個權限的目的:

 [Module(ModuleId=6)]
 public class Controller: Controller{
        //如果參數為null是將驗證添加權限否則驗證修改權限
        [SysAuthorize(Permission.Add,Permission.Edit,"id",null)]       
        public ActionResult Edit(int? id)
  }

 

對於第三種情況,我們可以為action驗證指定單獨的模塊id

    [Module(ModuleId=6)]
    public class Controller: Controller{
        [SysAuthorize(9,Permission.List)] //此方面驗證模塊9的列表權限
        public ActionResult List(int CType)
    }

 

對於第四種情況,我們可以為模塊添加不同的參數module對應關系

    [Module(ModuleId=5,"CType",1)]
    [Module(ModuleId=6,"CType",2)]
    public class Controller: Controller{
         如果當前傳入CType為1則驗證ModuleId=5,等於2是驗證ModuleId=6
        [SysAuthorize(Permission.List)]
        public ActionResult List(int CType)
    }

 

想法定好以后,我們就可以去實現了。

首先,我們定義一個module的特性:

/// <summary>
/// 模塊信息特性
/// </summary>
[AttributeUsage(AttributeTargets.Class, Inherited = true, AllowMultiple = true)]
public class ModuleAttribute : Attribute
{
    public ModuleAttribute() { }
    public ModuleAttribute(int moduleId) 
    {
        this.ModuleId = moduleId;
    }
    public ModuleAttribute(int moduleId, string parName, object value)
        : this(moduleId)
    {
        this.ParameterName = parName;
        this.ParameterValue = value;
    }
    /// <summary>
    /// 模塊Id
    /// </summary>
    public int ModuleId { get; set; }
    /// <summary>
    /// 當前模塊對應參數名
    /// </summary>
    public string ParameterName { get; set; }
    /// <summary>
    /// 當前模塊參數值
    /// </summary>
    public object ParameterValue { get; set; }
    /// <summary>
    /// 驗證參數值是否有正確
    /// </summary>
    public bool CheckParameter(HttpRequestBase request)
    {
        var val = request[ParameterName];
        bool b = false;
        if (val == null && ParameterValue == null)
            b = true;
        else if (val != null && ParameterValue != null && val == ParameterValue.ToString())
            b = true;
        return b;
    }
}
View Code

 

實現了模塊特性以后,就是比較重要的驗證特性了:

/// <summary>
/// 系統權限驗證
/// <remarks>
/// 0,只驗證登陸:無參數時,只驗證登陸
/// 1,單一:只一指定權限代碼時驗證當前模塊的指定權限
/// 2,二選一:指定兩種權限代碼時,根據參數驗證,如果參數等於指定參數,則驗證前者,否則驗證后者
/// </remarks>
/// </summary>
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, Inherited = true, AllowMultiple = false)]
public class SysAuthorizeAttribute : AuthorizeAttribute
{
    /// <summary>
    /// 驗證是否登陸
    /// </summary>
    public SysAuthorizeAttribute() { }
    /// <summary>
    /// 驗證基本權限
    /// </summary>
    public SysAuthorizeAttribute(Permission permission)
    {
        this.PermissionFlag = permission;
    }
    /// <summary>
    /// 驗證基本權限
    /// </summary>
    public SysAuthorizeAttribute(int moduleId, Permission permission)
        : this(permission)
    {
        this.ModuleId = moduleId;
        this.IsSetModuleId = true;
    }
    /// <summary>
    /// 帶參數的驗證
    /// </summary>
    public SysAuthorizeAttribute(Permission permission, string parName, object value = null)
    {
        this.PermissionFlag = permission;
        this.ParameterName = parName;
        this.ParameterValue = value;
    }
    /// <summary>
    /// 帶參數的驗證
    /// </summary>
    public SysAuthorizeAttribute(int moduleId, Permission permission, string parName, object value = null)
        : this(permission, parName, value)
    {
        this.ModuleId = moduleId;
        this.IsSetModuleId = true;
    }
    /// <summary>
    /// 帶參數的驗證二選一
    /// </summary>
    public SysAuthorizeAttribute(Permission before, Permission after, string parName, object value = null)
    {
        this.PermissionFlag = before;
        this.PermissionFlag1 = after;
        this.ParameterName = parName;
        this.ParameterValue = value;
    }
    /// <summary>
    /// 帶參數的驗證二選一
    /// </summary>
    public SysAuthorizeAttribute(int moduleId, Permission before, Permission after, string parName, object value = null)
        : this(before, after, parName, value)
    {
        this.ModuleId = moduleId;
        this.IsSetModuleId = true;
    }
    /// <summary>
    /// 當前要驗證的權限代碼
    /// </summary>
    private Permission? PermissionFlag { get; set; }
    /// <summary>
    /// 當前要驗證的另一個權限代碼(當二選一驗證驗證方式時有效)
    /// </summary>
    private Permission? PermissionFlag1 { get; set; }
    /// <summary>
    /// 是否自定義設置了moduleId
    /// </summary>
    private bool IsSetModuleId { get; set; }
    /// <summary>
    /// 獲取或設置當前模塊Id
    /// </summary>
    public int? ModuleId { get; set; }
    /// <summary>
    /// 權限驗證參數名
    /// </summary>
    public string ParameterName { get; set; }
    /// <summary>
    /// 權限驗證參數值
    /// </summary>
    public object ParameterValue { get; set; }
    /// <summary>
    /// 驗證結果
    /// </summary>
    public bool AuthorizeResult { get; private set; }
    /// <summary>
    /// 驗證前獲取moduleId
    /// </summary>
    public override void OnAuthorization(AuthorizationContext filterContext)
    {
        if (!IsSetModuleId)
        {
            var modules = filterContext.Controller.GetModules();
            //一個模塊的的時候,只第一次進入時獲取他的模塊id,緩存以后不作處理
            if (modules.Count == 1 && ModuleId == null) 
            {
                if (!string.IsNullOrWhiteSpace(modules[0].ParameterName))
                {
                    if (modules[0].CheckParameter(filterContext.HttpContext.Request))
                        ModuleId = modules[0].ModuleId;
                }
                else
                    ModuleId = modules[0].ModuleId;
            }
            //多個模塊的時候,每次驗證強制更新及moduleid
            else if (modules.Count > 1)
            {
                foreach (var m in modules)
                {
                    if (m.CheckParameter(filterContext.HttpContext.Request))
                    {
                        ModuleId = m.ModuleId;
                        break;
                    }
                }
            }
        }
        base.OnAuthorization(filterContext);
    }
    /// <summary>
    /// 核心驗證
    /// </summary>
    protected override bool AuthorizeCore(HttpContextBase httpContext)
    {
        //如果未登陸,則跳轉到登陸頁
        if (!httpContext.User.Identity.IsAuthenticated)
            httpContext.Response.Redirect(FormsAuthentication.LoginUrl);
        AuthorizeResult = true;
        if (PermissionFlag != null)
        {
            if (PermissionFlag.Value == Permission.Administrator)
                return AdminSiteService.CheckAdministrator();
            //未設置模塊id,則拋出異常
            if (ModuleId == null)
                throw new Exception(string.Format("未設置模塊id的Control不能進行權限驗證!"));
            //處理二選一
            if (PermissionFlag1 != null)
            {
                if (string.IsNullOrWhiteSpace(ParameterName))
                    throw new Exception(string.Format("請為二選一驗證指定相應的參數名!"));
                //如果參數值等於給定值,則驗證前者,否則驗證后者
                if (CheckParameter(httpContext.Request))
                    AuthorizeResult = AdminSiteService.CheckPermission(ModuleId.Value, (int)PermissionFlag.Value);
                else
                    AuthorizeResult = AdminSiteService.CheckPermission(ModuleId.Value, (int)PermissionFlag1.Value);
            }
            else //一般驗證處理
            {
                //如果參數名不為空,則先驗證參數值是否匹配
                if (!string.IsNullOrWhiteSpace(ParameterName))
                    AuthorizeResult = CheckParameter(httpContext.Request);
                if (AuthorizeResult)
                    AuthorizeResult = AdminSiteService.CheckPermission(ModuleId.Value, (int)PermissionFlag.Value);
            }
        }
        return AuthorizeResult;
    }
    /// <summary>
    /// 錯誤處理
    /// </summary>
    protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
    {
        filterContext.Result = new RedirectResult("~/Main/Error?code=100");
    }
    /// <summary>
    /// 驗證參數值是否有正確
    /// </summary>
    private bool CheckParameter(HttpRequestBase request)
    {
        var val = request[ParameterName];
        bool b = false;
        if (val == null && ParameterValue == null)
            b = true;
        else if (val != null && ParameterValue != null && val == ParameterValue.ToString())
            b = true;
        return b;
    }
}
View Code

 

注意AuthorizeAttribute緩存問題。第一訪問時會緩存該Action的身份認證類,所以多模塊驗證時需要重新獲取moduleId

如果當前AuthorizeAttribute沒有指定moduleid,則每次訪問強制更新其moduleId

補充一下獲取控制器模塊的擴展方法:

/// <summary>
    /// 獲取控制器相關模塊
    /// </summary>
    public static List<ModuleAttribute> GetModules(this ControllerBase controller, bool useCache = true)
    {
        if (controller == null)
            return null;
        string cacheKey = string.Format("{0}_Modules", controller.GetType().Name);
        if (useCache)
        {
            if (CacheProvider.Cache.Contains<List<ModuleAttribute>>(cacheKey))
                return CacheProvider.Cache.Get<List<ModuleAttribute>>(cacheKey);
        }
        var moduleInfos = controller.GetType().GetCustomAttributes(typeof(ModuleAttribute), false);
        List<ModuleAttribute> modules = new List<ModuleAttribute>();
        if (moduleInfos.Length <= 0)
            return modules;
        foreach (var m in moduleInfos)
        {
            modules.Add((ModuleAttribute)m);
        }
        if (useCache)
            //緩存控制器模塊信息
            CacheProvider.Cache.Add<List<ModuleAttribute>>(cacheKey, modules, 20);
        return modules;
    }
View Code

 

 驗證方法主要是幫我們區分出是驗證哪一個模塊的哪一個權限,最后把模塊id和權限標識傳入我們的邏輯層進行驗證,我們可以在登陸的時候緩存用戶的模塊權限。

驗證大致代碼:

        /// <summary>
        /// 判斷當前登陸用戶對操作是否有權限
        /// </summary>
        public static bool CheckPermission(int ModuleId, int permissionFlag)
        {
            //FormsAuthentication.GetAuthCookie()
            var user = HttpContext.Current.User;
            //未登陸的用戶
            if (!user.Identity.IsAuthenticated)
                return false;
            AdminInfo info = GetLoginAdminInfo();
            //超級管理員有所有權限
            if (info.RoleId == Constant.AdministratorRoleId)
                return true;
            if (!info.ModulePermissions.Exists(t => t.AdminId == info.AdminId &&
                                                    t.ModuleId == ModuleId &&
                                                    t.PermissionFlag == permissionFlag))
                return false;
            return true;
        }
View Code

 

最后,我們就可以在我們的系統中使用了:

using FL.Entitys;
using FL.Site.Service;
using FL.Site.SysManager.Common;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using FL.Site.ViewModel;
using FL.Site.SysManager.JUI;

namespace FL.Site.SysManager.Controllers
{
    [Module(ModuleId = 7)]
    public class AdminController : BaseController<AdminSiteService>
    {
        /// <summary>
        /// 分頁列表
        /// </summary>
        [SysAuthorize(Permission.List)]
        public ActionResult List(PagerPostItem postPager)
        {
            var pager = new PagerItem(postPager, TargetType.NavTab);
            int recordCount;
            var list = Service.GetPageList(pager.currentPage, pager.numPerPage, out recordCount);
            pager.totalCount = recordCount;
            var roles = new RoleSiteService().GetRoles();
            ViewBag.Roles = roles;
            return View(list, pager);
        }
        /// <summary>
        /// 編輯輸入
        /// </summary>
        [SysAuthorize(Permission.Add, Permission.Update, "id")]
        public ActionResult Edit(int? id)
        {
            var entity = new AdminSaveModel();
            if (id != null)
                entity = Service.GetById(id.Value);
            var roles = new RoleSiteService().GetRoles();
            ViewBag.Roles = new SelectList(roles, "RoleId", "RoleName", entity.RoleId);
            return View(entity);
        }
        /// <summary>
        /// 保存數據
        /// </summary>
        [HttpPost]
        [SysAuthorize(Permission.Add, Permission.Update, "AdminId", 0)]
        public ActionResult Edit(AdminSaveModel entity)
        {
            entity.LastUpdateTime = DateTime.Now;
            entity.LastUpdateAdmin = UserInfo.LoginName;
            if (ModelState.IsValid)
                return Json(Service.Save(entity));
            else
                return Json(AjaxResult.NewModelCheckErrorResult(ModelState));
        }
        /// <summary>
        /// 刪除指定id的數據
        /// </summary>
        [SysAuthorize(Permission.Delete)]
        public ActionResult Delete(int id,int roleId)
        {
            return Json(Service.Delete(id, roleId), false);
        }
        /// <summary>
        /// 修改密碼
        /// </summary>
        [SysAuthorize(Permission.Update)]
        public ActionResult EditPwd(int id)
        {
            ViewBag.Id = id;
            return View();
        }
        [HttpPost]
        [SysAuthorize(Permission.Update)]
        public ActionResult EditPwd(string Pwd, string ConfirmPwd, int id)
        {
            return Json(Service.UpdatePassword(Pwd, ConfirmPwd, id));
        }
        /// <summary>
        /// 獲取用戶登陸日志
        /// </summary>
        [SysAuthorize(22, Permission.List)]
        public ActionResult LoginLog(PagerPostItem postPager)
        {
            var pager = new PagerItem(postPager, TargetType.NavTab);
            int recordCount;
            var list = Service.GetLoginLogPageList(pager.currentPage, pager.numPerPage, out recordCount);
            pager.totalCount = recordCount;
            return View(list, pager);
        }
    }
}
View Code

 

很久沒寫文章,有點小亂,也沒提供代碼及數據庫相關的東西,也算是分享一種相法吧。程序一個人寫久了很寂寞

  數據庫大致結構:模塊表,角色表,用戶表,模塊權限表,角色模塊權限表,用戶角色表,  另外寫一個視圖獲取用戶的模塊權限,在登陸的時候緩存,就可以使用上面的方式驗證了。

總結:在基於模塊的權限驗證系統中,只需要為程序提供模塊,登陸用戶及要驗證的權限,就可以非常方便的驗證用戶權限。上面的一系列代碼都是為了取得我們想要的模塊id和權限

缺點:模塊id和權限代碼不能隨便更改,當然,可以用一個常量類來保存我們的模塊和權限,但是總的上來說,還可以將就使用的。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM