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


Posted in Python onAugust 31, 2019

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>

以上就是django中基于rbac实现的权限组件

这篇基于Django框架的权限组件rbac实例讲解就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持三水点靠木。

Python 相关文章推荐
简单谈谈python的反射机制
Jun 28 Python
Python下载网络文本数据到本地内存的四种实现方法示例
Feb 05 Python
Python中一些不为人知的基础技巧总结
May 19 Python
python解析xml简单示例
Jun 21 Python
解决python文件双击运行秒退的问题
Jun 24 Python
使用 Django Highcharts 实现数据可视化过程解析
Jul 31 Python
pytorch:实现简单的GAN示例(MNIST数据集)
Jan 10 Python
python实现的Iou与Giou代码
Jan 18 Python
PyQt5连接MySQL及QMYSQL driver not loaded错误解决
Apr 29 Python
Pyinstaller加密打包应用的示例代码
Jun 11 Python
Python自动化办公Excel模块openpyxl原理及用法解析
Nov 05 Python
Python anaconda安装库命令详解
Oct 16 Python
Django之PopUp的具体实现方法
Aug 31 #Python
对django layer弹窗组件的使用详解
Aug 31 #Python
python2.7实现复制大量文件及文件夹资料
Aug 31 #Python
python3实现高效的端口扫描
Aug 31 #Python
python nmap实现端口扫描器教程
May 28 #Python
Python3多线程版TCP端口扫描器
Aug 31 #Python
简单了解python协程的相关知识
Aug 31 #Python
You might like
PHP新手上路(九)
2006/10/09 PHP
PHPwind整合最土系统用户同步登录实现方法
2010/12/08 PHP
PHP错误WARNING: SESSION_START() [FUNCTION.SESSION-START]解决方法
2014/05/04 PHP
tp5(thinkPHP5)框架数据库Db增删改查常见操作总结
2019/01/10 PHP
jquery实现的元素的left增加N像素 鼠标移开会慢慢的移动到原来的位置
2010/03/21 Javascript
jQuery对象与DOM对象之间的转换方法
2010/04/15 Javascript
JavaScript实现大数的运算
2014/11/24 Javascript
JavaScript对象之深度克隆介绍
2014/12/08 Javascript
AngularJS基础 ng-srcset 指令简单示例
2016/08/03 Javascript
BootStrap iCheck插件全选与获取value值的解决方法
2016/08/24 Javascript
vue-resource 拦截器使用详解
2017/02/21 Javascript
web3.js增加eth.getRawTransactionByHash(txhash)方法步骤
2018/03/15 Javascript
vue 根据数组中某一项的值进行排序的方法
2018/08/30 Javascript
解决微信小程序防止无法回到主页的问题
2018/09/28 Javascript
Vue 递归多级菜单的实例代码
2019/05/05 Javascript
JS数组splice操作实例分析
2019/10/12 Javascript
Node.js 在本地生成日志文件的方法
2020/02/07 Javascript
Python 使用类写装饰器的小技巧
2018/09/30 Python
Python+selenium 获取浏览器窗口坐标、句柄的方法
2018/10/14 Python
Python实现查找数组中任意第k大的数字算法示例
2019/01/23 Python
使用python判断jpeg图片的完整性实例
2019/06/10 Python
详解pandas数据合并与重塑(pd.concat篇)
2019/07/09 Python
python实现ip地址查询经纬度定位详解
2019/08/30 Python
python+opencv实现移动侦测(帧差法)
2020/03/20 Python
在django admin中配置搜索域是一个外键时的处理方法
2020/05/20 Python
python切片作为占位符使用实例讲解
2021/02/17 Python
梅西百货澳大利亚:Macy’s Australia
2017/07/26 全球购物
英国赛车、汽车改装和摩托车零件购物网站:Demon Tweeks
2018/10/29 全球购物
新闻系毕业生推荐信
2013/11/16 职场文书
料理师求职信
2014/01/30 职场文书
十八大标语口号
2014/10/09 职场文书
四年级数学教学反思
2016/02/16 职场文书
十一月早安语录:把心放轻,人生就是一朵自在的云
2019/11/04 职场文书
python实现三次密码验证的示例
2021/04/29 Python
css实现左上角飘带效果的完整代码
2022/03/18 HTML / CSS
vue组件vue-esign实现电子签名
2022/04/21 Vue.js