基于资源名的MVC权限控制

  在程序复杂程度不断上升的过程中,无可避免需要触碰到权限控制,而权限控制又与业务逻辑紧紧相关,市场上出现了大量的权限控制产品,而程序的开发,讲究去繁化简的抽象,在我的开发过程中,逐渐发现程序的权限控制核心不外乎两个方面:1、资源定位;2、访问控制列表。本文主要针对资源定位进行分析,并解决一些我所遇见过的问题。而在MVC上,MVC提供给我们了非常好的访问控制扩展机制,我们能够通过这些机制更好地控制系统权限。

  在我们之前的开发中,针对ASP.NET下WebForm进行开发,很多人都采用了继承Page基类自定义BasePage,构造自己的验证逻辑后再将最终的展示页面继承于BasePage,所有的验证逻辑中,也必须解决我上文中提到的两个问题:资源定位和访问控制列表。而WebForm下使用路由机制的机会并不多,意义也并不大,在这个背景下,大多数人产生了这样的错觉:使用URL进行资源定位就够了。我所见过的几个项目,大多采用了这个办法,或者这个办法的细微变种。而WebForm的机制也决定了这个方法是具有一定可行性的,每一个资源(页面)都是一个类,资源定位较为容易。而进入MVC时代后,一切都发生了改变。在MVC的场景下,你将遇到以下几个问题:

  1、  路由机制的引入会出现一个资源多个URL的可能以及增加Area等参数后URL的不确定性。

  2、  Action重载的问题难以解决。例如一个页面Order,在Get方法下不带参访问是被允许的,而在Post下带参访问是被禁止的,但这两个资源的URL都是 /Order/,在一些第三方库的辅助下,甚至能有更多的重载Action,这就导致URL定位机制的全面失效。

  而面对上述情况,用一种什么样的方式进行资源的定位、控制就成为放在我们面前的难题。在MVC下,可以认为每一个方法(Action)即为一个资源(页面),用何种方法能在程序外部控制这些资源使得权限控制能对其轻松进行,这是整个问题的核心。上文说到,每个方法即为一个资源,那我们是否可以将方法名、方法签名作为资源的定位标识?答案是可以的。

  在MVC下,进行权限控制,很自然地想到了使用自定义AuthorizationFilter来进行控制,那么在这个Attribute中是可以获得方法相关信息的。

var t = (ReflectedActionDescriptor) filterContext.ActionDescriptor;
var method= t.MethodInfo.ToString();

  在派生自FilterAttribute, IAuthorizationFilter的自定义Attribute中,可以根据上面两个方法获取方法的完整签名,包括返回类型、方法名、参数类型。

  通过这个方法是可以进行权限控制的,但是这个方法存在着一个致命的缺点:返回值的类型名、参数的类型名,有的是完全限定名,即需要带命名空间,而有的是不完全限定名。而这个完全限定名的获取是较为繁琐的,因此,这个方法的可操作性大大降低。

  那如何优雅地定位到我们所需要控制的资源呢?我们是否可以为我们所需要的资源取一个名字,然后在访问控制列表中将这个名字添加进去,每次执行这个Action的时候获取当前Action的名字,然后在访问控制列表中进行比对就可以解决这个问题。那如何将资源进行命名就成了解决问题的关键。代码级的资源控制必须想到元数据,而需要在元数据中增加信息,这个任务自然又落到Attribute的身上。

  我们自定义一个Attribute为Action标注资源名称:

  [AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = false)]
    public sealed class ResourceCustomerNameAttribute : Attribute
    {
        private readonly string _resourceName;

        public ResourceCustomerNameAttribute(string resourceName)
        {
            _resourceName = resourceName;
        }

        public string ResourceName
        {
            get { return _resourceName; }
        }
    }

  这个Attribute实现的功能很简单:为资源进行命名(如果方便,使用ID会更高效一点)。

  完成了对资源的命名,接下来需要对资源与访问控制列表进行比对,我是这样使用的:继承FilterAttribute, IAuthorizationFilter,在派生类中先获取ResourceCustomerNameAttribute实例

  然后用attribute.ResourceName属性与我们的访问控制列表进行比对:

    public void OnAuthorization(AuthorizationContext filterContext)
        {
            var acl = filterContext.HttpContext.Session[SessionName.PermissionSessionName] as IEnumerable<IAcl>;

            var attNames = filterContext.ActionDescriptor.GetCustomAttributes(typeof(ResourceCustomerNameAttribute), true) as IEnumerable<ResourceCustomerNameAttribute>;

            var anonymous =filterContext.ActionDescriptor.GetCustomAttributes(typeof(AllowAnonymousAttribute), true) as IEnumerable<AllowAnonymousAttribute>;

            if (anonymous != null && anonymous.Any())
            {
                return;
            }
            if (acl == null || !acl.Any())
            {
                filterContext.Result = new RedirectToRouteResult(new System.Web.Routing.RouteValueDictionary(new { Controller = _controller, action = _action }));
            }
            else
            {
                var joinResult = (from aclEntity in acl
                                  join attName in attNames on aclEntity.ResourceName equals attName.ResourceName
                                  select attName
                ).Any();

                if (joinResult)
                {
                    return;
                }
                else
                {
                    filterContext.Result = new RedirectToRouteResult(new System.Web.Routing.RouteValueDictionary(new { Controller = _controller, action = _action }));
                }
            }
        }

  如果比对成功则获取相应访问许可,可以对资源进行访问,若比对失败则跳转至授权失败页面。

  而在实际的使用过程中,我们在登录成功后,将构造用户的ACL,并将ACL存入Session中,之后用户在每一次的访问中均使用Session中的ACL进行比对,授权。

  在这个具体实现中,我有一个难以处理的问题:即用户访问页面时若未授权,则跳转到未授权提示页面,若用户进行基于Json传递的ajax调用时如何向用户提供合适的信息?之前考虑过使用ContentType来判断,可是后来觉得这会导致开发之中的约定过多(更何况我对很多人对Http协议的了解程度并不抱有信心)。不知各位是否有更好的办法来实现多种错误返回信息?望不吝赐教。

  模块的具体代码已托管于GitHub:https://github.com/uliian/ULiiAnPermissionControlModule

  功能核心模块已经上传,示例正被GFW狂虐~各位稍安勿躁,如有靠谱的全局科学上网方法的同学也希望悄悄告诉我一下~T.T

  欢迎参考,提出您的宝贵意见。

时间: 2024-12-28 01:21:04

基于资源名的MVC权限控制的相关文章

基于Vue实现后台系统权限控制

原文地址:http://refined-x.com/2017/08/29/基于Vue实现后台系统权限控制/,转载请注明出处. 用Vue/React这类双向绑定框架做后台系统再适合不过,后台系统相比普通前端项目除了数据交互更频繁以外,还有一个特别的需求就是对用户的权限控制,那么如何在一个Vue应用中实现权限控制呢?下面是我的一点经验. 权限控制是什么 在权限的世界里服务端提供的一切都是资源,资源可以由请求方法+请求地址来描述,权限是对特定资源的访问许可,所谓权限控制,也就是确保用户只能访问到被分配

spring MVC 权限控制拦截

SecurityInterceptor实现spring mvc 框架的结构在访问控制@Controller之前的权限拦截,具体实现方法,增加总权限控制器 public class SecurityInterceptor extends HandlerInterceptorAdapter{ private static final Logger logger = Logger.getLogger(SecurityInterceptor.class); @Resource private Sessi

Asp.Net MVC 权限控制(二):Controller级别控制

续接上篇:Asp.Net MVC 权限控制(一):使用 Authorize Roles 简单实现 由于直接在Controller上标记角色名有很大的局限性,所以本示例使用 ActionFilterAttribute 进行权限拦截. 首先创建三类标记: 1. 匿名访问标记(AnonymousAttribute)2. 登录用户访问标记(LoginAllowViewAttribute)3. 权限验证访问标记(PermissionPageAttribute) 最重要的一个权限拦截:AuthorizeFi

Asp.Net MVC 权限控制(三):Controller和Action级别控制

续接上篇:Asp.Net MVC 权限控制(二):Controller级别控制 再次在重构!这次对Controller和Action进行验证. 思路:系统有很多功能集,功能集对应很多Controller和Action,角色分配很多功能集. 首先构建一个基础数据: 1.功能集初始化: /// <summary> /// 系统模块 /// </summary> public class SystemModule { public SystemModule() { this.ID = G

基于iptables下OpenVPN访问权限控制

最近有博友咨询关于OpenVPN的用户访问权限控制的问题,即当用户连接进来以后,怎么去控制他的权限,我这里采用了一个脚本的方式自动添加,其它就是采用iptables的三层功能做路由与端口的访问控制,这里将这个shell分享出来,希望对有需要的朋友可以提供帮忙. 权限控制: read -p "请选择您要做的操作:" caozuo case $caozuo in 1) read -p "请输入您需要添加ERP访问权限的用户:" vpnuser   while [ ! `

利用基于@AspectJ的AOP实现权限控制

一. AOP与@AspectJ AOP 是 Aspect Oriented Programming 的缩写,意思是面向方面的编程.我们在系统开发中可以提取出很多共性的东西作为一个 Aspect,可以理解为在系统中,我们需要很多次重复实现的功能.比如计算某个方法运行了多少毫秒,判断用户是不是具有访问权限,用户是否已登录,数据的事务处理,日志记录等等. AOP的术语 连接点(Joinpoint) 程序执行的某个特殊位置:比如类开始初始化前,类初始化后,某个方法调用前,调用后等. 连接点 可 以 理解

Asp.Net MVC 权限控制(一):使用 Authorize Roles 简单实现

最近由于项目的需要对权限控制做了几个Demo,贴出来供大家拍砖! 首先创建一个 BaseController ,让所有的Controller继承自BaseController . [Authorize] public class BaseController : Controller { } 系统登录需要一个 AccountController ,继承自BaseController ,并添加匿名访问标记 AllowAnonymous. AccountController 实现系统的登录功能,并将

基于原生PHP会员交叉权限控制

对于一个网站的后台管理系统,单一的超级管理员权限往往不能满足我们的需求,尤其是对于大型网站而言,这种单一的权限会引发许许多多的问题出现. 比如:一个网站编辑,平时他只是负责公司网站的公告更新,但如果网站后台没有严格的权限限制,他是不是就可以操作到客户的一些信息,这是存在很大隐患的. 如果学过ThinkPHP框架的朋友一定知道有个东西叫RBAC,今天咱不说那个,来说说在原生PHP语言中,怎么实现交叉权限控制. 好了,话不多说,老样子,直接说原理,上代码. 对于权限的交叉控制可以有很多种方法实现,这

asp.net core mvc权限控制:分配权限

前面的文章介绍了如何进行权限控制,即访问控制器或者方法的时候,要求当前用户必须具备特定的权限,但是如何在程序中进行权限的分配呢?下面就介绍下如何利用Microsoft.AspNetCore.Identity.EntityFrameworkCore框架进行权限分配. 在介绍分配方法之前,我们必须理解权限关系,这里面涉及到三个对象:用户,角色,权限,权限分配到角色,角色再分配到用户,当某个用户属于某个角色后,这个用户就具有了角色所包含的权限列表,比如现在有一个信息管理员角色,这个角色包含了信息删除权