Million annual salary python road - RBAC role permissions Design

RBAC (Role-Based Access Control, role-based access control), that is, by associating user roles and permissions. Simply put, a user has several roles, each role has a number of privileges. In this way, it is configured to "user - role - permission" license model.

In this model, between users and roles, and permissions between roles, usually-many relationship.

What role? It can be understood as a collection of a number of rights, privileges carrier. For example: a forum system, a "super administrator", "moderator" are roles. Moderators can post, and other users within the manageable version in version management, these permissions. Give a user to grant these permissions, without direct grant user permissions, "moderator" This role can be given to the user.

When a very large number of users, each user is authorized to give the system one by one (grant role), it is a very troublesome thing. In this case, it is necessary to user packets, each user has a plurality of users within the group. In addition to authorize the user, the user may further authorize groups. As a result, all the rights and privileges of the user owns, is owned by the individual user permissions of the user's own user group.

In the application system, the performance rights to what? The operation of the functional modules, deletion of uploaded files, access the menu, and even a button on the page, a picture of visibility controls, can fall within the competence of the category. Some rights design, the operation will function as a class, and the documents, menus, and other page elements as another class, this constitutes a "user - role - authority - resources" licensing model. In doing modeling data table, can the functional operation and integrated management of resources, that is, are carried out directly associated with permissions on the table, this may be more convenient and easy scalability.

RBAC permissions Design:

Users get a picture RBAC permissions Design:

General steps permission design:

# 1 权限表设计(model)
# 2 权限分配(数据分配)
# 3 查询权限并注入权限(封装的session功能)
# 4 权限验证(中间件)
# 5 动态生成左侧菜单

CRM design rights

1. The authority table design (model)

# 用户表
class UserInfo(models.Model):
    username = models.CharField(max_length=32)  # 用户名
    password = models.CharField(max_length=32)  # 密码    最好在注册时把密码加密,在这里没有
    roles = models.ManyToManyField('Role')  # 关联到角色表,在数据库中会生成第三张表rbac_userinfo_roles表

    def __str__(self):
        return self.username
        
        
# 角色表
class Role(models.Model):
    name = models.CharField(max_length=16)  # 角色名
    permissions = models.ManyToManyField('Permission')  # 关联到权限表

    def __str__(self):
        return self.name
        
        
# 权限
class Permission(models.Model):
    title = models.CharField(max_length=32)     # 权限名,如 账单管理
    url = models.CharField(max_length=32)       # 权限的url
    menus = models.ForeignKey('Menu',null=True,blank=True)
    # 关联Permission表,用于 非菜单权限 关联 二级菜单权限
    parent = models.ForeignKey('self',null=True,blank=True)
    # url_alias_name 存储url的别名
    url_alias_name = models.CharField(max_length=32,null=True,blank=True)
    


    def __str__(self):
        return self.title
    
    
# 一级菜单数据表
class Menu(models.Model):
    name = models.CharField(max_length=32)      # 一级菜单名
    icon = models.CharField(max_length=32, null=True, blank=True)   # 一级菜单所用的font-awesome图标值
    weight = models.IntegerField(default=100)  # 控制菜单排序的,权重值越大,菜单展示越靠前

    def __str__(self):
        return self.name
# 一级菜单的核心思想
    """
        一级菜单
        id  name  icon
        1   业务系统  
        2   教务系统
        
        
        权限表
        id   title          url              menu_id    
        1    客户展示       /list/            1             
        2    客户添加       /add/             None          
        3    跟进记录展示    /plist/          1           
        4    课程记录       /course/          2
        5    课程记录添加    /add/course/     None
    
    """

2. Rights Assignment (data distribution)

rbac_permission表

rbac_role表

rbac_role_permissions表

rbac_userinfo表

rbac_userinfo_roles表

rbac_menu表

3. Query rights and privileges injection (session function package)

from rbac import models


# 权限注入到session中
def init_permission(request, user_obj):
    # 登录成功之后,将该用户的所有权限(url)全部加入到session中
    permission_list = models.Role.objects.filter(
        userinfo__username=user_obj.username
    ).values(
        'permissions__url',         # 需要设置权限的url
        'permissions__title',       # 权限的名称(即二级菜单名)
        'permissions__pk',          # 权限表里对应的主键值(id)
        'permissions__menus__pk',   # 一级菜单的主键值(id)
        'permissions__menus__name', # 一级菜单名
        'permissions__menus__icon', # 一级菜单的图标的font-awesome值
        'permissions__menus__weight',# 一级菜单的权重(用来对多个一级菜单的排序)
        'permissions__parent_id',   # 权限的id(即二级菜单的id值)
        'permissions__url_alias_name'# 权限的url的别名

    ).distinct()    # 相同的权限去重
    # queryset对象不能通过Json进行可序列化,所以转化成List对象
    # # Object of type 'QuerySet' is not JSON serializable
    # request.session['permission_list'] = list(permission_list)
    permission_dict = {}
    url_alias_name = []         # 存放所有的权限url别名

    # 筛选菜单权限
    menu_dict = {}
    for i in permission_list:
        permission_dict[i.get('permissions__pk')] = i
        url_alias_name.append(i.get('permissions__url_alias_name'))
        if i.get('permissions__menus__pk'):
            if i.get('permissions__menus__pk') in menu_dict:
                menu_dict[i.get('permissions__menus__pk')]['children'].append(
                    {
                        'title': i.get('permissions__title'),
                        'url': i.get('permissions__url'),
                        'second_menu_id': i.get('permissions__pk'),
                    }
                )
            else:
                menu_dict[i.get('permissions__menus__pk')] = {
                    'name': i.get('permissions__menus__name'),
                    'icon': i.get('permissions__menus__icon'),
                    'weight': i.get('permissions__menus__weight'),
                    'children': [
                        {
                            'title': i.get('permissions__title'),
                            'url': i.get('permissions__url'),
                            'second_menu_id': i.get('permissions__pk'),
                        }
                    ]
                }
    # 将菜单权限注入到session
    request.session['menu_dict'] = menu_dict
    request.session['url_alias_name'] = url_alias_name
    request.session['permission_dict'] = permission_dict
    # menu_dict形成以下的数据结构
    '''
        {
            1: {
                'name': '业务系统',
                'icon': 'fa fa-home fa-fw',
                'weight': 100,
                'children': [{
                    'title': '客户管理',
                    'url': '/customer/list/',
                    'second_menu_id': None,
                }]
            },
            2: {
                'name': '财务系统',
                'icon': 'fa fa-jpy fa-fw',
                'weight': 200,
                'children': [{
                    'title': '账单管理',
                    'url': '/payment/list/',
                    'second_menu_id': None,
                }]
            }
    }

    '''
    
    """
    # permission_dict 形成以下的数据结构
    {
    1: {
        'permissions__url': '/customer/list/',
        'permissions__title': '客户管理',
        'permissions__pk': 1,
        'permissions__menus__pk': 2,
        'permissions__menus__name': '业务系统',
        'permissions__menus__icon': 'fafa-homefa-fw',
        'permissions__menus__weight': 200,
        'permissions__parent_id': None,
        'permissions__url_alias_name': 'customer_list'
    },
    2: {
        'permissions__url': '/customer/add/',
        'permissions__title': '添加客户',
        'permissions__pk': 2,
        'permissions__menus__pk': None,
        'permissions__menus__name': None,
        'permissions__menus__icon': None,
        'permissions__menus__weight': None,
        'permissions__parent_id': 1,
        'permissions__url_alias_name': 'customer_add'
    },
    3: {
        'permissions__url': '/customer/edit/(?P<cid>\\d+)/',
        'permissions__title': '编辑客户',
        'permissions__pk': 3,
        'permissions__menus__pk': None,
        'permissions__menus__name': None,
        'permissions__menus__icon': None,
        'permissions__menus__weight': None,
        'permissions__parent_id': 1,
        'permissions__url_alias_name': 'customer_edit'
    },
    4: {
        'permissions__url': '/customer/del/(?P<cid>\\d+)/',
        'permissions__title': '删除客户',
        'permissions__pk': 4,
        'permissions__menus__pk': None,
        'permissions__menus__name': None,
        'permissions__menus__icon': None,
        'permissions__menus__weight': None,
        'permissions__parent_id': 1,
        'permissions__url_alias_name': 'customer_del'
    },
    5: {
        'permissions__url': '/payment/list/',
        'permissions__title': '账单管理',
        'permissions__pk': 5,
        'permissions__menus__pk': 1,
        'permissions__menus__name': '财务系统',
        'permissions__menus__icon': 'fafa-rmbfa-fw',
        'permissions__menus__weight': 100,
        'permissions__parent_id': None,
        'permissions__url_alias_name': 'payment_list'
    },
    6: {
        'permissions__url': '/payment/add/',
        'permissions__title': '添加缴费',
        'permissions__pk': 6,
        'permissions__menus__pk': None,
        'permissions__menus__name': None,
        'permissions__menus__icon': None,
        'permissions__menus__weight': None,
        'permissions__parent_id': 5,
        'permissions__url_alias_name': 'payment_add'
    },
    7: {
        'permissions__url': '/payment/edit/(?P<pid>\\d+)/',
        'permissions__title': '编辑缴费',
        'permissions__pk': 7,
        'permissions__menus__pk': None,
        'permissions__menus__name': None,
        'permissions__menus__icon': None,
        'permissions__menus__weight': None,
        'permissions__parent_id': 5,
        'permissions__url_alias_name': 'payment_edit'
    },
    8: {
        'permissions__url': '/payment/del/(?P<pid>\\d+)/',
        'permissions__title': '删除缴费',
        'permissions__pk': 8,
        'permissions__menus__pk': None,
        'permissions__menus__name': None,
        'permissions__menus__icon': None,
        'permissions__menus__weight': None,
        'permissions__parent_id': 5,
        'permissions__url_alias_name': 'payment_del'
    },
    9: {
        'permissions__url': '/nashui/',
        'permissions__title': '纳税管理',
        'permissions__pk': 9,
        'permissions__menus__pk': 1,
        'permissions__menus__name': '财务系统',
        'permissions__menus__icon': 'fafa-rmbfa-fw',
        'permissions__menus__weight': 100,
        'permissions__parent_id': None,
        'permissions__url_alias_name': 'nashui'
    }
}
    
    """

4. permission verification (intermediate)

In rbac application in a new middlewares folder (the folder name at random), and then create a mymiddleware.py file (py random file name)

Remember to join middleware MIDDLEWARE settings in the file

import re
from django.utils.deprecation import MiddlewareMixin
from django.urls import reverse
from django.shortcuts import redirect, HttpResponse, render


class Auth(MiddlewareMixin):

    def process_request(self, request):

        # 登录认证白名单
        white_list = [reverse('login'), reverse('logout'), ]

        # 权限认证白名单
        permission_white_list = [reverse('index'), '/admin/*']

        request.pid = None

        bread_crumb = [
            {'url': reverse('index'), 'title': '首页'}
        ]

        request.bread_crumb = bread_crumb   # 把生成面包屑的数据放入到request对象中,在42行处加入注入数据
        # 登录认证
        path = request.path
        if path not in white_list:      
            is_login = request.session.get('is_login')
            if not is_login:
                return redirect('login')

            # 权限认证
            permission_dict = request.session.get('permission_dict')

            for white_path in permission_white_list:
                if re.match(white_path, path):
                    break
            else:
                for i in permission_dict.values():
                    reg = r"^%s$" % i['permissions__url']
                    if re.match(reg, path):
                        pid = i.get('permissions__parent_id')
                        if pid: # 如果这个不是菜单权限,就执行
                            # 父级二级菜单路径信息
                            request.bread_crumb.append(
                                {
                                    'url': permission_dict[str(pid)]['permissions__url'],   # 面包屑的父级二级菜单url
                                    'title': permission_dict[str(pid)]['permissions__title']    # 面包屑的父级二级菜单名字
                                }
                            )
                            # 子权限的路径信息  #/payment/add/
                            request.bread_crumb.append(
                                {   # 面包屑的当前权限url
                                    'url': i.get('permissions__url'),
                                    # 面包屑的当前权限名字
                                    'title': i.get('permissions__title')
                                }
                            )
                            request.pid = pid   # 把二级菜单的id注入到request里
                        else:
                            # 二级菜单路径信息
                            request.bread_crumb.append(
                                {
                                    'url': i.get('permissions__url'),
                                    'title': i.get('permissions__title')
                                }
                            )
                            request.pid = i.get('permissions__pk')
                        break
                else:
                    return HttpResponse('你权限不足!!!')

The dynamically generated menu on the left

Accomplished using custom labels

  1. Now rbac templatetags in a new folder (the folder name to be called this)

  2. Create a folder in the file mytags.py (py file any name)

  3. Registration "registrar" in the py file, as follows

    from django import template
    register = template.Library()
  4. Add @ register.inclusion_tag ( 'HTML file name') on the function

    mytags file:

import re
from collections import OrderedDict
from django import template

register = template.Library()   # 注册 注册器register


@register.inclusion_tag('menu.html')
def menu(request):
    menu_dict = request.session.get('menu_dict')
    menu_order_key = sorted(menu_dict, key=lambda x: menu_dict[x]['weight'], reverse=True)  # 对menu_dict里字典数据排序,
    menu_order_dict = OrderedDict() # 生成有序字典,python3.6以上字典默认顺序为加入字典时的顺序,可不用OrderedDict
    for key in menu_order_key:
        menu_order_dict[key] = menu_dict[key]
    # path = request.path
    for k, v in menu_order_dict.items():
        v['class'] = 'hidden'
        for i in v['children']:
            # if re.match(i['url'], path):
            if request.pid == i['second_menu_id']:
                v['class'] = ''
                i['class'] = 'active'
    menu_data = {'menu_data': menu_order_dict}
    return menu_data

Generated HTML file menu on the left

layout.html core components:

#### HTML中生成左侧菜单的两行代码
{% load mytags %}
{% menu request %}


##### 面包屑的代码
<div>
    <ol class="breadcrumb no-radius no-margin" style="border-bottom: 1px solid #ddd;">
{#                <li><a href="#">首页</a></li>#}
{#                <li class="active">客户管理</li>#}
        {% for  crumb in request.bread_crumb %}
            {% if forloop.last %}
                <li class="active">{{ crumb.title }}</li>
            {% else %}
                <li><a href="{{ crumb.url }}">{{ crumb.title }}</a></li>
            {% endif %}
        {% endfor %}
    </ol>
</div>

Guess you like

Origin www.cnblogs.com/zhangchaoyin/p/11761524.html