Django——权限控制进阶

一、一级菜单的排序

我们用字典存放菜单信息,而字典是无序的,当一级菜单过多时可能会出现乱序情况,因此需要给一级菜单排序

1.给一级菜单表的model中加一个weight权重的字段 ,权重越大越靠前

 weight = models.IntegerField(default=1, verbose_name=‘权重‘)

2.应用有序字典存放菜单信息

引用:

from collections import OrderedDict

排序:

 # sorted  按照权重的大小对字典的key进行排序
   for i in sorted(menu_dict, key=lambda x: menu_dict[x][‘weight‘], reverse=True):
       order_dict[i] = menu_dict[i]

二.非菜单权限的归属问题

一部分权限不是菜单权限,不在菜单栏显示;

如:

  • 信息列表

    • 客户列表
    • 添加客户
    • 编辑客户

在这个菜单中添加客户与编辑客户就不属于菜单权限,但在进入添加客户或编辑客户页面时需要客户列表展开;这就要给非菜单权限归类;将所属二级菜单权限作为其父权限

可以设计为如下结构:

  • 信息列表

    • 客户列表

      • 添加客户
      • 编辑客户

在model的权限表中添加一个自关联的字段

# 二级菜单的归属,创建自关联字段
    parent = models.ForeignKey(‘Permission‘, null=True, blank=True, verbose_name="二级菜单归属", on_delete=models.CASCADE)

三、路径导航(面包屑)

1、在权限中间件中设置一个列表,通过反射将其设为request的属性

setattr(request, settings.BREADCRUMB, [
            {‘url‘: ‘/index/‘, ‘title‘: ‘首页‘}
        ])

2、权限校验通过后给添加到路径导航信息中

3、自定义@register.inclusion_tag(‘rbac/breadcrumb.html‘)

4、页面中显示

引入rbac {% load rbac %}
显示路径导航栏 {% breadcrumb request %}

四、权限控制实现到按钮级别

控制:当前登录用户如果有该权限显示按钮,如果没有该权限不显示按钮

1、首先在权限表中创建一个存放url别名的字段

    name = models.CharField(max_length=32, verbose_name=‘URL别名‘)

2、url中设置url别名,并用admin后台录入数据库

3、权限初始化时将URL别名也保存到权限字典中,一起存入session

4、自定义过滤器,判断前端传入的name在不在权限字典中,在就返回Turn

5、模板页面引用自定义的过滤器进行判断,返回Turn判断通过,表示该用户有该权限,就显示相应的按钮,否则就不显示

功能实现代码:

组件目录结构:

1.首先是表结构models.py:

from django.db import models

# Create your models here.

class Menu(models.Model):
    """菜单表 一级菜单"""
    title = models.CharField(max_length=32)
    icon = models.CharField(max_length=64, null=True, blank=True, verbose_name=‘图标‘)
    weight = models.IntegerField(default=1, verbose_name=‘权重‘)

    def __str__(self):
        return self.title

class Permission(models.Model):
    """
    权限表
    可以做二级菜单的权限   menu 关联 菜单表
    不可以做菜单的权限    menu=null
    """
    url = models.CharField(max_length=32, verbose_name=‘权限‘)
    title = models.CharField(max_length=32, verbose_name=‘标题‘)
    menu = models.ForeignKey("Menu",null=True, blank=True, verbose_name="所属菜单",on_delete=models.CASCADE)
    # 二级菜单的归属,创建自关联字段
    parent = models.ForeignKey(‘Permission‘, null=True, blank=True, verbose_name="二级菜单归属", on_delete=models.CASCADE)
    name = models.CharField(max_length=32, verbose_name=‘URL别名‘, default=‘customer_list‘)

    class Meta:
        # 这个选项是指定,模型的复数形式是什么,比如:
        # verbose_name_plural = "学校"
        # 如果不指定Django会自动在模型名称后加一个’s’
        verbose_name_plural = ‘权限表‘
        verbose_name = ‘权限‘

    def __str__(self):
        return self.title

class Role(models.Model):
    """
    角色表
    """
    name = models.CharField(max_length=32, verbose_name=‘名称‘)
    permissions = models.ManyToManyField(‘Permission‘, verbose_name=‘角色拥有的权限‘,blank=True)

    def __str__(self):
        return self.name

class User(models.Model):
    """
    用户表
    """
    name = models.CharField(max_length=32, verbose_name=‘名称‘)
    password = models.CharField(max_length=32, verbose_name=‘密码‘)
    roles = models.ManyToManyField(‘Role‘, verbose_name=‘用户拥有的角色‘,blank=True)

    def __str__(self):
        return self.name

2.然后当用户登录成功后进行权限信息的初始化

service/permission.py

from django.conf import settings

def init_permisson(request, obj):
    """
        权限信息的初识化
        保存权限和菜单的信息
        :param request:
        :param obj:
        :return:
     """
    # 登陆成功,保存权限的信息(可能存在创建了角色没有分配权限,有的用户拥有多个角色权限重复的要去重.distinct())
    ret = obj.roles.all().filter(permissions__url__isnull=False).values(‘permissions__url‘,
                                                                        ‘permissions__title‘,
                                                                        ‘permissions__menu__title‘,
                                                                        ‘permissions__menu__icon‘,
                                                                        ‘permissions__menu_id‘,
                                                                        ‘permissions__menu__weight‘,
                                                                        ‘permissions__parent_id‘,
                                                                        ‘permissions__parent__name‘,
                                                                        ‘permissions__id‘,
                                                                        ‘permissions__name‘,
                                                                        ).distinct()

    # 存放权限信息
    permission_dict = {}
    ‘‘‘
    权限的数据结构
    permission_dict = {1: {
    ‘url‘: ‘/customer/list/‘,
    ‘id‘: 1,
    ‘pid‘: None
}, 2: {
    ‘url‘: ‘/customer/add/‘,
    ‘id‘: 2,
    ‘pid‘: 1
}, 3: {
    ‘url‘: ‘/customer/edit/(\\d+)/‘,
    ‘id‘: 3,
    ‘pid‘: 1
}}‘‘‘
    # 存放菜单信息
    menu_dict = {}

    for item in ret:
        # 将所有的权限信息添加到permission_dict
        permission_dict[item[‘permissions__name‘]] = ({‘url‘: item[‘permissions__url‘],
                                                       ‘id‘: item[‘permissions__id‘],
                                                       ‘pid‘: item[‘permissions__parent_id‘],
                                                       ‘pname‘: item[‘permissions__parent__name‘],
                                                       ‘title‘: item[‘permissions__title‘],
                                                       })

        # 构造菜单的数据结构
        menu_id = item.get(‘permissions__menu_id‘)

        # 表示当前的权限是不做菜单的权限
        if not menu_id:
            continue

        # 可以做菜单的权限
        if menu_id not in menu_dict:
            menu_dict[menu_id] = {
                ‘title‘: item[‘permissions__menu__title‘],  # 一级菜单标题
                ‘icon‘: item[‘permissions__menu__icon‘],
                ‘weight‘: item[‘permissions__menu__weight‘],  # 权重
                ‘children‘: [
                    {‘title‘: item[‘permissions__title‘],  # 二级菜单标题
                     ‘url‘: item[‘permissions__url‘],
                     ‘id‘: item[‘permissions__id‘],
                     },
                ]
            }
        else:
            menu_dict[menu_id][‘children‘].append(
                {‘title‘: item[‘permissions__title‘], ‘url‘: item[‘permissions__url‘],
                 ‘id‘: item[‘permissions__id‘], })

    # print(permission_dict)
    # print(menu_dict)
    # 保留权限信息到session(因为session可以存到内存中,提高工作效率)
    print(request)
    request.session[settings.PERMISSION_SESSION_KEY] = permission_dict

    # 保存菜单信息
    request.session[settings.PERMISSION_MENU_KEY] = menu_dict

3.在中间件中进行权限信息的校验

middlewares/rbac.py

import re

from django.utils.deprecation import MiddlewareMixin
from django.conf import settings
from django.shortcuts import HttpResponse, redirect, reverse

class RbacMiddleware(MiddlewareMixin):

    def process_request(self, request):
        # 1.获取当前访问的url
        url = request.path_info

        # 白名单 #(拿后面的url与i匹配,没匹配上Nnoe)
        for i in settings.WHITE_LIST:
            if re.match(i, url):
                return

        # 2. 获取当前用户的权限信息
        permission_dict = request.session.get(settings.PERMISSION_SESSION_KEY)

        # 没有登录的访问
        if not permission_dict:
            return redirect(reverse(‘login‘))

        # 需要登录但是不需要进行权限校验的列表
        for i in settings.NO_PERMISSION_LIST:
            if re.match(i, url):
                return

        # 路径导航
        # 反射(设置属性)setattr(object, name, values)  给对象的属性赋值,若属性不存在,先创建再赋值。
        setattr(request, settings.BREADCRUMB, [
            {‘url‘: ‘/index/‘, ‘title‘: ‘首页‘}
        ])

        # for i in permission_dict:
        #     print(‘i‘,i, type(i)) # 注意i存入session中后会序列化为数字字符串,与之校验也要转化
        ## 1 <class ‘str‘>2 <class ‘str‘>3 <class ‘str‘>4 <class ‘str‘>5 <class ‘str‘>

        # 3.权限校验
        for item in permission_dict.values():
            if re.match(r"^{}$".format(item[‘url‘]), url):
                pid = item.get(‘pid‘)
                id = item.get(‘id‘)
                pname = item.get(‘pname‘)
                if pid:
                    # 表示该权限是二级菜单的子权限,有父权限要让父权限展开
                    # request.current_parent_id = pid
                    setattr(request, settings.CURRENT_MENU, pid)

                    # permission_dict的key存入session后会json序列化为str所以pid也要变为str
                    # print(‘pid‘, pid, type(pid))  # pid 1 <class ‘int‘>

                    p_dict = permission_dict[str(pid)]  # 获取父权限信息
                    # 路径导航
                    getattr(request, settings.BREADCRUMB).append({‘url‘: p_dict[‘url‘], ‘title‘: p_dict[‘title‘]})
                    getattr(request, settings.BREADCRUMB).append({‘url‘: item[‘url‘], ‘title‘: item[‘title‘]})

                else:
                    # 表示当前访问的权限是父权限, 要让自己展开
                    # request.current_parent_id = id
                    setattr(request, settings.CURRENT_MENU, id)

                    # 路径导航
                    getattr(request, settings.BREADCRUMB).append({‘url‘: item[‘url‘], ‘title‘: item[‘title‘]})

                return
        # 拒绝访问
        return HttpResponse("没有访问权限 ")

4.自定义过滤器,inclusion_tag,控制页面数据的显示

templatetags/rbac.py

from django import template
from django.conf import settings
# 引入有序字典
from collections import OrderedDict
import re

register = template.Library()

# 菜单权限
@register.inclusion_tag(‘rbac/menu.html‘)
def menu(request):
    menu_dict = request.session.get(settings.PERMISSION_MENU_KEY)

    # 因为字典是无序的,要使菜单显示有序:
    # 1.在model的菜单表中设置weight权重字段,
    # 2.引用sorted()按权重排序倒叙,权重越大显示越靠前
    # 3.将数据放到有序字典中
    # sorted() 函数对所有可迭代的对象进行排序操作。
    # sort 是应用在 list 上的方法,sorted 可以对所有可迭代的对象进行排序操作。

    order_dict = OrderedDict()
    for i in sorted(menu_dict, key=lambda x: menu_dict[x][‘weight‘], reverse=True):
        # 复制到order_dict中
        order_dict[i] = menu_dict[i]
        # 取一级菜单的信息
        item = order_dict[i]

        # 控制当前权限如果是二级菜单的子权限,菜单展开
        item[‘class‘] = ‘hide‘
        for i in item[‘children‘]:
            if i[‘id‘] == request.current_parent_id:
                # 子权限
                i[‘class‘] = ‘active‘
                item[‘class‘] = ‘‘
                break

    return {‘menu_list‘: order_dict.values()}

# 路径导航,面包屑
@register.inclusion_tag(‘rbac/breadcrumb.html‘)
def breadcrumb(request):
    breadcrumb_list = getattr(request, settings.BREADCRUMB)
    return {‘breadcrumb_list‘: breadcrumb_list}

# 控制权限到按钮级别
@register.filter()
def has_permission(request, name):
    # 判断name是否在权限的字典中
   if name in request.session.get(settings.PERMISSION_SESSION_KEY):
        return True

templates/rbac/menu.html 生成菜单

<div class="multi-menu">
    {% for item in menu_list %}
        <div class="item">
            <div class="title"><i class="fa {{ item.icon }}"></i>&nbsp&nbsp{{ item.title }}</div>
            <div class="body {{ item.class }}" >
                {% for child in item.children %}
                    <a href="{{ child.url }}" class="{{ child.class }}">{{ child.title }}</a>
                {% endfor %}

            </div>
        </div>
    {% endfor %}

</div>

templates/rbac/breadcrumb.html  生成路径导航html片段

<ol class="breadcrumb no-radius no-margin" style="border-bottom: 1px solid #ddd;">
    {% for li in breadcrumb_list %}
        {% if forloop.last %}
              <li class="active">{{ li.title }}</li>
        {% else %}
              <li><a href="{{ li.url }}"> {{ li.title }}</a></li>
        {% endif %}

    {% endfor %}

</ol>

5.模板中应用

应用菜单

{#            <!--引入rbac -->#}
            {% load rbac %}
{#            <!--应用inclusion_tag(‘rbac/menu.html‘)-->#}
            {% menu request %}

路径导航

{% breadcrumb request %}

将权限控制到按钮级别,应用过滤器

 {% if request|has_permission:‘customer_del‘ or  request|has_permission:‘customer_edit‘ %}
                        <td>
                            {% if request|has_permission:‘customer_edit‘ %}
                                <a style="color: #333333;" href="{% url ‘customer_edit‘ row.pk %}">
                                    <i class="fa fa-edit" aria-hidden="true"></i></a>
                            {% endif %}

                            {% if request|has_permission:‘customer_del‘ %}
                                <a style="color: #d9534f;" href="{% url ‘customer_del‘ row.pk %}"><i
                                        class="fa fa-trash-o"></i></a>
                            {% endif %}
                        </td>
                    {% endif %}

settings中的权限相关配置:

# session中保留权限key
PERMISSION_SESSION_KEY = ‘permissions‘
# 保留菜单信息key
PERMISSION_MENU_KEY = ‘menus‘
# 白名单
WHITE_LIST = [
    r‘^/login/$‘,
    r‘^/reg/$‘,
    r‘^/admin/.*‘,
]
# 需要登录但不需要校验的权限列表
NO_PERMISSION_LIST = [
    r‘^/index/$‘,
]

# 路径导航(面包屑)
BREADCRUMB = ‘breadcrumb_list‘
# 路径导航
CURRENT_MENU = ‘current_parent_id‘

原文地址:https://www.cnblogs.com/zwq-/p/10181960.html

时间: 2024-08-27 04:16:43

Django——权限控制进阶的相关文章

[Python学习] Django 权限控制

本文为大家讲解 Django 框架里自带的权限模型,从理论到实战演练,带领大家了解 Django 里权限是怎么一回事. 主要内容 什么是权限管理? Web 权限 Django 权限机制 Django 的权限项 权限应用 Permission(一) Permission(二) User Permission 管理(一) User Permission 管理(二) Group Permission 管理 权限验证(一) 权限验证(二) 权限验证(三) 权限验证(四) 什么是权限管理 权限管理,一般指

django权限控制

django 权限机制的实现: http://blog.igevin.info/posts/django-permission/

django(权限、认证)系统——第三方组件实现Object级别权限控制

在我的系列blog<Django中内置的权限控制>中明确提及到,Django默认并没有提供对Object级别的权限控制,而只是在架构上留了口子.在这篇blog中,我们探讨一个简单流行的Django组件django-guardian来实现Object level permission. 安装配置django-guardian 首先需要安装django-guardian,一般我们喜欢用virtualenv创建一个虚拟环境: >>virtualenv --distribute venv

django中的权限控制

Django默认提供了权限控制,但只能对使用了其自带的登录认证的用户进行权限控制,说白了就是只能对存储在auth_user表中的用户进行权限控制,但不能对未登录过的用户进行权限控制.但如果通过集成LDAP认证后的用户,其用户也会被缓存到该表中,即变相实现了AD用户也能进行权限控制. 权限是auth 应用中定义的Permission类型:User与Permission是many-to-many的关系. Django对于每个模型类,自动增加add.change.delete三种权限,以便于权限控制.

2.4.3、Django用户权限控制

上面已经实现了登录和注销功能,但是还没起作用. 我们需要在每个视图函数里加上限制@login_required 如下: from django.contrib.auth.decorators import login_required ... ... @login_required def data_daka(request): all = class.objects.all() ... ... login_required实现了如下功能: 如果用户没有登录, 重定向到/accounts/log

2.4.4.3、Django用user表last_name字段做权限控制

这不是最好的方法! 数据库User表中因为是系统初始化的表,如果添加额外的字段需要修改django源文件,不利于移植,为避免带来其他问题,可以使用last_name字段做为权限控制,当然也可以用profile方法为user表添加新字段(对于user表示一对一关系,但不是在User表中),session在用户登录之后会一直存储user的信息,所以用user表中的字段在html中引用实现更简单一点(group可以在视图里使用) 如下图: 在http://10.1.1.145:8000/admin/a

django web 自定义通用权限控制

需求:web系统有包含以下5个url,分别对于不同资源: 1.stu/add_stu/ 2.stu/upload_homework/ 3.stu/query_homework/ 4.stu/add_record/ -------------------------------------------------------------------------------------------------------- 学生可以访问:2,3 老师可以访问:1,4 可以通过基于角色对用户权限进行控

django权限机制

1. Django权限机制概述 权限机制能够约束用户行为,控制页面的显示内容,也能使API更加安全和灵活:用好权限机制,能让系统更加强大和健壮.因此,基于Django的开发,理清Django权限机制是非常必要的. 1.1 Django的权限控制 Django用user, group和permission完成了权限机制,这个权限机制是将属于model的某个permission赋予user或group,可以理解为全局 的权限,即如果用户A对数据模型(model)B有可写权限,那么A能修改model

django(权限、认证)系统—— 基于Authentication backends定制

在这篇文章中,我们进行最后关于DjangoPermission系统的探讨,来谈谈关于Permission系统后台接口和扩展后台接口的开发. Django实现的这套permission体系,在底层被抽象为authentication backends.Django auth backends的默认的内置的实现,就是我们前4篇blog所描述的,基于三个数据库模型User,Permission,Group.在实际开发中,很有可能我们的用户标示或者是密码并非存在于User表中,比如说存放在LDAP中,再