Django通用权限设计

新建项目

models:

from django.db import models
from django.contrib.auth.models import User

# Create your models here.


class Customers(models.Model):
    name = models.CharField(max_length=20, verbose_name='姓名')
    phone = models.CharField(max_length=15, verbose_name='电话')
    owner = models.ForeignKey(User, on_delete=models.DO_NOTHING, verbose_name='所属用户')

    def __str__(self):
        return self.name

    class Meta:
        verbose_name = '客户表'
        verbose_name_plural = verbose_name

        permissions = (
            ('show_all_customer', '查看所有客户'),
            ('show_own_customer', '查看自己的客户')
        )

‘show_all_customer’:这个是需要留意一下的,后面要用到. django的权限匹配 request.user.has_prem(“app.show_all_customer”) 就是将 app名 和 这个名称用.连接之后匹配的。

‘查看所有客户’ 这个就是显示在后台权限列表里的描述。

image

这里只列举两个权限简单说明一下用法,一个是查看所有客户,一个是查看自己的客户。

views:

from django.shortcuts import render

from app.models import Customers
from app.perm_check import check_permission

# Create your views here.


def show_all(request):
    customer_list = Customers.objects.all()
    return render(request, 'app/list.html', locals())


def show_own(request):
    uid = request.GET.get('uid', None)
    if uid:
        customer_list = Customers.objects.filter(owner_id=uid)

    return render(request, 'app/list.html', locals())

urls:

假设:访问所有客户列表 http://localhost:8000/all

访问自己的客户列表需要加参数uid ,  http://localhost:8000/own/?uid=2

from django.contrib import admin
from django.urls import path
from app.views import show_all, show_own

urlpatterns = [
    path('admin/', admin.site.urls),
    path('all/', show_all, name='show_all'),
    path('own/', show_own, name='show_own'),
]

settings.py中添加应用“app”, 我取的名字叫app.  并设置模版目录 “templates”.

同步数据库,并创建超级管理员

python manage.py makemigrations
python manage.py migrate
python manage.py createsuperuser

启动项目,进入后台,添加数据

image

用户 zhangsan, lisi给予登录后台的权限,登录了后台,就等于用户已经登录了,访问其它非后台页面也是登录状态。


权限设计

app目录下新建 prem_list.py文件

perm_list = {
    'app_show_all_customer': ['show_all', 'GET', []],
    'app_show_own_customer': ['show_own', 'GET', ['uid',]]
}

大体思路是这样: 请求过来之后,先到prem_list中去匹配 ‘show_all’这样的url_name, 然后再匹配 请求方法,再去列表中 匹配参数,如果都匹配上了,说明有给设置权限。那么再用request.user.has_perm()方法确定权限是否跟当前用户绑定了,如果绑定了说明有权限。

写一个权限检测的方法,和一个装饰器

app/perm_check.py

from django.urls import resolve
from django.shortcuts import render

from app.perm_list import perm_list


def check(request, *args, **kwargs):
    url_obj = resolve(request.path_info)
    url_name = url_obj.url_name
    print('>>>>:', '权限匹配')

    if request.user.is_superuser:
        return True

    if url_name:
        url_method = request.method

        for k, v in perm_list.items():
            per_url_name = v[0]
            per_method = v[1]
            per_args = v[2]

            args_match = False
            #匹配perm_list中的权限
            if per_url_name == url_name and url_method == per_method:
                method_func = getattr(request, url_method)

                for arg in per_args:
                    if method_func.get(arg):
                        args_match = True
                    else:
                        args_match = False
                        break
                else:
                    args_match = True

            #prem_list权限匹配上了
            if args_match:
                app_name, perm_name = k.split('_', 1)
                perm_str = '%s.%s' % (app_name, perm_name)
                print('>>>>: ', perm_name)
                if request.user.has_perm(perm_str):
                    return True
                else:
                    return False
        else:
            return False


    else:
        return False   #没有匹配上权限,默认不允许



#装饰器
def check_permission(fun):
    def inner(request, *args, **kwargs):
        if check(request, *args, **kwargs):
            return fun(request, *args, **kwargs)
        return render(request, 'app/403.html', locals())
    return inner


权限到这就写完了。

模版

image

list.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <table>
        <tr>
            <th>姓名</th>
            <th>电话</th>
            <th>所属用户</th>
        </tr>
        {% for c in customer_list %}
        <tr>
            <td>{{ c.name }}</td>
            <td>{{ c.phone }}</td>
            <td>{{ c.owner }}</td>
        </tr>
        {% endfor %}
    </table>
</body>
</html>

403.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <div>
        403
    </div>
</body>
</html>

现在给两个视图加上装饰器,检测 权限

from django.shortcuts import render

from app.models import Customers
from app.perm_check import check_permission

# Create your views here.

@check_permission
def show_all(request):
    customer_list = Customers.objects.all()
    return render(request, 'app/list.html', locals())

@check_permission
def show_own(request):
    uid = request.GET.get('uid', None)
    if uid:
        customer_list = Customers.objects.filter(owner_id=uid)

    return render(request, 'app/list.html', locals())


给zhangsan分配查看所有客户的权限 ,lisi分配 查看自己客户 的权限 ,测试 一下

使用zhangsan登录后台后 :

查看所有客户

image

查看自己的客户:

image

image

再使用lisi登录后台:

image

image

image

这里发现一个问题,手动改变uid的值 后,lisi也能看到zhangsan的客户,这就不符合要求了。

要解决这个问题,可以使用勾子函数的方法 。

定义勾子函数

app/perm_hook.py

def own_hook(request):
    uid = request.GET.get('uid', None)
    if uid == str(request.user.id):
        return True
    return False

修改 app/perm_list.py

from app import perm_hook

perm_list = {
    'app_show_all_customer': ['show_all', 'GET', []],
    'app_show_own_customer': ['show_own', 'GET', ['uid',], perm_hook.own_hook]
}

修改 app/perm_check.py

from django.urls import resolve
from django.shortcuts import render

from app.perm_list import perm_list


def check(request, *args, **kwargs):
    url_obj = resolve(request.path_info)
    url_name = url_obj.url_name
    print('>>>>:', '权限匹配')

    if request.user.is_superuser:
        return True

    if url_name:
        url_method = request.method

        for k, v in perm_list.items():
            per_url_name = v[0]
            per_method = v[1]
            per_args = v[2]

            args_match = False
            #匹配perm_list中的权限
            if per_url_name == url_name and url_method == per_method:
                method_func = getattr(request, url_method)

                for arg in per_args:
                    if method_func.get(arg):
                        args_match = True
                    else:
                        args_match = False
                        break
                else:
                    args_match = True

            hook_match = True
            if len(v) >= 4:
                hook_func = v[3]
                hook_match = hook_func(request)

            #prem_list权限匹配上了
            if args_match and hook_match:
                app_name, perm_name = k.split('_', 1)
                perm_str = '%s.%s' % (app_name, perm_name)
                print('>>>>: ', perm_name)
                if request.user.has_perm(perm_str):
                    return True
                else:
                    return False
            else:
                return False
        else:
            return False


    else:
        return False   #没有匹配上权限,默认不允许



#装饰器
def check_permission(fun):
    def inner(request, *args, **kwargs):
        if check(request, *args, **kwargs):
            return fun(request, *args, **kwargs)
        return render(request, 'app/403.html', locals())
    return inner

修改完之后,启动应用 ,再用lisi访问zhangsan的客户:

image

发现已经 不能访问了,再访问一下自己的客户:

image

posted @ 2020-04-16 14:49  黄小墨  阅读(1046)  评论(0编辑  收藏  举报