zoukankan      html  css  js  c++  java
  • 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和权限代码不能随便更改,当然,可以用一个常量类来保存我们的模块和权限,但是总的上来说,还可以将就使用的。

  • 相关阅读:
    gerapy+scrapyd组合管理分布式爬虫
    selenium之chromedriver与谷歌浏览器映射,到谷歌71.0版本的
    windows10下成功安装docker splash及遇到问题的解决方案
    python安装win32api pywin32 后出现 ImportError: DLL load failed
    git客户端安装
    linux相关命令
    springboot集成swagger2
    git远程分支创建,本地分支关联远程分支,第一次发布、更新分支
    spring事件监听机制
    JavaScript页面被卷去的头部兼容性解决方案
  • 原文地址:https://www.cnblogs.com/crazyboy/p/3425631.html
Copyright © 2011-2022 走看看