基于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 相关文章推荐
wxPython 入门教程
Oct 07 Python
Django数据库操作的实例(增删改查)
Sep 04 Python
深入理解Python单元测试unittest的使用示例
Nov 18 Python
解决python通过cx_Oracle模块连接Oracle乱码的问题
Oct 18 Python
详解Python连接MySQL数据库的多种方式
Apr 16 Python
Python发展简史 Python来历
May 14 Python
jupyter notebook 增加kernel教程
Apr 10 Python
Keras实现将两个模型连接到一起
May 23 Python
使用keras实现Precise, Recall, F1-socre方式
Jun 15 Python
Python实现画图软件功能方法详解
Jul 28 Python
Python中requests做接口测试的方法
May 30 Python
如何用python清洗文件中的数据
Jun 18 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开启与关闭错误提示适用于没有修改php.ini的权限
2014/10/16 PHP
PHP读取、解析eml文件及生成网页的方法示例
2017/09/04 PHP
JavaScript 图片预览效果 推荐
2009/12/22 Javascript
js中字符替换函数String.replace()使用技巧
2011/08/14 Javascript
鼠标焦点离开文本框时验证的js代码
2013/07/19 Javascript
图标线性回归斜着移动到指定的位置
2013/08/16 Javascript
详解JavaScript中的4种类型识别方法
2015/09/14 Javascript
轮播的简单实现方法
2016/07/28 Javascript
浅谈JS读取DOM对象(标签)的自定义属性
2016/11/21 Javascript
如何实现json数据可视化详解
2016/11/24 Javascript
js中document.referrer实现移动端返回上一页
2017/02/22 Javascript
JS手机端touch事件计算滑动距离的方法示例
2017/10/26 Javascript
js实现mp3录音通过websocket实时传送+简易波形图效果
2020/06/12 Javascript
Python入门篇之文件
2014/10/20 Python
Python实现对一个函数应用多个装饰器的方法示例
2018/02/09 Python
浅谈pandas中shift和diff函数关系
2018/04/08 Python
Django 忘记管理员或忘记管理员密码 重设登录密码的方法
2018/05/30 Python
Python使用tkinter库实现文本显示用户输入功能示例
2018/05/30 Python
Python wxPython库Core组件BoxSizer用法示例
2018/09/03 Python
python中字符串内置函数的用法总结
2018/09/13 Python
python实现KNN分类算法
2019/10/16 Python
在python里创建一个任务(Task)实例
2020/04/25 Python
查看keras各种网络结构各层的名字方式
2020/06/11 Python
详解Selenium 元素定位和WebDriver常用方法
2020/12/04 Python
德国原装品牌香水、化妆品和手表网站:BRASTY.DE
2016/10/16 全球购物
英国乡村时尚和宠物用品专家:Pet & Country
2018/07/02 全球购物
英国鹦鹉店:Parrot Essentials
2018/12/03 全球购物
幼儿园中秋节活动方案
2014/02/06 职场文书
2014年公司工作总结
2014/11/22 职场文书
2014年法院工作总结
2014/11/24 职场文书
2015年度物流工作总结
2015/04/30 职场文书
行政答辩状范文
2015/05/21 职场文书
新闻稿格式范文
2015/07/18 职场文书
Nginx代理同域名前后端分离项目的完整步骤
2021/03/31 Servers
Spring Data JPA框架Repository自定义实现
2022/04/28 Java/Android
教你如何用cmd快速登录服务器
2022/06/10 Servers