Django之路 如何开发通用且万能的的权限框架组件

业务场景分析

假设我们在开发一个培训机构的客户关系管理系统,系统分客户管理、学员管理、教学管理3个大模块,每个模块大体功能如下:

客户管理

销售人员可以录入客户信息,对客户进行跟踪,为客户办理报名手续

销售人员可以修改自己录入的客户信息

客户信息不能删除

销售主管可以查看销售报表

学员管理

学员可以在线报名

学员可以查看自己的报名合同、学习有效期

学员可以在线提交作业、查看自己的成绩

教学管理

管理员可以创建新课程、班级

讲师可以创建上课纪录

讲师可以在线点名、批作业

从上面的需求中,我们至少提取出了5个角色,普通销售、销售主管、学员、讲师、管理员,他们能做的事情都是不一样的。

如何设计一套权限组件来实现对上面各种不同功能进行有效的权限控制呢?我们肯定不能LOW到为每个动作都一堆代码来控制权限,对吧?这些表面上看着各种不尽相同的功能,肯定是可以提取出一些相同的规律的,仔细分析,其实每个功能本质上都是一个个的动作,如果能把动作再抽象出具体权限条目,然后把这些权限条目再跟用户关联,每个用户进行这个动作,就检查他没有这个权限,不就实现权限的控制了么?由于这个系统是基于Web的B/S架构,我们可以把每个动作的构成提取成以下的元素。

一个动作 = 一条权限 = 一个url + 一种请求方法(get/post/put...) + 若干个请求参数

那我们接下来需要做的,就是把一条条的权限条目定义出来,然后跟用户关联上就可以了!

开发中需要的权限定义

什么是权限?

权限就是对软件系统中各种资源的访问和操作的控制!

什么是资源?

在软件系统中,数据库、内存、硬盘里数据都是资源,资源就是数据!

动作

资源本身是静态的,必须通过合适的动作对其进行访问和操作,我们说要控制权限,其实本质上是要对访问软件中各种数据资源的动作进行控制 。

动作又可以分为2种:

资源操作动作:访问和操作各种数据资源,比如访问数据库或文件里的数据。

业务逻辑事件动作:访问和操作的目的不是数据源本身,而是借助数据源而产生的一系列业务逻辑,比如批量往远程主机上上传一个文件,你需要从数据库中访问主机列表,但你真正要操作的是远程的主机,这个远程的主机,严格意义上来并不是你的数据资源,而是这个资源代表的实体。

权限授权

权限的使用者可以是具体的个人、亦可以是其他程序,这都没关系,我们可以把权限的授权主体,统称为用户,无论这个用户后面是具体的人,还是一个程序,对权限控制组件来讲,都不影响。

权限必然是需要分组的,把一组权限分成一个组,授权给特定的一些用户,分出来的这个组,就可以称为角色。

权限应该是可以叠加的!

权限组件的设计与代码实现

我们把权限组件的实现分3步,权限条目的定义,权限条目与用户的关联,权限组件与应用的结合。

权限条目的定义

我们前面讲过以下概念,现在需要做的,就是把我们系统中所有的需要控制的权限所对应的动作提取成一条条 url+请求方法+参数的集合就可以。

一个动作 = 一条权限 = 一个url + 一种请求方法(get/post/put...) + 若干个请求参数

以下是提取出来的几条权限


  1. perm_dic={ 
  2. 'crm_table_index':['table_index','GET',[],{},], #可以查看CRM APP里所有数据库表 
  3. 'crm_table_list':['table_list','GET',[],{}], #可以查看每张表里所有的数据 
  4. 'crm_table_list_view':['table_change','GET',[],{}],#可以访问表里每条数据的修改页 
  5. 'crm_table_list_change':['table_change','POST',[],{}], #可以对表里的每条数据进行修改 
  6. }

字典里的key是权限名,一会我们需要用过这些权名来跟用户进行关联。

后面values列表里第一个值如'table_index'是django中的url name,在这里必须相对的url name, 而不是绝对url路径,因为考虑到django url正则匹配的问题,搞绝对路径,不好控制。

values里第2个值是http请求方法。

values里第3个[]是要求这个请求中必须带有某些参数,但不限定对数的值是什么。

values里的第4个{}是要求这个请求中必须带有某些参数,并且限定所带的参数必须等于特定的值。

有的同学看了上面的几条权限定义后,提出疑问,说你这个权限的控制好像还是粗粒度的,比如我想控制用户只能访问客户表里的一条或多条特定的用户怎么办?

哈,这个问题很好,但很容易解决呀,只需要在[] or {}里指定参数就可呀,比如要求http请求参数中必须包括指定的参数,举个例子,我的客户表如下:

Customer表

里面的status字段是用来区分客户是否报名的,我现在的需求是,只允许用户访问客户来源为qq群且已报名的客户,你怎么控制?

通过分析我们得出,这个动作的url为


  1. http://127.0.0.1:9000/kingadmin/crm/customer/?source=qq&status=signed 

客户来源参数是source,报名状态为status,那我的权限条目就可以配置成


  1. 'crm_table_list':['table_list','GET',[],{'source':'qq', 'status':'signed'}] 

权限条目与用户的关联

我们并没有像其他权限系统一样把权限定义的代码写到了数据里了,也许是因为我懒,不想花时间去设计存放权限的表结构,but anyway,基于现有的设计,我们如何把权限条目与用户关联起来呢?

good news is 我们可以直接借用django自带的权限系统,大家都知道 django admin
自带了一个简单的权限组件,允许把用户在使用admin过程中控制到表级别的增删改查程度,但没办法对表里的某条数据控制权限,即要么允许访问整张表,要么不允许访问,实现不了只允许用户访问表中的特定数据的控制。

我们虽然没办法对通过自带的django admin 权限系统实现想要的权限控制,但是可以借用它的权限与用户的关联逻辑!自带的权限系统允许用户添加自定义权限条目,方式如下:


  1. class Task(models.Model): 
  2. ... 
  3. class Meta: 
  4. permissions = ( 
  5. ("view_task", "Can see available tasks"), 
  6. ("change_task_status", "Can change the status of tasks"), 
  7. ("close_task", "Can remove a task by setting its status as closed"), 
  8. ) 这样就添加了3条自定义权限的条目,然后 manage.py migrate 就可以在django自带的用户表里的permissions字段看到你刚添加的条目。
 

只要把刚添加的几条权限移动的右边的框里,那这个用户就相当于有相应的权限了!以后,你在代码里通过以下语句,就可以判定用户是否有相应的权限。


  1. user.has_perm('app.view_task') 

看到这,有的同学还在蒙逼,这个自带的权限跟我们刚才自己定义的权限条目有半毛钱关系么?聪明的同学已经看出来了, 只要我们把刚才自己定义的perm_dic字典里的所有key在这个META类的permissions元组里。就相当于把用户和它可以操作的权限关联起来了!这就省掉了我们必须自己写权限与用户关联所需要的代码了。

权限组件与应用的结合

我们希望我们的权限组件是通用的,可插拔的,它一定要与具体的业务代码分离,以后可以轻松把这个组件移植到其他的项目里去,因此这里我们采用装饰器的模式,把权限的检查、控制封装在一个装饰器函数里,想对哪个Views进行权限控制,就只需要在这个views上加上装饰器就可以了。


  1. @check_permission  
  2. def table_change(request,app_name,table_name,obj_id): 
  3. .....  

那这个@check_permission装饰器里干的事情,就是以下几步:

1.拿到用户请求的url+请求方法+参数到我们的的perm_dic里去一一匹配。

2.当匹配到了对应的权限条目后,就拿着这个条目所对应的权限名,和当前的用户,调用request.user.has_perm(权限名)。

3.如果request.user.has_perm(权限名)返回为True,就认为该用户有权限,直接放行,否则,则返回403页面!

权限检查代码

加入自定义权限

仔细按上面的步骤走下来,并玩了一会的同学,可能会发现一个问题,这个组件对有些权限是控制不到的,就是涉及到一些业务逻辑的权限,没办法控制, 比如我只允许用户访问自己创建的客户数据,这个你怎么控制?

通过控制用户的请求参数是没办法实现的,因为你获取到的request.user是个动态的值,你必须通过代码来判断这条数据是否是由当前请求用户创建的。类似的业务逻辑还有很多?你怎么搞?

仔细思考了10分钟,既然这里必须涉及到允许开发人员通过自定义一些业务逻辑代码来判断用户是否有权限的话,那我在我的权限组件里再提供一个权限自定义函数不就可以了,开发者可以把自定的权限逻辑写到函数里,我的权限组件自动调用这个函数,只要返回为True就认为有权限,就可以啦!

加入了自定义权限钩子的代码

权限配置条目


  1. 'crm_can_access_my_clients':['table_list','GET',[], 
  2. {'perm_check':33,'arg2':'test'},  
  3. custom_perm_logic.only_view_own_customers],

看最后面我们加入的only_view_own_customers就是开发人员自已加的权限控制逻辑,里面想怎么写就怎么写。


  1. def only_view_own_customers(request,*args,**kwargs): 
  2. print('perm test',request,args,kwargs) 
  3. consultant_id = request.GET.get('consultant') 
  4. if consultant_id:  
  5. consultant_id = int(consultant_id) 
  6. print("consultant=1",type(consultant_id)) 
  7. if consultant_id == request.user.id: 
  8. print("\033[31;1mchecking [%s]'s own customers, pass..\033[0m"% request.user) 
  9. return True 
  10. else: 
  11. print("\033[31;1muser can only view his's own customer...\033[0m") 
  12. return False 

这样,万通且通用的权限框架就开发完毕了,权限的控制粒度,可粗可细、可深可浅,包君满意!以后要移植到其它django项目时,你唯一需要改的,就是配置好perm_dic里的权限条目!

作者:李杰

来源:51CTO

时间: 2024-10-26 02:41:53

Django之路 如何开发通用且万能的的权限框架组件的相关文章

走上开放之路: ASP开发人员的J2EE基础(下)

相关文章:走上开放之路: ASP 开发人员的 J2EE 基础(上) ASP/COM 和 J2EE 应用程序模型 接下来研究不同的应用程序体系结构,您可以使用这些体系结构开发 J2EE 应用程序并将 其与相应的 ASP/COM 应用程序体系结构关联起来. 使用 Java Servlets 编程:基础知识 编写 Java Servlets 就可以对来自 URL 的请求进行可编程控制.典型的 servlet 调用 类似于以下步骤: 客户机向 Web 服务器发出请求,将 servlet 命名为 URL

用设计模式开发通用数据库访问器

访问|设计|数据|数据库 用设计模式开发通用数据库访问器      我们都希望在开发软件的时候能少写一些代码,希望能到处使用,希望不用管什么样的数据库软件都能用,我们该怎么办呢? 我们操作数据库时用到些什么类        一般来说,我们对数据库进行操作时都会用到一些类,下面我就对着些类做个总结:1.  SQLServer:a)         System.Data.SqlClient.SqlDataAdapter:SQL数据适配器.表示用于填充 DataSet 和更新 SQL Server

走上开放之路: .NET开发人员的J2EE基础(下)

相关文章:走上开放之路: .NET 开发人员的 J2EE 基础(上) .NET 和 J2EE 应用模型:理解它们的区别 要理解 J2EE 应用程序体系结构的基本概念,第一步是确定如何将现有的 ASP.NET 应用 程序移植到一个基于 J2EE 的模型,或者确定如何从头编写一个 J2EE 应用程序.我们将考 察几个 ASP.NET 模型,以及可能如何将它们转换为根据 J2EE 组件构建的模型.您将看到简 单的"意大利面条式的代码"如何演进为一个更优雅.可复用和可扩展的环境.在研究一些 代

django 简易博客开发 1 安装、创建、配置、admin使用(转)

Django 自称是"最适合开发有限期的完美WEB框架".本文参考<Django web开发指南>,快速搭建一个blog 出来,在中间涉及诸多知识点,这里不会详细说明,如果你是第一次接触Django ,本文会让你在感性上对Django有个认识,完成本文操作后会让你有兴趣阅读的相关书籍和文档. 废话少说,come on!! 本操作的环境: =================== Windows 7/10 python 2.7 Django 1.8.2 ============

用Python+Django+Google App Engine开发SNS应用(一):校内和PyXn

大家好!我是Marsbug团队的Damien.今天起,我会在本blog陆续发表一系列关于Python, Django, Google App Engine以及SNS应用开发的文章. SNS应用可以说是web2.0时代mashup概念的一个非常成功的例子.从Facebook F8平台开始,SNS应用经过1年的发展已经深入人心.而在F8开放一年之际,国内的SNS领域也开始了一场开放平台大战,这也是我们开发者一展身手的好机会.今天,让我先从开发校内应用谈起吧. 开发技术的选择 目前Marsbug已经在

网络相册开发(2)——Spring,SPA,Hibernate框架

搭建java代码框架 开发(2)--Spring,SPA,Hibernate框架-spring hibernate框架"> 引入辅助类和基类 PKgen为 PK 生成器 Java代码 package net.sw.util; import java.text.SimpleDateFormat; import java.util.Date; import java.util.HashMap; import java.util.Map; import java.util.concurrent.

IOS开发:Unity3D游戏引擎之构建框架与导出项目

  首先先去Unity3D官网 http://unity3d.com/,我们可以看到很多关于Unity3D的消息.点击DownLoad开始下载Unity,下载的时候建议不要使用Safari自带的下载工具,因为不支持断点续传,国外网站不稳定.建议使用迅雷去下载.目前最新的版本是Unity 3.4.1 ,完美支持Xcode 4 . 开发:Unity3D游戏引擎之构建框架与导出项目-"> 下载完成后,进入Unity.首次须要走注册流程,这里说一下Unity 3D的购买许可为1500美刀,如果要部

应用-刚准备开发一个毕设,问个程序框架问题

问题描述 刚准备开发一个毕设,问个程序框架问题 一个社区app应用,从程序员角度说,开发前需要准备什么呀?例如,程序框架需要怎么做啊? 解决方案 1.规划应用的功能,梳理清楚业务流程图,要有一个完整的操作过程比如楼主发帖子,其他人回复,并可以对其他人的回复点赞,再回复等等2.确定出实体,和实体见的关系比如上面的描述,一般的名词都是实体.楼主.其他人.帖子.回复.赞"楼主"和"其他人"差不多,可以抽象出个新的实体叫""用户"",

sessionfactory-新手开发web时的问题,采用框架ssh

问题描述 新手开发web时的问题,采用框架ssh org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'sessionFactory' defined in class path resource [applicationContext.xml]: Invocation of init method failed; nested exception is org.hibern