我是学院讲师Alex(李杰)。值此学院“4.20IT充电节”(4月19~20日)之际,与大家分享一下Django之道。正文来了~~~业务场景分析假设我们正在为一家培训机构开发一个客户关系管理系统。系统分为客户管理、学生管理、教学管理三大模块。各模块的大致功能如下:客户管理销售人员可以录入客户信息,跟踪客户,为客户办理注册手续。销售人员可以修改自己录入的客户信息。客户信息无法删除。销售主管可以查看销售报表。学生管理。学生可以在线注册。在线提交作业,查看成绩教学管理管理员可以创建新课程,班级讲师可以创建班级记录讲师可以在线点名,批作业从以上需求中,我们提取了至少5个角色,一般销售和销售主管,学生,讲师,行政人员,他们能做的是不同的。如何设计一套权限组件,实现对以上各种功能的有效权限控制?总不能LOW到每个action都用一堆代码来控制权限吧?这些表面上看起来五花八门。同样的功能,肯定可以提取出一些相同的规则。仔细分析,每个函数本质上都是一个动作。如果可以将动作抽象为具体的权限条目,那么这些权限条目就可以共享给用户。association,每个用户执行这个动作,检查自己没有这个权限。不是可以实现权限控制吗?由于本系统是基于Web的B/S架构,我们可以将各个动作的组成提炼成如下元素。一个action=一个permission=一个url+一个请求方法(get/post/put...)+几个请求参数那么接下来我们要做的就是定义每一个权限入口,并与用户关联起来,上传即可!开发中所需权限的定义是什么?权限就是对软件系统中各种资源的访问和操作的控制!什么是资源?在一个软件系统中,数据库、内存、硬盘中的数据都是资源,资源就是数据!动作资源本身是静态的,必须通过适当的动作来访问和操作。我们说我们需要控制权限,但本质上我们需要控制软件中访问各种数据资源的动作。动作可分为两种类型:资源操作动作:访问和操作各种数据资源,如访问数据库或文件中的数据。业务逻辑事件动作:访问和操作的目的不是数据源本身,而是数据源产生的一系列业务逻辑,比如批量上传文件到远程主机,需要访问主机列表从数据库,但你真正要操作的是一个远程主机。严格来说,这个远程主机不是你的数据资源,而是这个资源所代表的实体。权限授权权限的使用者可以是特定的个人,也可以是其他程序。没关系。我们可以将权限授权主体统称为用户。组件不受影响。权限必须分组。将一组权限分成一组,授权给特定的用户。划分的组可以称为角色。应该叠加权限!权限组件的设计与代码实现我们将权限组件的实现分为三个步骤,权限入口的定义、权限入口与用户的关联、权限组件与应用的结合。权限入口的定义我们之前讲过以下几个概念。我们现在要做的就是将我们系统中所有需要控制的权限对应的action抽取成一个url+请求方法+参数的集合。一个action=一个权限=一个url+一个请求方法(get/post/put...)+几个请求参数下面是提取的权限perm_dic={'crm_table_index':['table_index','GET',[],{},],#可以查看CRMAPP中的所有数据库表'crm_table_list':['table_list','GET',[],{}],#可以查看每个表中的所有数据'crm_table_list_view':['table_change','GET',[],{}],#可以访问表'crm_table_list_change'中每条数据的修改页面'crm_table_list_change':['table_change','POST',[],{}],#你可以修改表中的每条数据}字典中的key是权限名,我们一会儿就需要用到这些权限名来和用户进行关联。后面值列表中的最后一个值,比如'table_index'是django中的url名称,这里必须是相对url名称,而不是绝对url路径,因为考虑到djangourl正则匹配的问题,是不好用绝对路径控件。values中的第二个值是http请求方式。values中的第三个[]要求请求中包含一定的参数,但不限制对数的取值。values中的第四个{}要求在请求中包含一定的参数,参数必须等于具体的值。看了上面几个权限定义,有同学提出疑问,说你的权限控制好像比较粗粒度。比如我想控制用户只能访问customer表中的一个或多个特定用户怎么办?哈,这个问题很好,但是很容易解决。您只需要在[]或{}中指定参数。比如要求http请求参数中必须包含指定的参数。比如我的客户表是这样的:Customer表中的status字段用来区分客户是否已经注册。我现在的需求是只允许用户访问来源为qq群且已注册的客户。你如何控制它?通过分析,我们得出这个动作的url为http://127.0.0.1:9000/kingadmin/crm/customer/?source=qq&status=signed,customersource参数为source,注册状态为status,所以我的权限条目可以配置为'crm_table_list':['table_list','GET',[],{'source':'qq','status':'signed'}]权限条目和用户的关联我们没有像其他权限系统那样把权限定义的代码写到数据里是的,可能是我比较懒,不想花时间去设计存储权限的表结构,但是不管怎么说,基于现有的设计,如何我们是否将权限条目与用户相关联?好消息是我们可以直接借用django自带的权限系统,大家都知道djangoadmin自带了一个简单的权限组件,可以让用户在执行过程中控制表级别的增删改查使用admin,但是没有办法控制表中某条数据的权限,即要么允许访问整张表,要么不允许访问,只允许用户访问特定的控制表中数据无法实现。虽然我们没有办法通过django内置的admin权限系统来实现想要的权限控制,但是我们可以借用它的权限和用户关联逻辑!内置的权限系统允许用户添加自定义权限条目,如下:),("close_task","Canremoveataskbysettingitsstatusasclosed"),)这样就添加了3项自定义权限条目,然后manage.pymigrate就可以在django自带的user表的permissions字段中看到刚才添加的条目了。把刚才添加的几个权限移到右边的框里,用户就拥有了相应的权限!后面可以通过代码中的如下语句判断用户是否有相应的权限。user.has_perm('app.view_task')看到这里,有同学还在被逼。这个内置的权限是不是和我们刚刚定义的权限入口有关?聪明的同学已经看出来了。只要我们把刚才定义的perm_dic字典中的所有key都放在META类的permissionstuple中即可。相当于把用户和它可以操作的权限关联起来了!这使我们不必编写将权限与用户相关联所需的代码。权限组件与应用的结合我们希望我们的权限组件是通用的、可插拔的。它必须与特定的业务代码分开。这个组件以后可以方便的移植到其他项目中,所以这里我们使用装饰控制器的模式,将权限的检查和控制封装在一个装饰器函数中。如果要控制哪些View的权限,只需要在view上添加装饰器即可。@check_permissiondeftable_change(request,app_name,table_name,obj_id):....那么这个@check_permission装饰器做的就是以下几步:1.将用户请求的url+request方法+参数拿到我们的perm_dic中去一一匹配.2.当匹配到相应的权限条目时,调用request.user.has_perm(permissionname),并传入条目和当前用户对应的权限名。3、如果request.user.has_perm(权限名)返回True,则认为该用户有权限,直接允许,否则返回403页面!在自定义权限中添加权限校验码,仔细按照上面的步骤操作,玩一会,同学们可能会发现问题。这个组件不能控制一些权限,里面涉及到一些业务逻辑权限,没办法控制。例如,我只允许用户访问我自己创建的客户数据。如何控制?无法控制用户的请求参数,因为你得到的request.user是一个动态值,必须用代码判断这个数据是否是当前请求用户创建的。类似的业务逻辑还有很多?你怎么做呢?想了10分钟,既然肯定涉及到让开发者通过自定义一些业务逻辑代码来判断用户是否有权限,那我就把它加到我的权限组件中。仅提供权限自定义功能是不够的。开发者可以在函数中写入自定义的权限逻辑,我的权限组件会自动调用这个函数。只要返回True,就认为有权限。定义权限钩子'crm_can_access_my_clients':['table_list','GET',[],{'perm_check':33,'arg2':'test'},custom_perm_logic.only_view_own_customers]的代码权限配置入口,见***我们添加的only_view_own_customers是开发者自己添加的权限控制逻辑,开发者可以在里面随便写。defonly_view_own_customers(request,*args,**kwargs):print('permtest',request,args,kwargs)consultant_id=request.GET.get('consultant')ifconsultant_id:consultant_id=int(consultant_id)print("顾问=1",type(consultant_id))ifconsultant_id==request.user.id:print("\033[31;1mchecking[%s]'sowncustomers,pass..\033[0m"%request.user)returnTrueelse:print("\033[31;1musercanonlyviewhis'sowncustomer...\033[0m")returnFalse至此万通通用权限框架开发完成。权限控制的粒度可粗可细,可深可浅,包总很满意!以后想移植到其他django项目时,唯一需要改变的就是在perm_dic中配置权限项!高校4.20IT充电节(19日至20日2天,免费100节视频课程,视频课程会员40折,非会员30折,套餐20折,微职立减2000元)活动链接:http://edu.51cto.com/activity/lists/id-47.html?wenzhang相关视频教程:Python运维自动化开发视频课程包http://edu.51cto.com/pack/查看/id-291.html
