django的权限组件

个人思路设计,没啥太多经验,轻喷

权限的作用:

不同用户,根据不同的权限,显示不同的页面内容。

# 由于用户的权限路径可能重复,所以通过角色对应路径,可以重用,只需要用户指定角色,角色对应路径,就可以很方便(只要限制角色的权限即可,用户指定不同的角色,达到权限重用)

权限要关注的几个点:

(1) 权限注入

在登陆成功的时候注入权限,该测试项目,我按照我中间件认证、动态菜单、模板认证权限需要内容做的权限注入。

# 权限注入的目的是后续更轻松的操作,也可以不注入权限,通过登陆成功的id去看权限也可以

(2) 中间件认证

有人可能疑惑为啥要中间件,前端页面根据对应权限渲染,其实已经达到了权限分配的目的,没有权限不能请求到后端非自己权限的内容了。

中间件目的是:

  限制伪装前端人过来请求,由于伪装前端进行后端请求,如果不做中间件认证就会被偷鸡。

(3) 动态菜单

动态菜单的意义在于,当用户请求的页面,有一个菜单下,所有权限都没有的时候,就不显示该菜单。

(4) 前端页面的模板判断

这里疑惑是权限认证是前端做还是后端做。

后端做这件事情好:

  现实中的例子:

    一个完整的东西,按实际来讲肯定是要让拥有者交给用户手中的。

  实现方式:

    后端通过模板判断方式实现,通过权限路径别名,前端页面每个权限那块写死一个值进行用户的权限认证判断实现。

  前端做的坏处:

    (1) 如果前端做这个事情,肯定是在用户机器上运行到js代码那块才可以,权限用到的标签什么的都是隐藏状态,从后端获取到权限在按权限,进行显示,一个会代码的人,就可以看到,不安全

    (2) 增加了不必要的运行时间,用户权限低,本身,就看不到某块内容,前端还是要加载权限所有内容,不好

    (3) 前端做权限判断,这件事情太麻烦了

  后端做的好处:

    后端拿到页面,进行判断,通过判断的部分,进行部分页面内容返回,增加了安全性,和不必要的麻烦

二级菜单权限,我的写法流程:

1. 设计表结构

一开始,表结构的字段没考虑那么的多,表结构字段都是需要用到什么,临时加,即测试的。

所以根据作用目前完整表结构设计就如下:

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

# 继承了django内置的user表
class UserInfo(AbstractUser):

    # 用户对应的角色
    role = models.ManyToManyField(to="Role")

    class Meta:
        db_table = ‘userInfo‘

class Role(models.Model):
  
    # 角色名,权限配置页面用
    role_name = models.CharField(max_length=20, blank=False)

    # 角色对应路径,因为角色只需要关心这个角色有什么权限路径即可,菜单是权限路径该关系的,所以表结构这样设计
    path = models.ManyToManyField(to=‘Path‘)

    class Meta:
        db_table = ‘role‘

# 动态菜单用
class Menu(models.Model):
  
  # 权限配置页面渲染菜单名称的
    menu_name = models.CharField(max_length=30, blank=False)

    # 动态菜单的请求路径,由于我页面是用的Iframe标签,所以需要有个api地址获取到页面
    request_path = models.CharField(max_length=100)

  # 用于动态菜单渲染,如果值为False表示不渲染至用户显示页面
    is_menu = models.BooleanField(default=1)

    # 二级菜单指向一级菜单,如果是一级菜单pid字段为空
    pid = models.ForeignKey(to=‘self‘, on_delete=False, null=True)

    class Meta:
        db_table = ‘menu‘

class Path(models.Model):
 
    # api接口的地址,用于中间件判断
    auth_path = models.CharField(max_length=100, blank=False)
  
    # 用作一个api接口多种请求方式的限制,api接口设计规范,get获取数据,post提交数据,put更新数据... 权限的细控制
    method = models.CharField(max_length=10, blank=False, default=‘GET‘)
  
    # 用作前端模板判断该用户是否有这个权限,通过别名,防止api接口变化,不需要修改模板页面的作用  
    alias = models.CharField(max_length=20)
  
    # 用作权限配置页面显示权限路径的名称
    path_name = models.CharField(max_length=30)

    # 权限对应的二级菜单是哪个
    menu = models.ForeignKey(to=‘Menu‘, on_delete=False)

    class Meta:
        db_table = ‘path‘

2. 初始数据库数据

有了表结构,肯定是要初始一些初始权限、菜单名、角色、用户的。

文件位置:

在项目的rbac文件夹下的init_data.py。

文件内容:

import os
import django

project_name = ‘test_qx‘  # 不同项目需要修改

os.environ.setdefault("DJANGO_SETTINGS_MODULE", project_name + ".settings")
django.setup()

from rbac.models import *

menu_data = [
    {"id": 1, "menu_name": ‘非菜单-1‘, "is_menu": False},
    {"id": 2, "menu_name": ‘学生管理-1‘},
    {"id": 3, "menu_name": ‘书籍管理-1‘},
    {"id": 4, "menu_name": ‘学生管理‘, "request_path": "/student_page/", "pid_id": 2},
    {"id": 5, "menu_name": ‘获取学生信息‘, "request_path": "/student/", "pid_id": 2},
    {"id": 6, "menu_name": ‘书籍管理‘, "request_path": "/book_page/", "pid_id": 3},
    {"id": 7, "menu_name": ‘非菜单‘, "is_menu": False, "pid_id": 1},
]
path_data = [
    {"id": 1, "auth_path": ‘/student/$‘, "method": ‘GET‘, "alias": ‘get_students‘, "path_name": ‘获取学生‘, "menu_id": 4},
    {"id": 2, "auth_path": ‘/student/$‘, "method": ‘POST‘, "alias": ‘add_student‘, "path_name": ‘添加学生‘, "menu_id": 4},
    {"id": 3, "auth_path": ‘/student/$‘, "method": ‘PUT‘, "alias": ‘edit_student‘, "path_name": ‘编辑学生‘, "menu_id": 4},
    {"id": 4, "auth_path": ‘/student/$‘, "method": ‘DELETE‘, "alias": ‘delete_student‘, "path_name": ‘删除学生‘, "menu_id": 4},
    {"id": 5, "auth_path": ‘/book/$‘, "method": ‘GET‘, "alias": ‘get_books‘, "path_name": ‘获取书籍‘, "menu_id": 6},
    {"id": 6, "auth_path": ‘/book/$‘, "method": ‘POST‘, "alias": ‘add_book‘, "path_name": ‘添加书籍‘, "menu_id": 6},
    {"id": 7, "auth_path": ‘/book/$‘, "method": ‘PUT‘, "alias": ‘edit_book‘, "path_name": ‘编辑书籍‘, "menu_id": 6},
    {"id": 8, "auth_path": ‘/book/$‘, "method": ‘DELETE‘, "alias": ‘delete_book‘, "path_name": ‘删除书籍‘, "menu_id": 6},
    {"id": 9, "auth_path": ‘/index/$‘, "path_name": ‘访问首页‘, "menu_id": 7},
    {"id": 10, "auth_path": ‘/student_page/$‘, "path_name": ‘访问学生页面‘, "menu_id": 7},
    {"id": 11, "auth_path": ‘/book_page/$‘, "path_name": ‘访问书籍页面‘, "menu_id": 7},
    {"id": 12, "auth_path": ‘/logout/$‘, "path_name": ‘注销账户‘, "menu_id": 7},
    # 修改权限得权限
    {"id": 13, "auth_path": ‘/rbac/‘, "method": ‘*‘, "path_name": ‘修改权限‘, "menu_id": 7},
    # 测试一级菜单下面俩个二级菜单
    {"id": 14, "auth_path": ‘/student/$‘, "method": ‘GET‘, "alias": ‘get_students‘, "path_name": ‘获取学生‘, "menu_id": 5},
]
role_data = [
    {"id": 1, "role_name": "管理员", "path_id": (1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14)},
    {"id": 2, "role_name": "普通用户", "path_id": (1, 2, 3, 5, 6, 7, 9, 10, 11, 12, 14)},
]
admin_user_data = [
    {"id": 1, "username": "admin", "password": "123456", "email": "[email protected]"}
]
# 添加菜单数据
for dic in menu_data:
    # 添加菜单
    obj = Menu.objects.create(**dic)
# 添加权限路径数据
for dic in path_data:
    Path.objects.create(**dic)
# 添加角色数据
for dic in role_data:
    temp = dic
    path_ids = temp.pop("path_id")
    # 添加角色
    obj = Role.objects.create(**temp)
    # 添加角色和权限路径的关系
    Role.objects.get(id=obj.id).path.set(path_ids)
# 添加管理员用户
for dic in admin_user_data:
    obj = UserInfo.objects.create_superuser(**dic)
    obj.role.set((1,))  # 用户关联管理员角色

print("执行完毕.")

3. 注册关联初始角色

# 创建用户数据,返回的用户对象
obj = UserInfo.objects.create_user(**dic)
# 关联初始权限
role_id = Role.objects.get(role_name=‘普通用户‘).id
UserInfo.objects.get(id=obj.id).role.set((role_id,))

# 参考写法,效率不高,我随便写的

4. 登陆成功注入权限

注入权限完全是为了中间件认证、动态菜单、前端模板认证时候使用,所有里面放的内容,自己舒服就好。

首先要注入什么内容,我会有个参照结构:

            """
            [
                {
                    "one_menu": "书籍管理-1",
                    "is_menu": 1,  # 为真表示是动态菜单要渲染得内容
                    "two_menu": [
                        {
                            "menu": "书籍管理",
                            "request_path": "/book_page/",
                            "path": [
                                {"method": "get", "auth_path": "/book/", "alias": "get_books"},
                                {"method": "post", "auth_path": "/book/", "alias": "add_book"}
                            ]
                        },
                        {
                            "menu": "获取数据",
                            "request_path": "/book/",
                            "path": [
                                {"method": "get", "auth_path": "/book/$", "alias": "get_books"},
                            ]
                        },
                    ]
                },
                {
                    "one_menu": "学生管理-1",
                    "is_menu": 1,
                    "two_menu": [
                        {
                            "menu": "学生管理",
                            "request_path": "/student_page/",
                            "path": [
                                {"method": "get", "auth_path": "/student/", "alias": "get_students"},
                                {"method": "post", "auth_path": "/student/", "alias": "add_student"}
                            ]
                        },
                    ]
                },
            ]
            """

注:我这里套的结构深了点,有点不好理解些,其实可以菜单名和权限路径可以分到俩里面存的

代码写法:

            all_path_data = []
            # 查询用户都有什么权限路径,顺带出菜单,减少数据库交互,提升效率
            values = UserInfo.objects.filter(id=request.user.id).values(
                ‘role__path__menu__pid__menu_name‘,
                ‘role__path__menu__menu_name‘,
                ‘role__path__menu__request_path‘,
                ‘role__path__menu__is_menu‘,
                ‘role__path__method‘,
                ‘role__path__auth_path‘,
                ‘role__path__alias‘,
            )
            # print(values)
            for dic in values:
                one_menu = dic[‘role__path__menu__pid__menu_name‘]
                menu = dic[‘role__path__menu__menu_name‘]
                request_path = dic[‘role__path__menu__request_path‘]
                is_menu = dic[‘role__path__menu__is_menu‘]
                method = dic[‘role__path__method‘]
                auth_path = dic[‘role__path__auth_path‘]
                alias = dic[‘role__path__alias‘]
                for dic2 in all_path_data:
                    if dic2["one_menu"] == one_menu:
                        temp = {
                            "menu": menu,
                            "request_path": request_path,
                            "path": [{"method": method, "auth_path": auth_path, "alias": alias}]
                        }
                        for dic3 in dic2["two_menu"]:
                            if dic3["menu"] == menu:
                                temp = {"method": method, "auth_path": auth_path, "alias": alias}
                                dic3["path"].append(temp)
                                # 正常添加完跳出,否则重复执行for-else里得内容
                                break
                        else:
                            # 二级菜单没有时候执行
                            dic2["two_menu"].append(temp)
                        # 正常添加完跳出,否则重复执行for-else里得内容
                        break
                else:
                    # 一级菜单没时候才会执行
                    temp = {
                        "one_menu": one_menu,
                        "is_menu": is_menu,
                        "two_menu": [
                            {
                                "menu": menu,
                                "request_path": request_path,
                                "path": [{"method": method, "auth_path": auth_path, "alias": alias}]
                            }
                        ]
                    }
                    all_path_data.append(temp)
            # print(all_path_data)
            # 添加权限路径到session
            request.session["auth"] = all_path_data            

5. 中间件认证

防止伪造前端的人进行请求。

写法思路:

其实就是拿到所有二级菜单下得权限路径,进行请求方法、请求路径得判断,通过就返回None,就可以进入视图或下个中间件了。

中间件得介绍:

和装饰器一个道理,进入里面的内容,得先经过一个装饰器,只不过这里中间件是默认所有视图函数得装饰器。

process_request是进入视图前要进过的,再进入视图函数前,我进行权限认证,防止没权限得人不能进入其中拿到数据。

中间件代码:

import re

from django.utils.deprecation import MiddlewareMixin
from django.shortcuts import HttpResponse, redirect

from test_qx.settings import white_list

class MyMiddleware(MiddlewareMixin):

    def process_request(self, request):
        # 当前请求路径
        now_path = request.path
        now_method = request.method

        # 白名单直接进入
        for rule in white_list:
            if re.search(rule, now_path):
                return None

        # 判断是否进行了登陆
        if not request.user.is_authenticated:
            return redirect(‘/login/‘)

        # 取出用户的路径
        auth = request.session.get(‘auth‘, None)
        # 权限路径认证
        if auth:
            # 进行循环判断
            for one_menu_dic in auth:
                for two_menu_dic in one_menu_dic["two_menu"]:
                    for dic in two_menu_dic["path"]:
                        # 如果当前请求的内容,符合权限里的,就进入
                        if re.search(dic["auth_path"], now_path) and (dic["method"] == now_method or dic["method"] == ‘*‘):
                            return None

        # 没权,则直接返回
        return HttpResponse(‘not allow!‘)

注意:

(1) 我用到了白名单,以后不需要权限认证得、页面不需要用到权限认证得内容,请填入到白名单中,在settings中写的,其它中间件调用同一份白名单即可。

(2) 我path表里method字段为*表示所有请求方式都通过

(3) 请求路径是正则进行匹配通过得,所以path数据表里auth_path字段应该存正则表达式

6. 动态菜单

为了一个页面下所有权限都没有,就表示没必要渲染这个菜单,让用户点击进入,根据实际需求改把(页面没细分所有权限时候)

写法思路:

取出登陆注入得内容,进行循环渲染菜单。

写法:

7. 子页面得权限认证判断

根据权限是否显示该功能。

用到了django模板函数,为了达到内容复用,否则麻烦点,要视图下取出所有alias字段,进行判断,或,模板渲染那循环进行判断。

写法思路:

通过alias字段值,进行判断。

my_tag写法:

from django import template

register = template.Library()   # register和templatetags文件夹名是固定的名称

@register.filter
def match_permission(request, value):
    # value是模板页面传得死值
    # 从request中取出登陆出入得权限,和死值进行判断
    auth = request.session["auth"]
    for one_menu_dic in auth:
        if not one_menu_dic["is_menu"]:
            continue
        for two_menu_dic in one_menu_dic["two_menu"]:
            for dic in two_menu_dic["path"]:
                if dic["alias"] == value:
                    return True
    return False

子页面权限判断写法:

student.html为例:

注意:首先要导入my_tag文件,否则调用不到模板函数match_permission函数。

事件也要进行判断权限进行返回:

8. 完毕

到这权限得核心代码就都完毕了,之后就剩下权限配置页面了。

个人写的简单权限配置页面:

# 随便写的,只为达到个测试效果,个人觉得,权限核心代码可能就100多行,但是配置页面,繁华点可能就几千行了

测试项目代码:

一级菜单版本:

二级菜单版本:

原文地址:https://www.cnblogs.com/zezhou/p/12346202.html

时间: 2024-10-29 13:48:58

django的权限组件的相关文章

Django REST framework —— 权限组件源码分析

在上一篇文章中我们已经分析了认证组件源码,我们再来看看权限组件的源码,权限组件相对容易,因为只需要返回True 和False即可 代码 1 class ShoppingCarView(ViewSetMixin, APIView): 2 permission_classes = [MyPermission, ] 3 def list(self,request, *args, **kwargs): 4 """ 5 查看购物车信息 6 :param args: 7 :param k

Django的rest_framework的权限组件和频率组件源码分析

前言: Django的rest_framework一共有三大组件,分别为认证组件perform_authentication,权限组件check_throttles: 我在前面的博客中已经梳理了认证组件,不知道大家有没有看懂:在这里我把认证的组件的博客地址在贴出来,不清楚的人可以看下 局部设置认证组件的博客:https://www.cnblogs.com/bainianminguo/p/10480887.html 全局设置认证组件的博客:https://www.cnblogs.com/baini

Django中rest_framework的认证组件,权限组件,频率组件,序列化组件的最简化版接口

url urlpatterns = [ url(r'^admin/', admin.site.urls), url(r'^login/', views.Login.as_view()), # Book表 url(r'^books/$',views.BookHandle.as_view({ 'get':'list', 'post':'create' })), url(r'^books/(?P<pk>\d+)/',views.BookHandle.as_view({ 'get':'retrieve

django的RBAC认证z;自定义auth_user表;认证组件权限组件源码分析;认证组件;权限组件

一 RBAC 1.RBAC:全称(Role-Based Access Control):指的是基于用户权限访问控制的认证. 2.Django框架采用的是RBAC认证规则,RBAC认证规则通常会分为:三表规则,五表规则:Django采用的是六表规则. # 三表:用户表.角色表.权限表# 五表:用户表.角色表.权限表.用户角色关系表.角色权限关系表# 六表:用户表.角色表.权限表.用户角色关系表.角色权限关系表.用户权限关系表 3.在Django中六表之间是都是多对多的关系,可通过下面字段跨表访问

权限组件之录入数据

权限组件之录入数据 1.新建一个django项目 rbac 2.增加一个app应用 3.什么是rbac??? 生成表 4.3张表一写,会生成5张表 5.数据库迁移 6.查看表,没有我们自己写的表???都是自带的表 7.settings配置 8.入口文件 9.重新数据化迁移,查看表 admin  添加数据 10.添加数据   admin 11.创建管理员,用户名.(邮箱可以不用填写).密码 12.登入admin 13.默认没有我们自己写的app里的表,需要注册才会显示表 14.第三张表不用加. 1

Django之权限管理插件

参考:https://www.cnblogs.com/alex3714/articles/6661911.html http://www.cnblogs.com/wupeiqi/articles/6229414.html 1.      什么是权限? 权限就是对软件系统中各种资源的访问和操作的控制! 2.      什么是资源? 在软件系统中,数据库.内存.硬盘里数据都是资源,资源就是数据! 3.      动作 资源本身是静态的,必须通过合适的动作对其进行访问和操作,我们说要控制权限,其实本质

RESTful-rest_framework认证组件、权限组件、频率组件-第五篇

认证组件格式: 1 写一个认证类 from rest_framework.authentication import BaseAuthentication class MyAuth(BaseAuthentication): def authenticate(self,request): # request 是封装后的 token = request.query_params.get('token') ret = models.UserToken.objects.filter(token=toke

2 权限(组件)

1. 问:为什么程序需要权限控制? 程序开发时的权限控制,对于不同用户使用系统时候就应该有不同的功能,如: 普通员工 部门主管 总监 总裁 所以,只要有不同角色的人员来使用系统,那么就肯定需要权限系统. 2. 问:为什么要开发权限组件? 答:假设你今年25岁,从今天开始写代码到80岁,每年写5个项目,那么你的一生就会写275个项目,保守估计其中应该有150+个都需要用到权限控制,为了以后不再重复的写代码,所以就开发一个权限组件以便之后55年的岁月中使用. 亲,不要太较真哦,你觉得程序员能到80岁

Django框架(二十)-- Django rest_framework-权限组件

一.权限组件的使用 # 用户信息表 class UserInfo(models.Model): name = models.CharField(max_length=32) # 写choice user_choice=((0,'普通用户'),(1,'会员'),(2,'超级用户')) # 指定choice,可以快速的通过数字,取出文字 user_type=models.IntegerField(choices=user_choice,default=0) pwd = models.CharFiel