基于Django框架的权限组件rbac实例讲解

作者:Mr_Bai 时间:2022-09-27 17:11:51 

1.基于rbac的权限管理

RBAC(Role-Based Access Control,基于角色的访问控制),就是用户通过角色与权限进行关联。简单地说,一个用户拥有若干角色,一个角色拥有若干权限。这样,就构造成“用户-角色-权限”的授权模型。在这种模型中,用户与角色之间,角色与权限之间都是多对多的关系。

简单的模型图示如下:

基于Django框架的权限组件rbac实例讲解

2.Rbac组件的基本目录结构:

基于Django框架的权限组件rbac实例讲解

3.按照写的流程,来讲解rbac组件中的各个部分,以及功能,

3.1 models数据库表设计(models.py)。

为了在前端页面实现2方面的控制,还需要引入两个表菜单menu和分组group:1.在一个页面,当前用户的权限,例如是否显示添加按钮、编辑、删除等按钮;2.左侧菜单栏的创建。所以一共是5个类,7张表,详细model请看下边代码。

models.py


#models.py

from django.db import models

class Menu(models.Model):
 '''页面中的菜单名'''
 title = models.CharField(max_length=32)

class Group(models.Model):
 '''权限url所属的组'''
 caption = models.CharField(verbose_name='组名称',max_length=32)
 menu =models.ForeignKey(verbose_name='组所属菜单',to='Menu',default=1) # 组所在的菜单

class Meta:
   verbose_name_plural = 'Group组表'

def __str__(self):
   return self.caption

class User(models.Model):
 """
 用户表
 """
 username = models.CharField(verbose_name='用户名',max_length=32)
 password = models.CharField(verbose_name='密码',max_length=64)
 email = models.CharField(verbose_name='邮箱',max_length=32)

roles = models.ManyToManyField(verbose_name='具有的所有角色',to="Role",blank=True)
 class Meta:
   verbose_name_plural = "用户表"

def __str__(self):
   return self.username

class Role(models.Model):
 """
 角色表
 """
 title = models.CharField(max_length=32)
 permissions = models.ManyToManyField(verbose_name='具有的所有权限',to='Permission',blank=True)
 class Meta:
   verbose_name_plural = "角色表"

def __str__(self):
   return self.title

class Permission(models.Model):
 """
 权限表
 """
 title = models.CharField(verbose_name='标题',max_length=32)
 url = models.CharField(verbose_name="含正则URL",max_length=64)
 is_menu = models.BooleanField(verbose_name="是否是菜单")

code = models.CharField(verbose_name='url代码',max_length=32,default=0) # 路径对应的描述名称
 group = models.ForeignKey(verbose_name='所属组',to='Group',null=True,blank=True)  # 所属组

class Meta:
   verbose_name_plural = "权限表"

def __str__(self):
   return self.titlemodel

3.2 service中的init_permission.py

功能:在用户登录成功的时候,在session中写入两个内容:1.拿到当前用户的权限url(code信息);2.拿到当前用户的可以做菜单的url信息。

详细代码如下:

初始化权限



def init_permission(user, request):
 '''
 前端页面调用,把当前登录用户的权限放到session中,request参数指前端传入的当前当前login请求时的request
 :param user: 当前登录用户
 :param request: 当前请求
 :return: None
 '''
 # 拿到当前用户的权限信息
 permission_url_list = user.roles.values('permissions__group_id',
                     'permissions__code',
                     'permissions__url',
                     'permissions__group__menu__id',   # 菜单需要
                     'permissions__group__menu__title',  # 菜单需要
                     'permissions__title',  # 菜单需要
                     'permissions__url',   # 菜单需要
                     'permissions__is_menu', # 菜单需要
                     ).distinct()

# 页面显示权限相关,用到了权限的分组,
 dest_dic = {}
 for each in permission_url_list:
   if each['permissions__group_id'] in dest_dic:
     dest_dic[each['permissions__group_id']]['code'].append(each['permissions__code'])
     dest_dic[each['permissions__group_id']]['per_url'].append(each['permissions__url'])
   else:
     # 刚循环,先创建需要的结构,并把第一次的值放进去。
     dest_dic[each['permissions__group_id']] = {'code': [each['permissions__code'], ],
                           'per_url': [each['permissions__url'], ]}

request.session['permission_url_list'] = dest_dic

# 页面菜单相关
 # 1.去掉不做菜单的url,拿到的结果是menu_list,列表中的元素是字典
 menu_list = []
 for item_dic in permission_url_list:
   if item_dic['permissions__is_menu']:
     temp = {'menu_id':item_dic['permissions__group__menu__id'],
         'menu_title':item_dic['permissions__group__menu__title'],
         'permission__title': item_dic['permissions__title'],
         'permission_url':item_dic['permissions__url'],
         'permissions__is_menu':item_dic['permissions__is_menu'],
         'active':False,  # 用于页面是否被选中,
         }
     # temp 其实只是给key重新起名字,之前的名字太长了。。。。
     menu_list.append(temp)
 # 执行完成之后是如下的数据,用来做菜单。

request.session['permission_menu_list'] = menu_list

3.3 中间件md

功能:1.白名单验证;

2.验证是否已经写入session,即:是否已经登录;

3.当前访问的url与当前用户的权限url进行匹配验证,并在request中写入code信息,

详细代码如下:

中间件


import re
from django.shortcuts import render,redirect,HttpResponse
from django.conf import settings

class MiddlewareMixin(object):
 def __init__(self, get_response=None):
   self.get_response = get_response
   super(MiddlewareMixin, self).__init__()

def __call__(self, request):
   response = None
   if hasattr(self, 'process_request'):
     response = self.process_request(request)
   if not response:
     response = self.get_response(request)
   if hasattr(self, 'process_response'):
     response = self.process_response(request, response)
   return response

class M1(MiddlewareMixin):
 '''
 判断用户有无此url的权限的中间件
 '''
 def process_request(self,request):
   current_url = request.path_info

# 1.白名单验证
   valid_url = settings.VALID_URL
   for each in valid_url:
     if re.match(each, current_url):
       return None

# 2.验证是否已经写入session,即:是否已经登录
   permission_dic = request.session.get('permission_url_list')
   if not permission_dic:
     return redirect('/login/')

# 3.与当前访问的url与权限url进行匹配验证,并在request中写入code信息,
   flag = False
   for group_id,code_urls in permission_dic.items():
     for url in code_urls['per_url']:
       regax = '^{0}$'.format(url)
       if re.match(regax,current_url):
         flag = True
         request.permission_code_list = code_urls['code'] # 在session中增加code的信息,用于在页面判断在当前页面的权限,
         break
     if flag:
       break

if not flag:
     return HttpResponse('无权访问')

def process_response(self,request,response):
   return response

3.4 左侧菜单的生成templatetags目录下的rbac.py

功能;生成页面中的左侧菜单用inclusion_tag标签

运用:我们只需要在需要用到的文件中引用就可以生成这个菜单部分的内容。

需要用到的模板文件中:

{% load rbac %}

{% menu_html request %} 这部分就会变成用inclusion_tag生成的menu_html

详细代码如下:

inclusion_tag生成左侧菜单


import re

from django.template import Library

register = Library()

# inclusion_tag的结果是:把menu_html函数的返回值,放到menu_html中做渲染,生成一个渲染之后的大字符串,
# 在前端需要显示这个字符串的地方,只要调用menu_html就可以,如果有菜单需要传参数,这里是request,前端模板本来就有request,
@register.inclusion_tag('menu.html')
def menu_html(request):
 current_url = request.path_info

# 结构化在页面显示的menu数据
 menu_list = request.session.get('permission_menu_list')

menu_show_dic = {}
 for item in menu_list:
   # 先跟当前url进行匹配,如果当前的url在权限URl中,则需要修改当前的active,用于在前端页面的显示。
   url = item['permission_url']
   reg = '^{0}$'.format(url)
   if re.match(reg, current_url):
     print('匹配到了')
     item['active'] = True

if item['menu_id'] in menu_show_dic:
     menu_show_dic[item['menu_id']]['children'].append(
       {'permission__title': item['permission__title'], 'permission_url': item['permission_url'],
        'active': item['active']})
     if item['active']:
       menu_show_dic[item['menu_id']]['active'] = True
   else:
     menu_show_dic[item['menu_id']] = {'menu_id': item['menu_id'],
                      'menu_title': item['menu_title'],
                      'active': False,
                      'children': [{'permission__title': item['permission__title'],
                             'permission_url': item['permission_url'],
                             'active': item['active']}, ]
                      }
     if item['active']:
       menu_show_dic[item['menu_id']]['active'] = True

return {'menu_dic':menu_show_dic}

需要的模板文件templates下的menu.html

menu.html


# menu.html

<div class="menu">
 {% for k,menu in menu_dic.items %}
   {# 一级菜单 #}
   <div class="menu_first">{{ menu.menu_title }}</div>

{# 二级菜单(就是一级菜单下边的内容) #}
   {% if menu.active %}
     <ul class="">
   {% else %}
     <ul class="hide">
   {% endif %}

{% for child in menu.children %}
   {% if child.active %}
     <li class="menu_second active"><a href="{{ child.permission_url }}" rel="external nofollow" rel="external nofollow" >{{ child.permission__title }}</a></li>
   {% else %}
     <li class="menu_second"><a href="{{ child.permission_url }}" rel="external nofollow" rel="external nofollow" >{{ child.permission__title }}</a></li>
   {% endif %}
 {% endfor %}
 </ul>
 {% endfor %}
</div>

使用inclusion_tag的文件示例:

inclusion_tag的使用模板文件


# 这个是django的模板文件
{% load rbac %}

<!DOCTYPE html>
<html lang="en">
<head>
 <meta charset="UTF-8">
 <title>{% block title %}模板{% endblock %}</title>
 <link rel="stylesheet" href="{% static 'rbac/bootstrap-3.3.7/css/bootstrap.min.css' %}" rel="external nofollow" >
 <link rel="stylesheet" href="{% static 'rbac/menu.css' %}" rel="external nofollow" >
 {% block css %} {% endblock css %}

</head>
<body>
<div class="container-fluid">
 <div class="row">
   <div class="col-md-2 menu">
     {% block menu %}
       {% menu_html request %}  {# 用inclusion_tag生成的menu_html #}
     {% endblock menu %}
   </div>
   <div class="col-md-9">
     {% block content %}
     content
     {% endblock %}
   </div>
 </div>
</div>

来源:https://www.cnblogs.com/fengqing89/p/8283470.html

标签:Django,权限,组件,rbac
0
投稿

猜你喜欢

  • MySQL字段类型说明

    2007-09-27 19:22:00
  • Python基于list的append和pop方法实现堆栈与队列功能示例

    2022-10-20 02:26:26
  • 选择一个优秀正文字体的15个技巧

    2008-03-20 13:36:00
  • Python复制文件操作实例详解

    2023-10-22 19:15:29
  • 在mac下查找python包存放路径site-packages的实现方法

    2023-06-12 21:12:36
  • Python动态声明变量赋值代码实例

    2023-11-15 03:30:47
  • 大家一起来折磨浏览器吧!(好玩的东东)

    2010-02-07 12:40:00
  • python爬虫用mongodb的理由

    2023-09-27 23:06:40
  • python+pyqt实现12306图片验证效果

    2023-11-02 07:28:18
  • Python线程下使用锁的技巧分享

    2023-10-03 06:05:42
  • 浅谈Django+Gunicorn+Nginx部署之路

    2023-04-19 16:36:52
  • 用户体验设计中用到的统计学方法

    2009-07-12 08:26:00
  • MySQL的root帐户密码重置方法

    2007-08-24 15:53:00
  • Python用list或dict字段模式读取文件的方法

    2022-07-16 18:29:07
  • 用书的概念理解小网站结构

    2007-10-31 18:08:00
  • Python 自由定制表格的实现示例

    2023-11-11 16:54:41
  • [多图]新:60个国外创意404页面设计

    2008-12-05 12:00:00
  • ORACLE 自动提交问题

    2023-07-24 10:43:13
  • FrontPage2002简明教程五:css样式表的应用

    2008-09-17 11:31:00
  • 从if else到switch case再到抽象

    2010-11-05 18:30:00
  • asp之家 网络编程 m.aspxhome.com