阿里山QQ

导航

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类型的实例,该类型包含三个字段namecodename 和 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  

posted on 2017-03-01 10:50  阿里山QQ  阅读(670)  评论(0编辑  收藏  举报