zoukankan      html  css  js  c++  java
  • 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'u7528u6237')
    <class 'django.core.handlers.wsgi.WSGIRequest'>
    True
    <QueryDict: {}>
    (<WSGIRequest: GET '/crm/customers/1/'>, 'request')
    (u'/crm/customers/1/', u'u643au5e26u53c2u6570u8be6u7ec6path u8defu5f84')
    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("33[42;1m--------passed permission check----33[0m")
                return True
            else:
                print("33[41;1m ----- no permission ----33[0m")
                print(request.user,perm_str)
                return False
        else:
            print("33[41;1m ----- no matched permission  ----33[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  

  • 相关阅读:
    P1535 游荡的奶牛
    rmq
    bsgs算法详解
    P1396 营救
    P1547 Out of Hay
    P1474 货币系统 Money Systems
    P1209 [USACO1.3]修理牛棚 Barn Repair
    P1208 [USACO1.3]混合牛奶 Mixing Milk
    P1108 低价购买
    android屏幕适配的全攻略--支持不同的屏幕尺寸适配平板和手机
  • 原文地址:https://www.cnblogs.com/cqq-20151202/p/6060434.html
Copyright © 2011-2022 走看看