zoukankan      html  css  js  c++  java
  • rbac

    一、模板继承

    知识点:

    users.html / roles.html 继承自 base.html
    滑动时,固定
    position: fixed;top:60px;bottom:0;left:0;200px;
    overflow: auto;

    base.html
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>base</title>
        <!-- 最新版本的 Bootstrap 核心 CSS 文件 -->
        <link rel="stylesheet" href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
        <style type="text/css">
            *{padding: 0;margin: 0;}
            .header{width: 100%; height: 60px;background-color: #336699;}
            .menu{background-color: bisque; position: fixed; top:60px;bottom: 0;left: 0; width: 200px;}
            .content{ position: fixed;top: 60px;bottom: 0; right: 0; left: 200px;overflow: auto;padding: 20px;}
    
        </style>
    
    </head>
    <body>
    
    <div class="header">
    <p>{{ user.name }}</p>
    </div>
    
    <div class="container">
        <div class="menu">
            menu
        </div>
    
        <div class="content">
            {% block con %}
    
            {% endblock con%}
        </div>
    </div>
    
    </body>
    </html>

    users.html

    {% extends 'base.html' %}
    
    {% block con %}
    <h4>用户列表</h4>
        {% for user in user_list %}
        <p>{{ user }}</p>
        {% endfor %}
        
    {% endblock con%}

    二、table

    知识点:

    {% for user in user_list %}
    <tr>
    <td>{{ forloop.counter }}</td>
    <td>{{ user.name }}</td>
    <td>
    {% for role in user.roles.all %}
      {{ role.title }}
    {% endfor %}
    </td>
    <td>
    <a href="" class="btn btn-danger">删除</a>
    <a href="" class="btn btn-warning">编辑</a>
    </td>
    </tr>
    {% endfor %}

    users.html

    {% extends 'base.html' %}
    
    {% block con %}
        <h4>用户列表</h4>
        <a href="" class="btn btn-primary">添加</a>
        <table class="table table-bordered table-striped">
            <thead>
                <tr>
                    <th>序号</th>
                    <th>姓名</th>
                    <th>角色</th>
                    <th>操作</th>
                </tr>
            </thead>
            <tbody>
            {% for user in user_list %}
                <tr>
                    <td>{{ forloop.counter }}</td>
                    <td>{{ user.name }}</td>
                    <td>
                        {% for role in user.roles.all %}
                        {{ role.title }}
                        {% endfor %}
                    </td>
                    <td>
                        <a href="" class="btn btn-danger">删除</a>
                        <a href="" class="btn btn-warning">编辑</a>
                    </td>
                </tr>
            {% endfor %}
    
            </tbody>
    
        </table>
    
    {% endblock con%}

    三、权限不同(按钮 。。。)

    用户权限不同,按钮显示就不同! 
    登录成功后,就已经注册了session
    request.session['permission_list'] = permission_list

    permission_list = request.session.get('permission_list')

    0.
    {% if "/users/add/" in permission_list %}
    <a href="/users/add/" class="btn btn-primary">添加</a>
    {% endif %}

    {% if '/users/delete/(d+)/' in permission_list %}
    <a href="/users/delete/{{ user.pk }}/" class="btn btn-danger">删除</a>
    {% endif %}

    BUT: 不好,不想让 if "/users/add/" 写死,会有 "/roles/add/" 情况,不健壮!怎么办?
    不应该根据表名,去判断!!
    权限不同,按钮显示就不同 如何做呢?    
    上面问题的解决办法:
      为了扩展,
      # 把两条线 合成一个线
      /users/..
       /roles/...

    1.修改表结构 
    class Permission(models.Model):
    title = models.CharField(max_length=32)
    url = models.CharField(max_length=32)

    # 标记出行为
    action = models.CharField(max_length=32, default="")

    group = models.ForeignKey('PermissionGroup', default=1, on_delete=True)

    def __str__(self):
    return self.title

    class PermissionGroup(models.Model):
    title = models.CharField(max_length=32)

    def __str__(self):
    return self.title

    makemigrations
    migrate

    注意点:

    加了一个权限组表
    将每张表的增删改查,划到一个组里面!
    无论多复杂的,最终一定是对数据库的(增删改查

    修改表结构,重新处理中间件,登录页面:
    目的:全是为了按钮的粒度,同一个模板,同一个视图,
    显示不同的数据,权限

    models.py

    from django.db import models
    
    class User(models.Model):
        name = models.CharField(max_length=32)
        pwd = models.CharField(max_length=32)
        roles = models.ManyToManyField(to='Role')
    
        def __str__(self):
            return self.name
    
    class Role(models.Model):
        title = models.CharField(max_length=32)
        permission = models.ManyToManyField(to='Permission')
    
        def __str__(self):
            return self.title
    
    class Permission(models.Model):
        title = models.CharField(max_length=32)
        url = models.CharField(max_length=32)
    
        # 标记出行为
        action = models.CharField(max_length=32, default="")
    
        group = models.ForeignKey('PermissionGroup', default=1, on_delete=True)
    
        def __str__(self):
            return self.title
    
    class PermissionGroup(models.Model):
        title = models.CharField(max_length=32)
    
        def __str__(self):
            return self.title
    2.admin 修改

    注意:list_display = []


    admin.py
    from django.contrib import admin
    from .models import *
    
    class PerConfig(admin.ModelAdmin):
        list_display = ['title','url','group','action']
    
    admin.site.register(User)
    admin.site.register(Role)
    admin.site.register(Permission,PerConfig)
    admin.site.register(PermissionGroup)

    3.登录之后,重写 initial_session(user,request)
    就是:
    # 在session中注册权限列表 用户权限
    # request.session['permission_list'] = permission_list

    不应该是list 而是dict

    # 在session中注册权限字典
    request.session['permission_dict'] = permission_dict

     注意点:

    permission = user.roles.all().values('permission__url', 'permission__group_id', 'permission__action').distinct()

      对数据的处理,以组为键

    
    
    {1: {'urls': ['/users/', '/users/add/', '/users/delete/(\d+)/', '/users/edit/(\d+)/'], 
         'actions': ['list', 'add', 'delete', 'edit']}, 
     2: {'urls': ['/roles/'], 
         'actions': ['list']}}

    permission.py
    # -*- coding:utf-8 -*-
    
    def initial_session(user, request):
        # 方案一
        # permission = user.roles.all().values('permission__url').distinct()
        # # print(permission)
        # # 去重后的  所有权限!! 将权限 存在 session 中!!
        # # <QuerySet [{'permission__url': '/users/'}, {'permission__url': '/users/add'}]>
        #
        # permission_list = []
        # for item in permission:
        #     permission_list.append(item['permission__url'])
        #
        # print(permission_list)  # ['/users/', '/users/add']
        #
        # # 在session中注册权限列表   用户权限
        # request.session['permission_list'] = permission_list
    
        # 方案二
        permission = user.roles.all().values('permission__url', 'permission__group_id', 'permission__action').distinct()
    
        print('permission:', permission)
    
        # permission: <QuerySet [
        # {'permission__url': '/users/',
        # 'permission__group_id': 1,
        # 'permission__action': 'list'},
    
        # {'permission__url': '/users/add/',
        # 'permission__group_id': 1,
        # 'permission__action': 'add'},
    
        # {'permission__url': '/users/delete/(\d+)/',
        # 'permission__group_id': 1,
        # 'permission__action': 'delete'},
    
        # {'permission__url': '/users/edit/(\d+)/',
        # 'permission__group_id': 1,
        # 'permission__action': 'edit'}]>
    
        # {'permission__url': 'roles/',
        # 'permission__group_id': 2,
        # 'permission__action': 'list'}]>
    
        # 处理数据 : 以组为键
        """
        1:{
            "url":['/users/','/users/add','/users/delete/(\d+)/','/users/edit/(\d+)']
            "action":['list','add','delete','edit']
        }
    
        2:{
            "url":['/roles/']
            "action":['list']  
        }
    
    
        """
    
        permission_dict = {}
        for item in permission:
            gid = item.get('permission__group_id')
            url = item.get('permission__url')
            action = item.get('permission__action')
    
            if not gid in permission_dict.keys():
                permission_dict[gid] = {
                    "urls": [url, ],
                    "actions": [action, ]
                }
    
            else:
                permission_dict[gid]['urls'].append(url)
                permission_dict[gid]['actions'].append(action)
    
        print(permission_dict)
        """
        {1: {'urls': ['/users/', '/users/add/', '/users/delete/(\d+)/', '/users/edit/(\d+)/'], 
            'actions': ['list', 'add', 'delete', 'edit']}, 
        2: {'urls': ['/roles/'], 
            'actions': ['list']}}
        """
    
        # 注册session
        # # 在session中注册权限字典   用户权限
    
        request.session['permission_dict'] = permission_dict
    4.重写中间件
    校验权限

    # 注意: !!
    request.actions = item["actions"]

    rbac.py
    # -*- coding:utf-8 -*-
    from django.shortcuts import HttpResponse, render, redirect
    from django.utils.deprecation import MiddlewareMixin
    import re
    
    class ValidPermission(MiddlewareMixin):
    
        def process_request(self,request):
    
            current_path = request.path_info
    
            # 白名单,不需要任何权限的url
            valid_url_list = ['/login/', '/reg/', '/admin/.*']
    
            for valid_url in valid_url_list:
                ret = re.match(valid_url, current_path)
                if ret:
                    return
    
            # 校验是否登录
            user_id = request.session.get('user_id')
            if not user_id:
                return redirect('/login/')
    
            # # 校验权限 1
            # permission_list = request.session.get('permission_list',[])
            #
            # flag = False
            # for permission in permission_list:
            #     # ['/users/', '/users/add/', '/users/edit/(\d+)/', '/users/delete/(\d+)/']
            #     # 需要 ^ $ 限定!!
            #     permission = "^%s$" % permission
            #
            #     ret = re.match(permission, current_path)
            #     if ret:
            #         flag = True
            #         break
            #
            # if not flag:
            #     return HttpResponse('无访问权限!')
    
            # 校验权限 2 permission_dict
            permission_dict = request.session.get('permission_dict')
            for item in permission_dict.values():
                urls = item['urls']
                for reg in urls:
                    reg = "^%s$" % reg
                    ret = re.match(reg, current_path)
                    if ret:
                        print("action", item['actions'])
    
                        # 注意:妙 !!
                        request.actions = item["actions"]
    
                        return
    
            return HttpResponse('无权访问')
    
    
    """
    permission_dict:
    
    {1: {'urls': ['/users/', '/users/add/', '/users/delete/(\d+)/', '/users/edit/(\d+)/'], 
        'actions': ['list', 'add', 'delete', 'edit']}, 
    2: {'urls': ['/roles/'], 
        'actions': ['list']}}
    
    """
    5.重写users()视图,以及users.html

    1.也可以实现:
    {% if "add" in request.actions %}
    <a href="/users/add/" class="btn btn-primary">添加</a>
    {% endif %}

    BUT: 还可以更好:用来实现!!

    {% if per.add %}
    <a href="/users/add/" class="btn btn-primary">添加</a>
    {% endif %}

    {% if per.delete %}
    <a href="" class="btn btn-danger">删除</a>
    {% endif %}
    -------
    per = Per(request.actions)

    class Per(object):
    def __init__(self,actions):
    self.actions = actions

    def add(self):
    return "add" in self.actions

    def delete(self):
    return "delete" in self.actions

    def edit(self):
    return "edit" in self.actions

    def list(self):
    return "list" in self.actions

    views.py
    from django.shortcuts import render,HttpResponse,redirect
    import re
    
    from rbac.models import *
    from rbac.service.permission import initial_session
    
    class Per(object):
        def __init__(self,actions):
            self.actions = actions
    
        def add(self):
            return "add" in self.actions
    
        def delete(self):
            return "delete" in self.actions
    
        def edit(self):
            return "edit" in self.actions
    
        def list(self):
            return "list" in self.actions
    
    
    
    def users(request):
        user_list = User.objects.all()
    
        user_id = request.session.get('user_id')
        user = User.objects.filter(id=user_id).first()
    
        per = Per(request.actions)
    
        return render(request,'users.html',locals())

    users.html

    {% extends 'base.html' %}
    
    {% block con %}
        <h4>用户列表</h4>
    
    {#    {% if "/users/add/" in permission_list %}#}
    {#    {% if "add" in request.actions %}#}
        {% if per.add %}
            <a href="/users/add/" class="btn btn-primary">添加</a>
        {% endif %}
    
        <table class="table table-bordered table-striped">
            <thead>
                <tr>
                    <th>序号</th>
                    <th>姓名</th>
                    <th>角色</th>
                    <th>操作</th>
                </tr>
            </thead>
            <tbody>
            {% for user in user_list %}
                <tr>
                    <td>{{ forloop.counter }}</td>
                    <td>{{ user.name }}</td>
                    <td>
                        {% for role in user.roles.all %}
                        {{ role.title }}
                        {% endfor %}
                    </td>
                    <td>
                        {% if per.delete %}
                             <a href="" class="btn btn-danger">删除</a>
                        {% endif %}
                        {% if per.edit %}
                            <a href="" class="btn btn-warning">编辑</a>
                        {% endif %}
                    </td>
                </tr>
            {% endfor %}
    
            </tbody>
    
        </table>
    
    {% endblock con%}

    四、效果

    不同的用户,具有不同的权限,

    权限不同,显示的按钮就不同!!

    
    
    权限粒度控制
    
    简单控制:
        {% if "users/add" in permissions_list%}
    
    摆脱表控制
    
    更改数据库结构
        class Permission(models.Model):
            title=models.CharField(max_length=32)
            url=models.CharField(max_length=32)
    
            action=models.CharField(max_length=32,default="")
            group=models.ForeignKey("PermissionGroup",default=1)
            def __str__(self):return self.title
    
        class PermissionGroup(models.Model):
            title = models.CharField(max_length=32)
    
            def __str__(self): return self.title
    
    登录验证:
        permissions = user.roles.all().values("permissions__url","permissions__group_id","permissions__action").distinct()
    
        构建permission_dict
    
            permissions:
                [
    
                 {'permissions__url': '/users/add/',
                 'permissions__group_id': 1,
                 'permissions__action': 'add'},
    
                 {'permissions__url': '/roles/',
                 'permissions__group_id': 2,
                 'permissions__action': 'list'},
    
                 {'permissions__url': '/users/delete/(\d+)',
                 'permissions__group_id': 1,
                 'permissions__action': 'delete'},
    
                 {'permissions__url': 'users/edit/(\d+)',
                 'permissions__group_id': 1,
                 'permissions__action': 'edit'}
                 ]
    
            permission_dict
                 {
    
                 1: {
                 'urls': ['/users/', '/users/add/', '/users/delete/(\d+)', 'users/edit/(\d+)'],
                 'actions': ['list', 'add', 'delete', 'edit']},
    
                 2: {
                 'urls': ['/roles/'],
                 'actions': ['list']}
    
                 }
    
    中间件校验权限:
        permission_dict=request.session.get("permission_dict")
    
        for item in permission_dict.values():
              urls=item['urls']
              for reg in urls:
                  reg="^%s$"%reg
                  ret=re.match(reg,current_path)
                  if ret:
                      print("actions",item['actions'])
                      request.actions=item['actions']
                      return None
    
        return HttpResponse("没有访问权限!")
    
    思考:
        菜单权限显示
    笔记

    五、权限不同(菜单。。。)

    权限不同,菜单栏不同

    只有查看,有必要放到菜单栏!
    即:action == list 放到 菜单栏中

    1.用户登录后,在initial_session中,注册菜单权限

    注意:permission__group__title 还可以这样用,跨了3张表!!

    permissions = user.roles.all().values('permission__url', 'permission__action',
    'permission__group__title').distinct()

    menu_permission_list = []
    for item in permissions:
    if item['permission__action'] == 'list':
    menu_permission_list.append((item['permission__url'], item['permission__group__title']))

    print(menu_permission_list)
    # [('/users/', '用户管理'), ('/roles/', '角色管理')]

    request.session['menu_permission_list'] = menu_permission_list
    2.menu
    menu_permission_list = request.session.get('menu_permission_list')

    base.html
    {% for item in menu_permission_list %}
    <p class="menu_btn"><a href="{{ item.0 }}">{{ item.1 }}</a></p>
    {% endfor %}

    可以实现,菜单显示!但是不行,为什么?

    因为模板继承,只继承样式,不继承数据!所有需要用到 自定义标签(inclusion_tag)

    3.自定义标签(inclusion_tag)
    rbac/templatetags/my_tags.py
    from django import template
    register = template.Library()

    @register.inclusion_tag('menu.html')
    def get_menu(request):
    # 获取当前用户,应该放到菜单栏的权限
    menu_permission_list = request.session.get('menu_permission_list')

    return {'menu_permission_list':menu_permission_list}

    menu.html:
    <div>
    {% for item in menu_permission_list %}
    <p class="menu_btn"><a href="{{ item.0 }}">{{ item.1 }}</a></p>
    {% endfor %}

    </div>

    base.html
    {% load my_tags %}
    <div class="menu">
    {% get_menu request %}
    </div>
    4.包...建在哪个App
    属于权限的就建在rbac的APP里,因为rpac最后是可插拔的组件!!

    users.html / roles.html / base.html / menu.html
    是和权限相关的,所以应该放在 rbac/templates/... 方便以后copy走!!

    djangod的render去渲染 .html 时,先到项目的 templates 下找,找不到,再到App下 templates 下找,
    最后找不到,才报错!!

    如果多个App的templates 下的.html重名怎么办? django 会根据注册的顺序显示!
    解决办法:
    项目/rbac/templates/rbac/xxx.html

    这时调用:
    return render(request, 'rbac/users.html', locals())

    注意:
    templates 或者 templatetag 注意多个app下面 的文件名 有可能都会重名!!
    办法:就是 eg:/rbac/templates/rbac/xxx.html 或者不起重名

    注意:
    如果 base.html 在项目下有,在App下有,先找项目下的,找不到才找App
    全局可以覆盖局部的!!

    六、效果

    用户的权限不同,

    显示的菜单栏,就不同

    按钮也不同

    七、路径自动添加

    知识点:路径自动添加问题:
    http://127.0.0.1:8010/users
    http://127.0.0.1:8010/users/

    浏览器发请求:
    django 发现之后,发了一个重定向的 url 加了一个 /
    所有才能匹配上:
    path('users/', views.users),

    如何让django不给浏览器发重定向,不加 /
    配置:
    APPEND_SLASH = False

    APPEND_SLASH = True # 默认为 True

    ajax 也是,django会默认的加 / 发重定向

    八、rbac_code

    原始版 

    https://github.com/alice-bj/rbac_pro_0

  • 相关阅读:
    数据库(六):多表关系
    数据库(五):约束关系
    数据库(四):数据类型
    数据库(三):存储引擎
    数据库(二):初识sql语句
    数据库(一):初识数据库
    番外:socketserver用法
    ~~并发编程(十六):协程理论~~
    ~~并发编程(十五):进程池线程池~~
    ~~并发编程(十四):Queue~~
  • 原文地址:https://www.cnblogs.com/alice-bj/p/9173372.html
Copyright © 2011-2022 走看看