基于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&amp;MongoDB爬取图书馆借阅记录
Feb 05 Python
Python基于正则表达式实现文件内容替换的方法
Aug 30 Python
python通过百度地图API获取某地址的经纬度详解
Jan 28 Python
Python Xml文件添加字节属性的方法
Mar 31 Python
Python中模块(Module)和包(Package)的区别详解
Aug 07 Python
通过celery异步处理一个查询任务的完整代码
Nov 19 Python
python 实现二维列表转置
Dec 02 Python
Transpose 数组行列转置的限制方式
Feb 11 Python
基于python实现上传文件到OSS代码实例
May 09 Python
Python代码覆盖率统计工具coverage.py用法详解
Nov 25 Python
Python中threading库实现线程锁与释放锁
May 17 Python
Python序列化与反序列化相关知识总结
Jun 08 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
世界收音机发展史
2021/03/01 无线电
php抛出异常与捕捉特定类型的异常详解
2016/10/26 PHP
记录一次排查PHP脚本执行卡住的问题
2016/12/27 PHP
PHP下载远程图片的几种方法总结
2017/04/07 PHP
菜单效果
2006/10/14 Javascript
js 上传图片预览问题
2010/12/06 Javascript
JavaScript对象和字串之间的转换实例探讨
2013/04/21 Javascript
js如何取消事件冒泡
2013/09/23 Javascript
使用apply方法实现javascript中的对象继承
2013/12/16 Javascript
javascript函数定义的几种区别小结
2014/01/06 Javascript
Bootstrap基础学习
2015/06/16 Javascript
javascript倒计时效果实现
2015/11/12 Javascript
详解Bootstrap创建表单的三种格式(一)
2016/01/04 Javascript
jquery ztree实现树的搜索功能
2016/02/25 Javascript
学习使用bootstrap的modal和carousel
2016/12/09 Javascript
Angular 2 ngForm中的ngModel、[ngModel]和[(ngModel)]的写法
2017/06/29 Javascript
详解微信小程序中的页面代码中的模板的封装
2017/10/12 Javascript
使用vue-cli webpack 快速搭建项目的代码
2018/11/21 Javascript
JavaScript数值类型知识汇总
2019/11/17 Javascript
[01:07:20]DOTA2-DPC中国联赛 正赛 Dynasty vs XG BO3 第二场 2月2日
2021/03/11 DOTA
Python中使用PyQt把网页转换成PDF操作代码实例
2015/04/23 Python
Python引用计数操作示例
2018/08/23 Python
用Python+OpenCV对比图像质量的几种方法
2019/07/15 Python
python实现udp聊天窗口
2020/03/31 Python
python 使用xlsxwriter循环向excel中插入数据和图片的操作
2021/01/01 Python
CSS3控制HTML元素动画效果
2014/02/08 HTML / CSS
HTML5+CSS3实现无插件拖拽上传图片(支持预览与批量)
2017/01/05 HTML / CSS
html5 冒号分隔符对齐的实现
2019/07/31 HTML / CSS
新西兰最大的天然保健及护肤品网站:HealthPost(直邮中国)
2021/02/13 全球购物
师范应届毕业生自荐信
2013/11/18 职场文书
优秀广告词大全
2014/03/19 职场文书
工作说明书范文
2014/05/07 职场文书
2015年小学数学教师工作总结
2015/05/20 职场文书
黄埔军校观后感
2015/06/10 职场文书
教师见习总结范文
2015/06/23 职场文书
2015年暑期社会实践方案
2015/07/14 职场文书