asp.net core权限模块的快速构建
大部分系统都会有权限模块,别人家系统的权限怎么生成的我不知道,我只知道这样做是可以并且挺好的。
文章中只对asp.net core的部分代码进行说明 呃 记录~,mvc版本自行前往仓库查阅
代码中的一些特性标记后面列出,或前往仓库查看~
1.根据特性标记生成模块权限
先上效果图,感兴趣的前往Demo仓库地址,不感兴趣的关闭页面吧~
模型定义
Demo中菜单分为三级,首先使用枚举定义模块,FirstModuleMenu为一级菜单,SecondModuleMenu为二级菜单,三级菜单在action方法上由
PermissionDescription标识并IsMenu=true的方法
若是页面功能则为IsMenu=false
可使用的特性标记还包含以下几种,并且权限验证时依次递增:
- 免登录:
AllowAnonymous - 管理员默认权限:
NonePermissionAttribute - 指定权限:
PermissionDescriptionAttribute - 依赖权限(包含有这些的任一权限都将获得授权):
ParentPermissionAttribute
//一级菜单
public class FirstModuleMenu
{
public const string 系统管理 = "系统|icon-setting";
public const string 用户管理 = "用户|icon-user";
}
//二级菜单
public enum SecondModuleMenu
{
[Description(FirstModuleMenu.系统管理)]
系统设置,
[Description(FirstModuleMenu.用户管理)]
用户管理,
}
//三级菜单
[PermissionDescription(SecondModuleMenu.系统设置, "站点设置", true)]
public ActionResult SiteSetting()
{
return Content("站点设置 System/SiteSetting");
}
生成权限模型集合
定义权限模型 SysModule.cs
调用初始化权限方法
private static List<SysModule> _AllAdminModule { get; set; } = new List<SysModule>();
/// <summary>
/// 初始化权限
/// </summary>
public static void InitPermission()
{
var result = new List<SysModule>();
#region 通过反射读取Action方法写入对应权限至集合
//读取CoreDemo程序集中集成自AdminController的控制器
var types = Assembly.Load("CoreDemo").GetTypes().Where(e => e.BaseType.Name == nameof(AdminController));
var area = "";//默认未使用区域
var now = DateTime.Now;
var i = 1;
foreach (var type in types)
{
//获取所有action方法
var members = type.GetMethods().Where(e => e.ReturnType.Name == nameof(ActionResult) || e.ReturnType.Name == nameof(IActionResult));
foreach (var member in members)
{
//获取功能列表
var attrs = member.GetCustomAttributes(typeof(PermissionDescriptionAttribute), true);
if (attrs.Length == 0)
continue;
//功能对应的二级菜单
var parentMenuEnum = (attrs[0] as PermissionDescriptionAttribute).ParentMenu;
var parentMenuName = parentMenuEnum.ToString();
//功能对应的一级菜单 名称|icon类名
var enumArry = parentMenuEnum.GetEnumDescription().Split('|');
var mainMenuName = enumArry[0];
var existMainMenu = result.Where(e => e.ModuleName == mainMenuName).FirstOrDefault();
if (existMainMenu == null)
{
var mainMenu = new SysModule()
{
Id = i,
ParentId = 0,
ModuleName = mainMenuName,
Icon = enumArry[1] ?? "",
Area = area,
Controller=string.Empty,
Action=string.Empty,
IsMenu = true,
IsVisible = true,
Remark = string.Empty,
DisplayOrder = i,
CreateTime = now
};
i++;
existMainMenu = mainMenu;
existMainMenu.Children = new List<SysModule>();
//添加一级菜单
result.Add(existMainMenu);
}
var existParentMenu = existMainMenu.Children.Where(e => e.ModuleName == parentMenuName).FirstOrDefault();
if (existParentMenu == null)
{
var parentMenu = new SysModule()
{
Id = i,
ParentId = existMainMenu.Id,
ModuleName = parentMenuName,
Icon=string.Empty,
Area = area,
Controller = string.Empty,
Action = string.Empty,
IsMenu = enumArry.Length != 3 || bool.Parse(enumArry[2]),
IsVisible = true,
DisplayOrder = i,
CreateTime = now,
Children = new List<SysModule>()
};
i++;
existParentMenu = parentMenu;
existParentMenu.Children = new List<SysModule>();
existMainMenu.Children.Add(existParentMenu);
//添加二级菜单
result.Add(existParentMenu);
}
var menu = new SysModule()
{
Id = i,
ParentId = existParentMenu.Id,
Area = area,
Action = member.Name,
DisplayOrder = i,
CreateTime = now,
Controller = member.DeclaringType.Name.Substring(0, member.DeclaringType.Name.Length - 10),
IsMenu = (attrs[0] as PermissionDescriptionAttribute).IsMenu,
Children = new List<SysModule>(),
};
if (menu.IsMenu)
menu.IsVisible = true;
menu.ModuleName = (attrs[0] as PermissionDescriptionAttribute).FuncName;
i++;
existParentMenu.Children.Add(menu);
result.Add(menu);
}
}
#endregion
//todo 添加到数据库
_AllAdminModule = result;
}
2.使用过滤器拦截请求进行验证
新建特性标记
AdminAuthorizeAttribute继承Attribute类以及实现IAuthorizationFilter接口的OnAuthorization方法
不多说,上图

不多说,上代码↓_↓
权限验证过滤器:AdminAuthorizeAttribute
//后台权限验证
public class AdminAuthorizeAttribute : Attribute,IAuthorizationFilter
{
public void OnAuthorization(AuthorizationFilterContext filterContext)
{
//匿名标识 无需验证
if (filterContext.Filters.Any(e => (e as AllowAnonymous) != null))
return;
var adminInfo = GlobalContext.AdminInfo;//此处应为获取的登录用户
if (adminInfo == null)
{
if(filterContext.HttpContext.Request.Headers["X-Requested-With"] == "XMLHttpRequest")
{
filterContext.Result = new JsonResult("未登录");
}
else
{
filterContext.Result =new ContentResult() { Content = "未登录" };
}
return;
}
//对应action方法或者Controller上若存在NonePermissionAttribute标识,即表示为管理员的默认权限,只要登录就有权限
var isNone = filterContext.Filters.Any(e => (e as NonePermissionAttribute) != null);
if (isNone)
return;
//获取请求的区域,控制器,action名称
var area = filterContext.RouteData.DataTokens["area"]?.ToString();
var controller = filterContext.RouteData.Values["controller"]?.ToString();
var action = filterContext.RouteData.Values["action"]?.ToString();
var isPermit = false;
//校验权限
isPermit = ServiceFactory.CheckAdminPermit(adminInfo.Id, area, controller, action);
if (isPermit)
return;
//此action方法的父辈权限判断,只要有此action对应的父辈权限,皆有权限访问
var pAttrs = filterContext.Filters.Where(e => (e as ParentPermissionAttribute) != null).ToList();
if (pAttrs.Count > 0)
{
foreach (ParentPermissionAttribute pattr in pAttrs)
{
if (!string.IsNullOrEmpty(pattr.Area))
area = pattr.Area;
isPermit = ServiceFactory.CheckAdminPermit(adminInfo.Id, area, pattr.Controller, pattr.Action);
if (isPermit)
return;
}
}
if (!isPermit)
{
filterContext.Result = new ContentResult() { Content = "无权限访问" };
return;
}
}
}
自定义特性标记,用于权限校验
此处的自定义的特性标记不能继承Attribute,因无法在AdminAuthorizeAttribute中的上下文filterContext.Filters中获取到特性标记(不知道咋取特性标记,所以用这种方式代替,也更为简单 冏)
!!!!!!!!!修改: 之前脑袋没有转过弯来,要使过滤器上下文的Filters中发现自定义过滤器需要继承 Attribute, IFilterMetadata
/// <summary>
/// 管理员的默认权限
/// </summary>
public class NonePermissionAttribute : Attribute, IFilterMetadata{}
/// <summary>
/// 匿名验证
/// </summary>
public class AllowAnonymous : Attribute, IFilterMetadata{}
/// <summary>
/// 长辈权限
/// </summary>
[AttributeUsage(AttributeTargets.Method, AllowMultiple = true, Inherited = true)]
public class ParentPermissionAttribute : Attribute, IFilterMetadata
{
/// <summary>
/// 区域
/// </summary>
public string Area { get; set; }
/// <summary>
/// 控制器
/// </summary>
public string Controller { get; set; }
/// <summary>
/// Action名称
/// </summary>
public string Action { get; set; }
public ParentPermissionAttribute(string area, string controller, string action)
{
this.Area = area;
this.Controller = controller;
this.Action = action;
}
public ParentPermissionAttribute(string controller, string action)
{
this.Controller = controller;
this.Action = action;
}
}
若将代码全部贴出,有点略显多余,故,只贴出了部分核心代码.其他一些模型,扩展 请直奔仓库地址...
或使用git命令克隆MvcPermission分支到MvcPermission文件夹:git clone https://git.coding.net/yimocoding/WeDemo.git -b MvcPermission
补充
- 2017-09-29
突然灵光一现,将文中的ResultFilterAttribute特性标记替换为Attribute, IFilterMetadata,果然可以~
故得出:实现了IFilterMetadata的特性标记能够在过滤器的上下文中获取到。
asp.net core权限模块的快速构建的更多相关文章
- 在ASP.NET Core中使用Apworks快速开发数据服务
不少关注我博客的朋友都知道我在2009年左右开发过一个名为Apworks的企业级应用程序开发框架,旨在为分布式企业系统软件开发提供面向领域驱动(DDD)的框架级别的解决方案,并对多种系统架构风格提供支 ...
- CZGL.Auth: ASP.NET Core Jwt角色授权快速配置库
CZGL.Auth CZGL.Auth 是一个基于 Jwt 实现的快速角色授权库,ASP.Net Core 的 Identity 默认的授权是 Cookie.而 Jwt 授权只提供了基础实现和接口,需 ...
- asp.net core系列 76 Apollo 快速安装模式下填坑和ASP.NetCore结合使用
前言:由于公司占时没有运维,出于微服务的需要,Apollo只能先装在windows 阿里云上跑起来,由于环境及网络等问题,在安装过程中遇到很多坑,算是一个个坑填完后,最终实现. 一. java jdk ...
- 麻雀虽小,五脏俱全。基于Asp.net core + Sqlite 5分钟快速上手一个小项目
虽然该方法不会用在实际开发中,但该过程对于初学者还是非常友好的,真应了麻雀虽小,五脏俱全这句话了.好了不多废话了,直接开始!! 1.建立一个名为test的Asp.net core web应用程序 这一 ...
- ASP.NET Core 3.0 实战:构建多版本 API 接口
第一次在博客写分享,请多多捧场,如有歧义请多多包含! 因为业务需求发展需要,所以API接口的变更升级是必不可少的事情,而原有的接口是不可能马上停止使用的.例如:Login接口为例,1.0版本之返回用户 ...
- 使用 Asp.net core 2.0 + Angular 4 构建车辆管理的Web应用程序
https://www.codeproject.com/Articles/1210559/Asp-net-core-Angular-Build-from-scratch-a-web
- c#、ASP.NET core 基础模块之一:linq(原创)
最近做数据查询,发现linq 真的比我 印象中 要强大的多,实用的多,所以 我决定 要与linq 来一场 深入交流, 因为linq的基础用法 可以百度一大摞,我就记录点不一样的,结合我做项目使 ...
- .NET Core实战项目之CMS 第二章 入门篇-快速入门ASP.NET Core看这篇就够了
作者:依乐祝 原文链接:https://www.cnblogs.com/yilezhu/p/9985451.html 本来这篇只是想简单介绍下ASP.NET Core MVC项目的(毕竟要照顾到很多新 ...
- ASP.NET Core WebApi构建API接口服务实战演练
一.ASP.NET Core WebApi课程介绍 人生苦短,我用.NET Core!提到Api接口,一般会想到以前用到的WebService和WCF服务,这三个技术都是用来创建服务接口,只不过Web ...
随机推荐
- Google研究人员宣布完成全球首例SHA-1哈希碰撞!
2004年的国际密码讨论年会(CRYPTO)尾声,我国密码学家王小云及其研究同事展示了MD5.SHA-0及其他相关杂凑函数的杂凑碰撞并给出了实例.时隔13年之后,来自Google的研究人员宣布完成第一 ...
- nohup介绍
背景 我们通常使用&将前台任务变为后台任务执行,但是如果只是使用&,那么在突然断网或者关闭启动该任务的终端(ps:可使用putty来测试,部分软件如mobaxterm做了优化,关闭终端 ...
- Cookie常用操作以及属性
概述 最近项目要用到cookie存储部分用户信息;研究了一下做一下分享 Cookie 是服务器保存在浏览器的一小段文本信息,每个 Cookie 的大小一般不能超过4KB.浏览器每次向服务器发出请求,就 ...
- 10分钟学会ES7+ES8
撰文为何 身为一个前端开发者,ECMAScript(以下简称ES)早已广泛应用在我们的工作当中.了解ECMA机构流程的人应该知道,标准委员会会在每年的6月份正式发布一次规范的修订,而这次的发布也将作为 ...
- java中的jdk切换(无需卸载原有jdk)
该转自 : http://blog.csdn.net/u010011371/article/details/50749954 很好的一片文章,适合我这种小白,方便以后使用. 之前一直使用的是JDK1 ...
- ABP从入门到精通(6):快速重命名解决方案
SolutionRenamer SolutionRenamer 是一个解决方案快速重命名工具.经测试重命名一个全新asp.net zero core项目(ABP asp.net zero,.net c ...
- fixed定位兼容性
不过从ios5.1以来,fixed定位就已经支持了,但很遗憾,ios现在对它还只是半支持. 但是在某些情况下,会出现一些比较奇葩的问题,比如fixed元素中存在输入框子元素,这个时候就会跪了. 可以看 ...
- 关于破解Quartus
在网上找了很多资料,说的也很详细,安装的Quartus13.0,在破解的时候遇到x64和x86两种破解器,两个针对的路径不一样,如果搞混了~可能就会出现这种情况 Error: Current li ...
- 排序--SelectionSort 选择排序
选择排序 no implementation 选择排序(Selection sort)是一种简单直观的排序算法.它的工作原理是每一次从待排序的元素中中选出最小(或最大)的一个元素,存放在序列的起始位置 ...
- 聊一聊我们都熟知的 “ Java分层 ”
一.为什么要分层. 以前的我们,写代码的时候,都在main()方法中,出现了错误,就慢慢调试,这样浪费了我们很长的时间,而我们程序员的时间是非常宝贵的 但是当我们使用分层架构的时候,就可以清晰明确的知 ...
