Django 权限管理
参考博客:http://blog.igevin.info/posts/django-permission/
前段时间领导问我一个问题,django与没有自带的权限机制,是如何实现的?我当时告诉他,django自带的权限管理机制,可以实现对数据库表的增删改查权限的管理,然后他又问我对于业务逻辑的权限管理,我当时一脸懵逼,因为确实不知道啊,没有搞过,所以下来看了看,现在介绍如下;
diango自带的程序可以实现对表的增删该查的权限管理;
1、将所有的动作列出来;
2、分析所有的动作;
首先对于所有的业务逻辑,通过url+action+传入的参数就可以实现;比如,对于销售而言,如果想要修改自己的记录,可以定义action=post,而对于创建记录,可以定义动作为action=add,这些参数都是明确的,但是如果想要查看自己区域的客户记录,url+get+area=区域名,这里的区域名是不确定的;
django自带的权限,可以实现部分功能,我们如果需要给用户或组赋予具体的权限,只需要将具体的权限的字符串赋予用户和组就OK了,但是这样是不够的,因为,大部分时候,我们需要自定义自己的权限,然后赋予给特定的用户,那么怎么做呢?
需要知道的是,django系统自带的权限,它是关联到具体的记录的,我们如果知道了它这样的记录是怎么写的,如果照着写就可以这样的功能啦~~~
一、不使用第三方扩展库实现django权限控制
1、添加自定义权限
总结了两种方式
a、自定义权限可以在model中手动添加
class UserProfile(models.Model): user = models.OneToOneField(User) name = models.CharField(max_length=64) school = models.ForeignKey('School') def __unicode__(self): return self.name class Meta: permissions =(('view_customer_list', u"可以查看客户列表"), ('view_customer_info',u"可以查看客户详情"), ('edit_own_customer_info',u"可以修改自己的客户信息"), )
判断有无自定义的权限:
使用语句:用户对象.has_perm('app名字.权限的codename')
>>> u2=models.UserProfile.objects.last() >>> u2.user.has_perm("crm.view_customer_list") True
方式b、使用Permission对象进行创建
from crm.models import UserProfile from django.contrib.auth.models import Permission from django.contrib.auth.models import ContentType content_type = ContentType.objects.get_for_model(UserProfile) print content_type user profile permission = Permission.objects.create(condename='aaa', name ='bbb', content_type=content_type) permission = Permission.objects.create(codename='aaa', name ='bbb', content_type=content_type) print permission crm | user profile | cccc
每个permission都是django.contrib.auth.Permission
类型的实例,该类型包含三个字段name
, codename
和 content_type
,其中 content_type
反应了permission属于哪个model,codename在
代码逻辑中检查权限时要用, name
是permission的描述,将permission打印到屏幕或页面时默认显示的就是name;
所有的权限都是Permission的对象,保存在auth_permission表中
2、对用户增删权限
总结了四种方式:
a、使用django admin可以实现对用户权限的增删
b、利用user_permissions语句实现用户对象的权限的增删
myuser.user_permissions = [permission_list] myuser.user_permissions.add(permission, permission, ...) #增加权限 myuser.user_permissions.remove(permission, permission, ...) #删除权限 myuser.user_permissions.clear() #清空权限 myuser表示用户对象 其中添加或者删除的permission为auth_permission表的id
上述的user_permission中增加或者删除的的permission表示auth_permission表中的id值;
c、使用Permission对象增删用户对象的权限
需要注意的是上述auth_permissions表中的每一条记录(每一条权限)都是Permission的对象,而使用user_permissions添加或者删除用户对象的权限,参数都是Permission的对象。所以
可以采用如下方式增删用户对象权限;
from django.contrib.auth.models import Permission
view = Permission.objects.get(codename='view_customer_list') print view <Permission: crm | user profile | 可以查看客户列表> u2.user.user_permissions.add(view)
添加结果如下:
d、如果自定义的权限是直接由Permission直接创建的对象,使用u2.user.user_permissions.add(permission),将permission作为参数直接传入就可以啦!!!
Permission对象的创建如下:
from crm.models import UserProfile from django.contrib.auth.models import Permission from django.contrib.auth.models import ContentType content_type = ContentType.objects.get_for_model(UserProfile) print content_type user profile permission = Permission.objects.create(condename='aaa', name ='bbb', content_type=content_type) permission = Permission.objects.create(codename='aaa', name ='bbb', content_type=content_type) print permission crm | user profile | cccc
添加结果如下:
这样自定义权限以及向用户对象增删权限就实现啦;;
补充两条语句:
用户对象.get_all_permissions():获取用户对象的所有权限列表
用户对象.get_group_permissions():获取用户所属组的权限列表
组权限管理逻辑和用户权限管理逻辑一致;
那么如何将我们定义的权限和具体的action关联起来呢
3、将定义好的权限和url(action)关联起来
装饰器可以实现在用户调用url的时候进行权限检测,如果权限通过,可以执行views中对应的函数,django 自带permission_required装饰器可以实现对于url权限的检测,而对于request.method以及传入的参数没有办法实现检测;代码如下:
from django.shortcuts import render,redirect import models from django.contrib.auth.decorators import permission_required @permission_required('crm.view_customer_list',login_url='login',raise_exception=True) def customers(request): customer_list = models.Customer.objects.all() paginator = Paginator(customer_list,3) page = request.GET.get('page') try: customer_objs = paginator.page(page) except PageNotAnInteger: customer_objs = paginator.page(1) except EmptyPage: customer_objs = paginator.page(paginator.num_pages) return render(request,'crm/customers.html',{'customer_list':customer_objs})
如果raise_exception设定为True,在权限没有通过的时候,显示403 Forbidden
但是如果我们需要权限更为精确的控制,例如不仅要求url匹配,request.method以及参数也必须匹配;这个时候,就需要自己写装饰器函数啦
预备知识如下:
from django.core.urlresolvers import resolve def customer_detail(request,customer_id): print(request.user,u'用户') #当前登录的用户 print(type(request)) print(hasattr(request,'GET')) #request.method是否为method print(getattr(request,'GET')) print(request,'request') print(request.path_info,u'携带参数详细path 路径') #url的详细路径 print(resolve(request.path_info).url_name) #url的别名 (<SimpleLazyObject: <User: cqq>>, u'\u7528\u6237') <class 'django.core.handlers.wsgi.WSGIRequest'> True <QueryDict: {}> (<WSGIRequest: GET '/crm/customers/1/'>, 'request') (u'/crm/customers/1/', u'\u643a\u5e26\u53c2\u6570\u8be6\u7ec6path \u8def\u5f84') customer_detail
有了上面的内容,就可以自定义装饰器啦;看下面:
#!/usr/bin/env python # -*- coding:utf-8 -*- from django.core.urlresolvers import resolve from django.shortcuts import render perm_dic = { 'view_customer_list': ['customer_list','GET',[]], 'view_customer_info': ['customer_detail','GET',[]], 'edit_own_customer_info': ['customer_detail','POST',['qq','nam']], } def perm_check(*args,**kwargs): request = args[0] url_resovle_obj = resolve(request.path_info) #request.path.info 用户获取url的路径 current_url_namespace = url_resovle_obj.url_name #获取url的别名,在perm_dic中我们也使用别名 #app_name = url_resovle_obj.app_name #use this name later print("url namespace:",current_url_namespace) matched_flag = False # find matched perm item matched_perm_key = None if current_url_namespace is not None:#if didn't set the url namespace, permission doesn't work,说明这个url别设定了权限控制 print("find perm...") for perm_key in perm_dic: #循环perm_dic perm_val = perm_dic[perm_key] if len(perm_val) == 3:#otherwise invalid perm data format url的动作由三部分组成,url别名+url的method+参数 url_namespace,request_method,request_args = perm_val print(url_namespace,current_url_namespace) if url_namespace == current_url_namespace: #matched the url #在perm_dic中找到了当前的url if request.method == request_method:#matched request method #并且method也相同 if not request_args:#if empty , pass #参数为空的 matched_flag = True #表示匹配了 matched_perm_key = perm_key #并且设定了这个url需要检测的权限 print('mtched...') break #no need looking for other perms else: #如果参数不为空 for request_arg in request_args: #might has many args request_method_func = getattr(request,request_method) #get or post mostly request是一个class #print("----->>>",request_method_func.get(request_arg)) if request_method_func.get(request_arg) is not None: #从反射的字典中获取请求的参数,看是否为空 matched_flag = True # the arg in set in perm item must be provided in request data else: matched_flag = False print("request arg [%s] not matched" % request_arg) break #no need go further if matched_flag == True: # means passed permission check ,no need check others print("--passed permission check--") matched_perm_key = perm_key break else:#permission doesn't work url没有被设定权限检测 return True if matched_flag == True: #pass permission check perm_str = "crm.%s" %(matched_perm_key) #crm.view_customer_list if request.user.has_perm(perm_str): #request.user表示当前用户 print("\033[42;1m--------passed permission check----\033[0m") return True else: print("\033[41;1m ----- no permission ----\033[0m") print(request.user,perm_str) return False else: print("\033[41;1m ----- no matched permission ----\033[0m") def check_permission(func): def wrapper(*args,**kwargs): print('---start check perm---') if perm_check(*args,**kwargs) is not True:#no permisssion return render(args[0],'crm/403.html') return func(*args,**kwargs) return wrapper
这样,不仅对url,而且对request.method和参数都可以进行检测啦!!
4、Template中的权限检查
django权限检查使用全局变量perms
{% if perms.crm.view_customer_list %} <h2>有权限</h2> {% else %} <h2>没有权限</h2> {% endif %}
二、使用第三方库实现django权限控制
虽然django可以实现自定义权限的控制,但是装饰器@permission_required只能实现对url的检查,否则就需要我们自己写装饰器函数啦;
如果不想写装饰器函数,就可以借助第三方扩展库django-guardian实现啦;
1、用户对象增删权限
from guardian.shortcuts import assign_perm, get_perms from guardian.core import ObjectPermissionChecker from guardian.decorators import permission_required from crm.models import UserProfile u3 = UserProfile.objects.get(name='cqq') assign_perm('crm.view_customer_list',u3.user) #给用户添加权限,也可以替换成组 <Permission: crm | user profile | 可以查看客户列表>
需要注意的是,上述的权限设计师通用权限的设计,如果是针对业务逻辑的控制,比如只有在购物之后,才可以查看购物情况,这种权限控制,不是通用权限的工作内容,是开发自己写的;
好,对于用户自定义的权限解决了,我们可以使用上面的方法实现自定义的权限,但是,还有一个问题需要解决,如何将自定义的权限和用户/用户组对应起来;当用户需要执行每一项操作的时候,只需要到对应列表中查看就可以找到自己能够操作的权限了;
可以将对应的列表写入数据库,当然,写入列表或者字典中可是OK的;
最后,自定义权限我们可以写在model中的任何地方,django默认django权限是对于自带的user用户进行权限设定的,所以我们一般将自定义权限的内容写在user用户表下,或者与用户表一对于的表中;
好了,接下来就需要将我们自定义的权限和views试图中的权限进行匹配,但是我们不能改变原来views中函数的调用方式,这样就需要借助装饰器实现啦;;;
参考博客:http://www.cnblogs.com/alex3714/articles/5535652.html