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

今天老男孩IT教育Python教学总监alex带你开通Django之路python

需求讨论数据库

权限设计django

代码设计架构

自定义权限钩子app

 

业务场景分析


假设咱们在开发一个培训机构的客户关系管理系统系统分客户管理、学员管理、教学管理3个大模块每一个模块大致功能以下框架

客户管理
销售人员能够录入客户信息对客户进行跟踪为客户办理报名手续
销售人员能够修改本身录入的客户信息
客户信息不能删除
销售主管能够查看销售报表ide


学员管理 
学员能够在线报名 
学员能够查看本身的报名合同、学习有效期
学员能够在线提交做业 、查看本身的成绩函数

教学管理
管理员能够建立新课程、班级
讲师能够建立上课纪录
讲师能够在线点名、批做业post

从上面的需求中 咱们至少提取出了5个角色普通销售、销售主管、学员、讲师、管理员 他们能作的事情都是不同的学习


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

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

那咱们接下来须要作的就是把 一条条的权限条目定义出来而后跟用户关联上就能够了


开发中须要的权限定义

什么是权限

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

什么时资源

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

 

动做

资源自己是静态的 必须经过合适的动做对其进行访问和操做咱们说要控制权限其实本质上是要对访问 软件中各类数据资源的动做进行控制 

动做又能够分为2种

资源操做动做访问和操做各类数据资源好比访问数据库或文件里的数据

业务逻辑事件动做访问和操做的目的不是数据源自己而是借助数据源而产生的一系列业务逻辑好比批量往远程 主机上上传一个文件你须要从数据库中访问主机列表但你真正要操做的是远程的主机这个远程的主机严格意义上来并非你的数据资源而是这个资源表明的实体。

 

权限受权

权限的使用者能够是具体的我的、亦能够是其它程序 这都不要紧咱们能够把权限的受权主体统称为用户 不管这个用户后面是具体的人仍是一个程序对权限控制组件来说都不影响 。


权限必然是须要分组的把一组权限 分红一个组受权给特定的一些用户分出来的这个组就能够称为角色。


权限 应该是能够叠加的

 

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

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

 

权限条目的定义

咱们前面讲过如下概念 如今须要作的就是把咱们系统中全部的须要控制的权限 所对应的动做 提取成 一条条 url+请求方法+参数的集合就能够如下是提取出来的几条权限


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


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

  

字典里的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
2
3
4
5
6
7
8
class  Task(models.Model):
     ...
     class  Meta:
         permissions  =  (
             ( "view_task" "Can see available tasks" ),
             ( "change_task_status" "Can change the status of tasks" ),
             ( "close_task" "Can remove a task by setting its status as closed" ),
         )

这样就添加了3条自定义权限的条目 而后 manage.py migrate 就能够在django自带的用户表里的permissions字段看到你刚添加的条目。

只要把刚添加的几条权限移动的右边的框里那这个用户就至关于有相应的权限了之后你在代码里经过如下语句就能够断定用户是否有相应的权限。

1
user.has_perm( 'app.view_task' )

 

看到这有的同窗还在懵逼这个自带的权限跟咱们刚才本身定义的权限条目有半毛钱关系么聪明的同窗已经看出来了 只要咱们把刚才本身定义的perm_dic字典里的全部key在这个META类的permissions元组里。就至关于把用户和它能够操做的权限关联起来了这就省掉了咱们必须本身写权限与用户关联所须要的代码了

 

权限组件与应用的结合

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

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

 

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

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

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

  3. 若是request.user.has_perm(权限名)返回为True,就认为该用户有权限 直接放行不然则返回403页面

   权限检查代码

 

加入自定义权限

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

经过控制用户的请求参数是没办法实现的 由于你获取到的request.user是个动态的值你必须经过代码来判断这条数据是不是由当前请求用户建立的。 相似的业务逻辑还有不少你怎么搞

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

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

权限配置条目

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

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

一、def only_view_own_customers(request,*args,**kwargs):

二、    print('perm test',request,args,kwargs)

三、

四、    consultant_id = request.GET.get('consultant')

五、    if consultant_id:

六、       consultant_id = int(consultant_id)

七、 

八、    print("consultant=1",type(consultant_id))

九、

十、    if consultant_id == request.user.id:

十一、       print("\033[31;1mchecking [%s]'s own customers, pass..\033[0m"% request.user)

十二、        return True

1三、    else:

1四、        print("\033[31;1muser can only view his's own customer...\033[0m")

1五、        return False


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

用完以为好记得点赞噢  

更多精彩请关注老男孩IT教育python学院

相关文章
相关标签/搜索