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;
    }
}

实现了模块特性以后,就是比较重要的验证特性了:

/// <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;
    }
}

注意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;
    }

验证方法主要是帮我们区分出是验证哪一个模块的哪一个权限,最后把模块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;
        }

最后,我们就可以在我们的系统中使用了:

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);
        }
    }
}

很久没写文章,有点小乱,也没提供代码及数据库相关的东西,也算是分享一种相法吧。程序一个人写久了很寂寞

数据库大致结构:模块表,角色表,用户表,模块权限表,角色模块权限表,用户角色表,  另外写一个视图获取用户的模块权限,在登陆的时候缓存,就可以使用上面的方式验证了。

总结:在基于模块的权限验证系统中,只需要为程序提供模块,登陆用户及要验证的权限,就可以非常方便的验证用户权限。上面的一系列代码都是为了取得我们想要的模块id和权限

缺点:模块id和权限代码不能随便更改,当然,可以用一个常量类来保存我们的模块和权限,但是总的上来说,还可以将就使用的。

时间: 2024-08-11 03:28:01

Asp.net Mvc4 基于Authorize实现的模块访问权限的相关文章

从零开始实现asp.net MVC4框架网站的用户登录以及权限验证模块 详细教程

用户登录与权限验证是网站不可缺少的一部分功能,asp.net MVC4框架内置了用于实现该功能的类库,只需要简单搭建即可完成该功能. 下面详细介绍该功能的完成方法,尾部有实例源码下载,希望可以给刚开始接触MVC的朋友做个参考.     第一步:给VS安装MVC4框架 VS2012自带MVC4框架,其他版本可以使用独立安装包进行安装,这里就不讨论了,本例使用VS2013创建,.NET4.0+MVC4 第二步:创建MVC4网站项目         选择文件-新建-项目,按下图示例创建一个空的MVC网

ASP.NET MVC4.0+EF+LINQ+EasyUI+网站+角色权限管理系统(1)

本系列的的角色权限管理主要采用Dotnet MVC4工程内置的权限管理模块Simplemembership实现,主要有关文件是InitializeSimpleMembershipAttribute.cs和AccountModels.cs 下面是对这两个文件的了解和改造 WebSecurity.InitializeDatabaseConnection("DefaultConnection", "UserProfile", "UserId", &qu

ASP.NET MVC4.0+EF+LINQ+bui+网站+角色权限管理系统(7)

今天将使用Simplemembership进行权限控制 我们使用mvc的AuthorizeAttribute来实现对Controller and Action权限控制 看如下标为红色的代码片段: /// <summary> /// 删除数据操作 /// </summary> /// <param name="id"></param> /// <returns></returns> [MVCSystemAuthori

ASP.NET MVC4快速开发框架权限模块开发要点

另外献上在<线体验Demo地址>希望大家也能从中得到一些启发.地址:http://121.40.148.178:8080/ . 用户名:guest,密码:123456 一.前言 权限管理是开发框架中很重要的一个模块,因为每套管理系统都会涉及到权限管理,如果我们的开发框架中的权限模块做得很好,很灵活,那么就要可以在所有的项目中通用从而节省大量的时间降低开发成本. 这篇博文会详细地介绍权限开发的重点. 二.权限的模块管理 一套系统会由一个个的功能模块构成,那么权限管理起码要能管到每个功能的准入权限

windows server 2012 r2 iis8.5 部署asp.net mvc4/5程序小结

windows server 2012 r2 iis8.5 部署asp.net mvc4/5程序小结 原文链接:http://www.xuanhun521.com/Blog/66d491f8-b479-437e-90a2-c24a898e44ce 基本配置 打开服务器管理器,点击添加角色和功能. 选择基于角色或基于功能的安装. 选择服务器. 选择webserver(iis) 选择角色. 打开web服务器(IIS)选项.选择常见http功能. 安全性选择中勾选常用的安全性选项. 应用程序开发选项中

ASP.NET MVC4入门到精通系列目录汇总(转)

序言 最近公司在招.NET程序员,我发现好多来公司面试的.NET程序员居然都没有 ASP.NET MVC项目经验,其中包括一些工作4.5年了,甚至8年10年的,许多人给我的感觉是:工作了4.5年,Web开发依旧停留在拖控件的水平,最最基本的算 法,递归.排序(我不要求快速排序,你会冒泡就行了)都不会,数据库方面,很基础的SQL都写不出,分组过滤也不会,更别提性能了,问下数据优化经验,除 了回答加索引基本就没下文了.当然,一些过去N年都是做ASP.NET WebForm开发的,不熟悉MVC,那也没

asp.net mvc4 学习1

1 简介:微软在很早就看到了基于windows系统的web开发平台的需求,这时便开始提出自己的解决方案即微软的第一个基于web开发的平台ASP.再后来随着需求和性能的要求再2002年推出第二个解决方案ASP.NET来满足.但是技术不断地在改进很快又有了新的需求,不再基于页面在2008年推出了asp.net mvc的开发模式(模型-视图-控制器),看来五六年技术会得到一次飞跃,当然这只是猜测而已. mvc模式把应用程序分成三层,而且这三层之间的关系是松耦合即相互隔离,互不影响,而这种情况确实最好的

构建ASP.NET MVC4+EF5+EasyUI+Unity2.x注入的后台管理系统(35)-文章发布系统②-构建项目

注:阅读本文,需要阅读本系列的之前文章 代码生成器下载地址(文章开头处) 接下来我们建立数据库的表和各层的代码 我们只需要两张表,文章列表(MIS_Article)和类别表(MIS_Article_Category) USE [AppDB] GO /****** Object: Table [dbo].[MIS_Article] Script Date: 05/15/2014 17:33:15 ******/ SET ANSI_NULLS ON GO SET QUOTED_IDENTIFIER

ASP.Net MVC4+Memcached+CodeFirst实现分布式缓存

ASP.Net MVC4+Memcached+CodeFirst实现分布式缓存 part 1:给我点时间,允许我感慨一下2016年 正好有时间,总结一下最近使用的一些技术,也算是为2016年画上一个完美的句号,回顾2016年,感受颇多,感恩那些帮助我的人.展望2017年,我相信一定会遇到一个更好的自己.附上自己喜欢的一张图片: 好了~~~装逼结束,下面开始说说如何实现分布式缓存在项目中的应用. part2:先分析以下需求 软件架构从单机到分布式遇到的问题(当然这是一个很深的问题,由于能力有限今天