一. 权限场景分析:

1. 系统具有角色概念, 部门概念, 且都具有相应不同的权限

2. 用户具有多个角色, 多个部门等关系, 并且能给单个用户指派独有的权限

3. 具有细粒度权限控制到资源的RBAC, 能控制页面, 控制菜单, 控制逻辑, 控制单个操作, 控制到单一数据; 且具有一定的可扩展性

4. 适用于webapi/ mvc / wcf / webservice  混合项目中

5. 设置权限和验证权限易用性高

二. 数据库表设计

  1. 角色表

  2. 部门表

  3. 用户表

  4. 菜单表

  5. 用户表

  6. 权限表

  7. 用户角色关系表

  8. 用户部门关系表

  9. 菜单权限关系表

  10.用户权限关系表

  11.部门权限关系表

  12. 权限汇总表

在一般正常的情况下, 大家伙都会有 菜单权限关系, 用户权限关系, 部门权限关系等表, 在此处省去如上几项权限关系表,  将如上权限表做成dll 当插件形式容入到项目中.

权限汇总表描述:

  既然将如上所有跟权限有关联的关系表剔除后该如何来设计各种来自不同体系的权限呢? 因此在此权限汇总表中要有字段来定位到, 该条权限是属于什么体系.

  因此权限汇总表中出现三个至关重要的字段:

    1. PermissionType(权限的类型: 菜单,部门,角色,用户 );

    2. PermissionReferenceId(权限类型的引用ID: 当Type为角色时 为角色的ID 以此类推);

    3. PermissionAction(权限操作的ID: 每个权限将会划分为一个操作编号ID, 往后会深入讲解)

 -- 权限汇总表
CREATE TABLE `Permission` (
`Id` int(11) NOT NULL AUTO_INCREMENT,
`PermissionReferenceId` int(11) DEFAULT NULL COMMENT '权限引用ID(对应 角色ID, 用户ID, 部门ID)',
`PermissionAction` int(11) DEFAULT NULL COMMENT '权限操作ID(每种资源的操作ID)',
`PermissionType` int(11) DEFAULT NULL COMMENT '权限类型()',
`CreateTime` datetime DEFAULT '1990-01-01 00:00:00' COMMENT '创建时间',
`UpdateTime` datetime DEFAULT '1990-01-01 00:00:00' COMMENT '修改时间',
`IsEnable` bit(1) DEFAULT b'' COMMENT '是否可用',
PRIMARY KEY (`Id`)
) ENGINE=InnoDB AUTO_INCREMENT=390 DEFAULT CHARSET=utf8 COMMENT='权限汇总表';

权限汇总表SQL代码

 -- 部门表
CREATE TABLE `AdminMent` (
`Id` int(11) NOT NULL AUTO_INCREMENT,
`DepartName` varchar(20) DEFAULT NULL COMMENT '部门名',
`Description` varchar(20) DEFAULT NULL COMMENT '部门说明',
`Icon` varchar(20) DEFAULT NULL COMMENT '图标',
`CreateTime` datetime DEFAULT NULL COMMENT '创建时间',
`UpdateTime` datetime DEFAULT NULL COMMENT '修改时间',
`IsEnable` bit(1) DEFAULT b'' COMMENT '是否可用',
PRIMARY KEY (`Id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8 COMMENT='部门表'; -- 部门用户关系表
CREATE TABLE `AdminMentAdminUsers` (
`AdminMent_Id` int(11) NOT NULL,
`AdminUser_Id` int(11) NOT NULL,
PRIMARY KEY (`AdminMent_Id`,`AdminUser_Id`),
KEY `AdminMent_UserCollection_Target` (`AdminUser_Id`)
) ENGINE=MyISAM DEFAULT CHARSET=gbk; -- 角色表
CREATE TABLE `AdminRole` (
`Id` int(11) NOT NULL AUTO_INCREMENT,
`RoleName` varchar(20) DEFAULT NULL COMMENT '角色名',
`Description` varchar(20) DEFAULT NULL COMMENT '角色说明',
`CreateTime` datetime DEFAULT NULL COMMENT '创建时间',
`UpdateTime` datetime DEFAULT NULL COMMENT '修改时间',
`IsEnable` bit(1) DEFAULT b'' COMMENT '是否可用',
PRIMARY KEY (`Id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8 COMMENT='后台角色表'; -- 角色用户关系表
CREATE TABLE `AdminRoleAdminUsers` (
`AdminRole_Id` int(11) NOT NULL,
`AdminUser_Id` int(11) NOT NULL,
PRIMARY KEY (`AdminRole_Id`,`AdminUser_Id`),
KEY `AdminRole_UserCollection_Target` (`AdminUser_Id`)
) ENGINE=MyISAM DEFAULT CHARSET=gbk; -- 用户表
CREATE TABLE `AdminUser` (
`Id` int(11) NOT NULL AUTO_INCREMENT,
`UserName` varchar(20) DEFAULT NULL COMMENT '后台用户名',
`Password` varchar(50) DEFAULT NULL COMMENT '后台用户密码',
`Mail` varchar(50) DEFAULT NULL COMMENT '用户邮箱',
`Phone` varchar(20) DEFAULT NULL COMMENT '用户手机',
`Description` varchar(20) DEFAULT NULL COMMENT '附加说明',
`CreateTime` datetime DEFAULT NULL COMMENT '创建时间',
`UpdateTime` datetime DEFAULT NULL COMMENT '修改时间',
`IsEnable` bit(1) DEFAULT b'' COMMENT '是否可用',
PRIMARY KEY (`Id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8 COMMENT='后台用户表'; -- 菜单表
CREATE TABLE `AdminMenu` (
`Id` int(11) NOT NULL AUTO_INCREMENT,
`ParentsID` int(11) DEFAULT NULL COMMENT '父菜单ID',
`MenuName` varchar(20) DEFAULT NULL COMMENT '菜单名称',
`Descriptotion` varchar(20) DEFAULT NULL COMMENT '菜单说明',
`ControllerPermissionName` varchar(20) DEFAULT NULL COMMENT '控制器名',
`ActionPermissionName` varchar(20) DEFAULT NULL COMMENT '操作器名',
`Sort` int(11) DEFAULT NULL COMMENT '排序',
`Icon` varchar(20) DEFAULT NULL COMMENT '图标',
`CreateTime` datetime DEFAULT NULL COMMENT '创建时间',
`UpdateTime` datetime DEFAULT NULL COMMENT '修改时间',
`IsEnable` bit(1) DEFAULT b'' COMMENT '是否可用',
PRIMARY KEY (`Id`)
) ENGINE=InnoDB AUTO_INCREMENT=32 DEFAULT CHARSET=utf8 COMMENT='后台管理菜单表';

其它数据表SQL代码

三. 权限无处不在

若要让设置权限和验证权限易用性高, 最好的解决方案则是利用 Attribute 来进行权限的设置.

如图:

     /// <summary>
/// 附加权限
/// </summary>
[AttributeUsage(validOn: AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = true, Inherited = true)]
public class PermissionAttribute : Attribute
{
/// <summary>
/// 权限类型
/// </summary>
public PermissionKinds Kind { get; set; } = PermissionKinds.AllowAnonymous;
/// <summary>
/// 操作权限
/// </summary>
public PermissionActions Action { get; set; } = PermissionActions.AllowAnonymous;
}

PermissionAttribute

将PermissionKinds 划分为 菜单 和 数据

     /// <summary>
/// 用于标识api接口和mvc访问权限验证类型
/// </summary>
public enum PermissionKinds : int
{
/// <summary>
/// 匿名
/// </summary>
AllowAnonymous = ,
/// <summary>
/// 菜单
/// </summary>
MENU = ,
/// <summary>
/// 资源操作
/// </summary>
DATASOURCE =
}

PermissionKinds

PermissionActions 则是 标识操作资源的权限编号

例:  用户表: User_Read = 10, User_Edit=11, User_Add=12, User_Delete=13  (当然此处也可以进行扩展如: 搜索用户 User_Search=14)

     /// <summary>
/// 用于标识操作资源的权限动作类型
/// </summary>
[JsonConverter(typeof(int))]
public enum PermissionActions : int
{
/// <summary>
/// 匿名
/// </summary>
AllowAnonymous = , #region 表:ActionPermission(控制器操作权限表) 权限
/// <summary>
/// 控制器操作权限表读取权限
/// </summary>
[PermissionActionDescription("控制器操作权限表", "读取")]
ActionPermission_Read = ,
/// <summary>
/// 控制器操作权限表修改权限
/// </summary>
[PermissionActionDescription("控制器操作权限表", "修改")]
ActionPermission_Edit = ,
/// <summary>
/// 控制器操作权限表添加权限
/// </summary>
[PermissionActionDescription("控制器操作权限表", "添加")]
ActionPermission_Add = ,
/// <summary>
/// 控制器操作权限表删除权限
/// </summary>
[PermissionActionDescription("控制器操作权限表", "删除")]
ActionPermission_Delete = ,
#endregion #region 表:AdminMent(部门表) 权限
/// <summary>
/// 部门表读取权限
/// </summary>
[PermissionActionDescription("部门表", "读取")]
AdminMent_Read = ,
/// <summary>
/// 部门表修改权限
/// </summary>
[PermissionActionDescription("部门表", "修改")]
AdminMent_Edit = ,
/// <summary>
/// 部门表添加权限
/// </summary>
[PermissionActionDescription("部门表", "添加")]
AdminMent_Add = ,
/// <summary>
/// 部门表删除权限
/// </summary>
[PermissionActionDescription("部门表", "删除")]
AdminMent_Delete = ,
#endregion #region 表:AdminMenu(后台管理菜单表) 权限
/// <summary>
/// 后台管理菜单表读取权限
/// </summary>
[PermissionActionDescription("后台管理菜单表", "读取")]
AdminMenu_Read = ,
/// <summary>
/// 后台管理菜单表修改权限
/// </summary>
[PermissionActionDescription("后台管理菜单表", "修改")]
AdminMenu_Edit = ,
/// <summary>
/// 后台管理菜单表添加权限
/// </summary>
[PermissionActionDescription("后台管理菜单表", "添加")]
AdminMenu_Add = ,
/// <summary>
/// 后台管理菜单表删除权限
/// </summary>
[PermissionActionDescription("后台管理菜单表", "删除")]
AdminMenu_Delete = ,
#endregion #region 表:AdminRole(后台角色表) 权限
/// <summary>
/// 后台角色表读取权限
/// </summary>
[PermissionActionDescription("后台角色表", "读取")]
AdminRole_Read = ,
/// <summary>
/// 后台角色表修改权限
/// </summary>
[PermissionActionDescription("后台角色表", "修改")]
AdminRole_Edit = ,
/// <summary>
/// 后台角色表添加权限
/// </summary>
[PermissionActionDescription("后台角色表", "添加")]
AdminRole_Add = ,
/// <summary>
/// 后台角色表删除权限
/// </summary>
[PermissionActionDescription("后台角色表", "删除")]
AdminRole_Delete = ,
#endregion #region 表:AdminUser(后台用户表) 权限
/// <summary>
/// 后台用户表读取权限
/// </summary>
[PermissionActionDescription("后台用户表", "读取")]
AdminUser_Read = ,
/// <summary>
/// 后台用户表修改权限
/// </summary>
[PermissionActionDescription("后台用户表", "修改")]
AdminUser_Edit = ,
/// <summary>
/// 后台用户表添加权限
/// </summary>
[PermissionActionDescription("后台用户表", "添加")]
AdminUser_Add = ,
/// <summary>
/// 后台用户表删除权限
/// </summary>
[PermissionActionDescription("后台用户表", "删除")]
AdminUser_Delete = ,
#endregion #region 表:AdminUserRoles(后台用户角色表) 权限
/// <summary>
/// 后台用户角色表读取权限
/// </summary>
[PermissionActionDescription("后台用户角色表", "读取")]
AdminUserRoles_Read = ,
/// <summary>
/// 后台用户角色表修改权限
/// </summary>
[PermissionActionDescription("后台用户角色表", "修改")]
AdminUserRoles_Edit = ,
/// <summary>
/// 后台用户角色表添加权限
/// </summary>
[PermissionActionDescription("后台用户角色表", "添加")]
AdminUserRoles_Add = ,
/// <summary>
/// 后台用户角色表删除权限
/// </summary>
[PermissionActionDescription("后台用户角色表", "删除")]
AdminUserRoles_Delete = ,
#endregion
}

PermissionActions

至此,我们只需要要将要 进行权限审核验证的方法上进行 permissionattribute 标记即可

附上相应的codesmith 代码生成

 <%@ CodeTemplate Language="C#" TargetLanguage="C#" Src="" Inherits="" Debug="False" Description="Template description here." ResponseEncoding="UTF-8" %>
<%@ Property Name="SourceDatabase" Type="SchemaExplorer.DatabaseSchema" Default="" Description="源数据库" %>
<%@ Assembly Name="SchemaExplorer" %>
<%@ Assembly Name="System.Data" %>
<%@ Assembly Name="mscorlib" %>
<%@ Import Namespace="SchemaExplorer" %>
<%@ Import Namespace="System.Data" %>
<%@ Import Namespace="System.Collections.Generic" %> <%@ Assembly Src="..\CommonUtility.cs" %>
<%@ Import Namespace="Common" %> public enum PermissionActions : int
{
/// <summary>
/// 匿名
/// </summary>
AllowAnonymous = , <%
int index = ;
foreach(var table in SourceDatabase.Tables){
var tableName = table.GetClassName();
var tableDescription = table.Description;
index +=;
var count = index;
%>
#region 表:<%=tableName %>(<%=tableDescription%>) 权限
/// <summary>
/// <%=tableDescription%>读取权限
/// </summary>
[PermissionActionDescription("<%=tableDescription%>","读取")]
<%=tableName %>_Read = <%=count++%>,
/// <summary>
/// <%=tableDescription%>修改权限
/// </summary>
[PermissionActionDescription("<%=tableDescription%>","修改")]
<%=tableName %>_Edit = <%=count++%>,
/// <summary>
/// <%=tableDescription%>添加权限
/// </summary>
[PermissionActionDescription("<%=tableDescription%>","添加")]
<%=tableName %>_Add = <%=count++%>,
/// <summary>
/// <%=tableDescription%>删除权限
/// </summary>
[PermissionActionDescription("<%=tableDescription%>","删除")]
<%=tableName %>_Delete = <%=count++%>,
#endregion <%
}
%>
}

Codesmith权限数据库表生成权限Enum(PermissionActions)

四. 权限认证

1. 在用户登录时,将用户所在的所有角色组权限, 和部门权限全部查询出来合并,并添加到缓存中.

2. 当用户在访问指定的资源或action方法前 进行验证其是否具有相应权限

实施方案:

1.webapi

  新建 Filter 实现 IAuthorizationFilter 接口  

 public Task<HttpResponseMessage> ExecuteAuthorizationFilterAsync(HttpActionContext actionContext, CancellationToken cancellationToken, Func<Task<HttpResponseMessage>> continuation)
{
var permissions_on_action = actionContext.ActionDescriptor.GetCustomAttributes<PermissionAttribute>().ToList(); bool isAnonymous = actionContext.ActionDescriptor.GetCustomAttributes<AllowAnonymousAttribute>().Count() >
|| permissions_on_action.Any(a => a.Action == PermissionActions.AllowAnonymous)
; if (isAnonymous)
return continuation.Invoke();
else
{
//该 操作所需要的所有权限
var action_needs_permissions = permissions_on_action.Select(s => s.Action).ToArray();
if (action_needs_permissions.All(a => session.PermissionsArray.Contains(a)))
{
isAuthor = true;
}
else if (action_needs_permissions.Count() == )
{
isAuthor = true;
} if (!isAuthor)
{
var result = new ResultModel(Code.Unauthorized, "没有足够的权限操作资源");
actionContext.Response = new HttpResponseMessage(System.Net.HttpStatusCode.OK) { Content = new StringContent(result.ToJson()) };
}
//如果已经登录,则跳过验证
return continuation.Invoke();
}
}

ExecuteAuthorizationFilterAsync核心代码

2. mvc

  新建Filter 实现 IAuthorizationFilter 接口

 public void OnAuthorization(AuthorizationContext filterContext)
{
//throw new NotImplementedException();
if (filterContext.ActionDescriptor.GetCustomAttributes(typeof(AllowAnonymousAttribute), true).Any()) return; var token = HttpContext.Current.Request.Cookies["token_manager"]?.Value; ActionNeedsPermissions = filterContext.ActionDescriptor.GetCustomAttributes(typeof(PermissionAttribute), false).Cast<PermissionAttribute>().ToList(); if (!token.IsNullOrEmpty())
{
var user_session = Redis.FindStore<SysUserSession>(token);
if (user_session == null)
{
if (ActionNeedsPermissions.Any(a => a.Kind == PermissionKinds.MENU && a.Action == PermissionActions.AllowAnonymous)) return;
}
else
{
if (ActionNeedsPermissions.Count == ) return;
var action_permissions = ActionNeedsPermissions.Select(s => s.Action).ToList();
if (user_session.PermissionsArray.Intersect(action_permissions).Count() == action_permissions.Count) return;
if (ActionNeedsPermissions.Any(a => a.Action == PermissionActions.AllowAnonymous)) return;
}
}
filterContext.Result = new RedirectResult("/Main/Login");
}

OnAuthorization核心代码

.NET 通用高扩展性的细粒度权限管理架构(webApi/Mvc)的更多相关文章

  1. Atitit.软件架构高扩展性and兼容性原理与概论实践attilax总结

    Atitit.软件架构高扩展性and兼容性原理与概论实践attilax总结 1. 什么是可扩展的应用程序?1 2. 松耦合(ioc)2 3. 接口的思考 2 4. 单一用途&模块化,小粒度化2 ...

  2. 优秀开源项目之三:高性能、高并发、高扩展性和可读性的网络服务器架构State Threads

    译文在后面. State Threads for Internet Applications Introduction State Threads is an application library ...

  3. 使用Lua脚本语言开发出高扩展性的系统,AgileEAS.NET SOA中间件Lua脚本引擎介绍

    一.前言 AgileEAS.NET SOA 中间件平台是一款基于基于敏捷并行开发思想和Microsoft .Net构件(组件)开发技术而构建的一个快速开发应用平台.用于帮助中小型软件企业建立一条适合市 ...

  4. jetbrick,新一代 Java 模板引擎,具有高性能和高扩展性

    新一代 Java 模板引擎,具有高性能和高扩展性. <!-- Jetbrick Template Engineer --> <dependency> <groupId&g ...

  5. (转)mysql数据库高可用高扩展性架构方案实施

    http://shanhu.blog.51cto.com/1293405/1212605-----mysql数据库高可用高扩展性架构方案实施

  6. 一步一步Asp.Net MVC系列_权限管理总结(附MVC权限管理系统源码)

    在上一节中我们总结了关于权限控制的方式,我们这一节讲解关于权限控制中角色权限的授予处理等等并做本系列的总结. 首先,我们来谈谈权限控制中角色权限的控制,上一节只是针对权限拦截中比较粗的控制,如果我们需 ...

  7. 如何做到MySQL高扩展性?

    高并发及其关注要点 近年来,随着互联网.移动互联网的飞速发展,业务系统的互动性日益增强,用户规模不断攀升,电商.游戏.直播.在线教育.短视频等一系列新兴移动端应用如雨后春笋般涌现出来,这些应用 “高并 ...

  8. 利用OC对象的消息重定向forwardingTargetForSelector方法构建高扩展性的滤镜功能

    在OC中,当像一个对象发送消息,而对象找到消息后,从它的类方法列表,父类方法列表,一直找到根类方法列表都没有找到与这个选择子对应的函数指针.那么这个对象就会触发消息转发机制. OC对象的继承链和isa ...

  9. MySQL 复制 - 性能与扩展性的基石 1:概述及其原理

    1. 复制概述 MySQL 内置的复制功能是构建基于 MySQL 的大规模.高性能应用的基础,复制解决的基本问题是让一台服务器的数据与其他服务器保持同步. 接下来,我们将从复制概述及原理.复制的配置. ...

随机推荐

  1. Transport layer and Network layer

    http://stackoverflow.com/questions/13333794/networking-difference-between-transport-layer-and-networ ...

  2. oracle监听无法启动

    昨天再监听里新加了个地址,重启电脑后监听无法启动,删除新加地址就好了 # listener.ora Network Configuration File: d:\oracle\product\10.2 ...

  3. iOS之UILabel自适应大小

    //初始化一个label self.label=[[UILabel alloc] init]; //设置自动行数与字符换行 [self.label setNumberOfLines:0]; //给la ...

  4. iOS开发——NSArray中的字典排序

    手头上碰到一个项目,需要给数组中的字典中的一个字段排序,想了想,干脆再字典中增加一个字段,用来记录需要排序字段的第一个字符,用它来作为比较的对象,进行排序. - (void)viewDidLoad { ...

  5. AngularJs ng-class 使用

    今天在做项目的时候要对表格内的部分的最大最小值高亮 解决方案 1. 引用 ng-class 2. 引用原型求最大最小值 实例 AngularJs HTML 代码 <table class=&qu ...

  6. 分治算法(Divide-and-Conquer)和Google的云计算

    1.云计算:涉及到存储.计算.资源的调度和权限的管理等   2.分治算法的原理:           讲一个复杂的问题,分成若干个简单的子问题进行解决,然后对子问题的记过进行合并,得到原有问题的解   ...

  7. Photoshop 画基本图形

    作者:zccst 1,画三角形 按下“U”选择“多边形工具”(是个六边形的图标),在上方的菜单栏中的“边”后面输入“3”,然后在画布中拖动就是三角形了,画好后按下CTRL+ENTER键转化成选区,然后 ...

  8. 如何针对已经安装好的Apache/PHP/Mysql/Nginx程序查看他们的编译参数

    我们经常要对我们已经安装好的程序进行查看他当时的一些编译参数,特别是针对要安装多台服务器来说,而且要保证其他服务器上的软件版本和安装参数必须一致时,这种查看就是很有必要的了.具体查看各程序的编译参数命 ...

  9. u-boot源码下载

    1. 使用git下载u-boot源码  1.1 clone u-boot源码仓库 可以使用git.HTTP.和 rsync协议来下载u-boot源码.你可以使用如下方法来克隆源码树: $ git cl ...

  10. 字符集UTF-8MB4 MySQL utf8mb4 字符集,用于存储emoji表情

    字符集UTF-8MB4 utf8mb4兼容utf8,且比utf8能表示更多的字符.看unicode编码区从1 - 126就属于传统utf8区,当然utf8mb4也兼容这个区,126行以下就是utf8m ...