• crm-3权限


    1.权限基本实现

      rbac: rbac基于角色的权限控制 ,权限本质就是url 

      权限表: url列表  角色表: 一个角色固定访问一些url的地址  用户表: 用户可以绑定角色 ,用户拥有了角色的权限

      生成表数量: url权限表 + 角色表 + 用户表 + 权限角色对多对关系表 + 角色用户多对多关系表

    2.rbac组件实现

      基于角色的权限控制创建一个组件实现复用

      1)创建权限表 + 角色表 + 用户表(因为之前项目有用户表 ,此表仅用作抽象类用于子类继承 ,建立映射但不做数据迁移)

    ####rbac/models
    from django.db import models
    
    
    # Create your models here.
    class Permisssion(models.Model):
        url = models.CharField('含正则url', max_length=128)
        title = models.CharField('权限标题', max_length=32, blank=True, null=True)
        is_menu = models.BooleanField('是否为菜单', default=False)
    
    
    class Role(models.Model):
        name = models.CharField('角色名称', max_length=32)
        permissions = models.ManyToManyField('Permisssion', verbose_name='角色拥有权限')
    
    
    class rbacuser(models.Model):
        name = models.CharField('用户名', max_length=32)
        password = models.CharField('密码', max_length=32)
        roles = models.ManyToManyField(Role, verbose_name='用户拥有角色')    #这里面不在使用反射获取指定多对多 ,使用类名即可
    
        # 用户表需要作为基类 让已有的user体系表去继承
        class Meta:
            abstract = True


    ####crm/models
    class User(rbacuser, models.Model):      #直接继承
    ......


    ####数据迁移

      2)权限的管理 ,使用admin系统对权限进行直观的管理

    ####rbac/admin
    from django.contrib import admin

    # Register your models here.
    from rbac import models
    from crm.models import User

    # 设置配置类对某张表中展示的表字段做页面修改查看
    class PermissionAdmin(admin.ModelAdmin):
    list_display = ['pk', 'title', 'url', 'is_menu']
    list_editable = ['url', 'is_menu']

    # 注册时该表引用配置类
    admin.site.register(models.Permisssion, PermissionAdmin)
    admin.site.register(models.Role)
    admin.site.register(User)

      3)权限的流程控制

        用户请求----wsgi----中间件----路由----view----返回数据

        中间件: 增加白名单url(用于获取session)   ,校验session权限 

        返回数据: 权限放入session中 

      4)用户登录功能 (白名单成员)

    ####urls
        url(r'^login/', views.login.as_view(), name='login')
    
    
    ###crm/view
    class login(View):
        def get(self, request):
            return render(request, 'login.html')
    
        def post(self, request):
            name = request.POST.get('username')
            password = request.POST.get('password')
            md5 = hashlib.md5()
            md5.update(password.encode('utf-8'))
            md5_pwd = md5.hexdigest()
            print(md5_pwd, name)
            obj = models.User.objects.filter(name=name, password=md5_pwd)
            print(obj)
            if obj:
                return redirect(reverse('crm:userlist'))
            return HttpResponse('账号密码错误')
    
    ####html
    {% load static %}
    <!doctype html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport"
              content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
        <meta http-equiv="X-UA-Compatible" content="ie=edge">
        <link rel="stylesheet" href="{% static 'plugins/bootstrap/css/bootstrap.css' %} "/>
        <link rel="stylesheet" href="{% static 'plugins/font-awesome/css/font-awesome.css' %} "/>
        <link rel="stylesheet" href="{% static 'css/commons.css' %} "/>
        <link rel="stylesheet" href="{% static 'css/nav.css' %} "/>
        <link rel="stylesheet" href="{% static 'css/menu.css' %} "/>
    
        <title>login</title>
    </head>
    <body>
    <form class="form-horizontal" method="post">
        {% csrf_token %}
        <div class="form-group">
            <label for="inputEmail3" class="col-sm-2 control-label">用户名</label>
            <div class="col-sm-10">
                <input type="text" class="form-control" id="inputEmail3" name="username">
            </div>
        </div>
        <div class="form-group">
            <label for="inputPassword3" class="col-sm-2 control-label">Password</label>
            <div class="col-sm-10">
                <input type="password" class="form-control" id="inputPassword3" name="password" placeholder="Password">
            </div>
        </div>
        <div class="form-group">
            <div class="col-sm-offset-2 col-sm-10">
                <div class="checkbox">
                    <label>
                        <input type="checkbox"> Remember me
                    </label>
                </div>
            </div>
        </div>
        <div class="form-group">
            <div class="col-sm-offset-2 col-sm-10">
                <button type="submit" class="btn btn-default">Sign in</button>
            </div>
        </div>
    </form>
    <script src="{% static 'js/jquery-3.3.1.min.js' %} "></script>
    <script src="{% static 'plugins/bootstrap/js/bootstrap.js' %} "></script>
    <script src="{% static 'js/menu.js' %} "></script>
    </body>
    </html>
    View Code

      5)白名单功能  

        settings中定义白名单 ,login算入白名单

        rbac应用中创建中间件rbac/middlewares/rbac.py ,使用process_request完成校验白名单放行! 

        settings注册中间件注意顺序

    ####rbac中间件
    from django.utils.deprecation import MiddlewareMixin
    from django.shortcuts import HttpResponse
    from untitled import settings
    import re
    
    
    class RbacMiddleWare(MiddlewareMixin):
        def process_request(self, request):
            url = request.path_info
            for i in settings.VALID_LIST:
                if re.match(i, url):
                    return
            return HttpResponse('不在白名单中呢!!')
    
    ####settings配置中间件
        'rbac.middlewares.rbac.RbacMiddleWare',

      6)权限实现访问控制

       

      login功能关键: 找到该用户的角色 ,并取出所有的权限去重, 放入session中

    ###crm/view 的login功能
    from django.shortcuts import render, redirect, reverse, HttpResponse
    from django.views import View
    from crm import models
    import hashlib
    
    
    # Create your views here.
    class login(View):
        def get(self, request):
            return render(request, 'login.html')
    
        def post(self, request):
            name = request.POST.get('username')
            password = request.POST.get('password')
            md5 = hashlib.md5()
            md5.update(password.encode('utf-8'))
            md5_pwd = md5.hexdigest()
            print(md5_pwd, name)
            obj = models.User.objects.filter(name=name, password=md5_pwd).first()
            if not obj:
                return HttpResponse('账号密码错误')
    
            # 如果用户登录成功!!此时取出该用户的权限放入session
            # 该用户的(角色名 权限名 权限url)放入permisssion__query中去重(角色定义url重叠)
            else:
                permission_query = obj.roles.all().filter(permissions__url__isnull=False).values(
                    # 'name',
                    'permissions__title',
                    'permissions__url',
                ).distinct()
                print(list(permission_query))
    
            # 将对象列表转换为列表 ! 并将用户权限存入session
            request.session['permissions'] = list(permission_query)
    
            # 此刻该用户登录成功
            request.session['is_login'] = True
    
            return redirect(reverse('crm:userlist'))

      中间件关键 : 判断白名单 --- 判断登录状态 --- 登录后不需权限访问的url --- 登录后需要权限访问的url(从login给的session中取出对应的权限校验一下)

    ####rbac/middlerware/rbac   中间件校验
    from django.utils.deprecation import MiddlewareMixin
    from django.shortcuts import HttpResponse
    from untitled import settings
    import re
    
    
    class RbacMiddleWare(MiddlewareMixin):
        def process_request(self, request):
            url = request.path_info
            # 白名单校验
            for i in settings.VALID_LIST:
                if re.match(i, url):
                    return
            # 登录状态校验
            is_login = request.session.get('is_login', None)
            if not is_login:
                return HttpResponse('未登录')
            # 已登录 判断url是否不需要权限访问
            for i in settings.NO_PERMISSION_LIST:
                if re.match(i, url):
                    return
            # 已登录 不在白名单 需要权限访问的url ,查看用户的权限
            permission_list = request.session['permissions']
            for i in permission_list:
                re_url = i['permissions__url']
                print(re_url)
                if re.match(re_url, url):
                    return
    
            return HttpResponse('无权访问!!')

    3.后端模板加工三种方法

      filter        #自定义管道过滤器 ,通过后端对变量做二次加工

      simple_tag     #展示执行函数的返回值

      inclusion_tag    #生成一小段代码 ,对比include而言能使用传参

      使用方法: 三种方法的使用流程基本一致 ,仅装饰器不同

        1)app下创建templatetags的py包

        2)包中创建py文件

        3)python文件写入

          from django import template    

          register = template.Library()

      filter 自定义加工变量(简单 ,但是参数唯一)

    ##rbac/templatetags/filter_test.py
    from django import template
    
    register = template.Library()
    
    # 因为前端使用只有一个参数(value|add_str:arg)
    @register.filter
    def add_str(value, arg):
        return '{}={}'.format(value, arg)
    
    ##html中引用  注意字符串需要加引号 ,也可以是后端内容
        {% load filter_test %}
        {{ 'ppp'|add_str:'lolo' }}

      simple_tag 显示执行函数的返回值 (简单 ,还可以传入任意数量参数)

    ##rbac/templatetags/filter_test.py
    from django import template
    
    register = template.Library()
    
    @register.simple_tag
    def add_test(*args, **kwargs):
        return '位置参数{} 关键字参数{}'.format('-'.join(args), '-'.join(kwargs.values()))
    
    
    ##html中应用 ,参数随便写
        {% load my_define %}
        {% add_test 'a' 'b' age='18' name='屈冠文' %}

      inclusion_tag 生成代码段 ,需要一个html文件存放代码段 ,在定义函数中引用 ,返回值放入html代码段 ,并在模板中引用函数执行(复杂 ,可以生成标签功能较好)

        1)准备html代码段 2)准备函数返回值给到代码段处理 3)模板中执行函数返回代码段内容

    ##准备代码片段 rbac/templates/menu.html
    {% for foo in num %}
        <li>foo</li>
    {% endfor %}
    
    
    ##返回值给到代码片段 rbac/templatetags/filter_test.py
    from django import template
    
    register = template.Library()
    
    @register.inclusion_tag('menu.html')
    def show_range(num):
        return {'num': range(num)}
    
    
    ##html中load引用执行函数展示返回值
    {% load my_define %}
    {% block menu_list %}
        {% show_range 10 %}
    {% endblock %}

    4. 根据权限实现一级菜单

      1)动态生成一级菜单

        login的登录函数 :

          登陆成功后从数据库拿出用户的所有权限 ,并将权限放入permission_dict字典 ,再将字典中权限是菜单的放入menu_list列表中

          将权限字典与菜单列表放入session中保存给用户

    class login(View):
        def get(self, request):
            return render(request, 'login.html')
    
        def post(self, request):
            name = request.POST.get('username')
            password = request.POST.get('password')
            md5 = hashlib.md5()
            md5.update(password.encode('utf-8'))
            md5_pwd = md5.hexdigest()
            print(md5_pwd, name)
            obj = models.User.objects.filter(name=name, password=md5_pwd).first()
            if not obj:
                return HttpResponse('账号密码错误')
    
            # 如果用户登录成功!!此时取出该用户的权限放入session
            # 该用户的(角色名 权限名 权限url)放入permisssion__query中去重(角色定义url重叠)
            else:
                permission_query = obj.roles.all().filter(permissions__url__isnull=False).values(
                    # 'name',
                    'permissions__title',
                    'permissions__url',
                    'permissions__is_menu',
                    'permissions__name',
                ).distinct()
                # print(permission_query)
    
            permission_dict = {}
            menu_list = []
    
            for i in permission_query:
                permission_dict[i['permissions__name']] = {'url': i['permissions__url']}
                if i['permissions__is_menu']:
                    menu_list.append({'title': i['permissions__title'], 'url': i['permissions__url']})
    
            # 将对象列表转换为列表 ! 并将用户权限存入session
            request.session[settings.PERMISSION_SESSION_KEY] = permission_dict
            print(permission_dict)
            request.session[settings.MENU_SESSION_KEY] = menu_list
            # 此刻该用户登录成功
            request.session['is_login'] = True
    
            return redirect(reverse('crm:deplist'))
    View Code

        rbac中间件:

          遇到需要权限才能访问的url ,去session中将权限字典的value取出匹配 ,匹配成功就返回None表示通过

    from django.utils.deprecation import MiddlewareMixin
    from django.shortcuts import HttpResponse
    from untitled import settings
    import re
    
    
    class RbacMiddleWare(MiddlewareMixin):
        def process_request(self, request):
            url = request.path_info
            # 白名单校验
            for i in settings.VALID_LIST:
                if re.match(i, url):
                    return
            # 登录状态校验
            is_login = request.session.get('is_login', None)
            if not is_login:
                return HttpResponse('未登录')
            # 已登录 判断url是否不需要权限访问
            for i in settings.NO_PERMISSION_LIST:
                if re.match(i, url):
                    return
            # 已登录 不在白名单 需要权限访问的url ,查看用户的权限
            permission_dict = request.session[settings.PERMISSION_SESSION_KEY]
            for i in permission_dict.values():
                re_url = '^'.format(i['url'])
                if re.match(re_url, url):
                    return
    
            return HttpResponse('无权访问!!')
    View Code

        /rbac/templatetags/my_define自定义代码段后台:

          使用inclusion_tag制作菜单代码段 ,函数接收模板中传过的request参数 ,取出request中的session的菜单列表, 对菜单列表中匹配的当前访问菜单添加active标记 ,将菜单列表返回给menu代码段

    @register.inclusion_tag('menu.html')
    def menu(request):
        url_now = request.path_info
        menu_list = request.session[settings.MENU_SESSION_KEY]
        for i in menu_list:
            # 重点标记当前访问的菜单
            if re.match('^{}'.format(i['url']), url_now):
                i['class'] = 'active'
        return {'menu_list': menu_list}
    View Code

        /rbac/template/menu.html自定义代码段前端:

          代码段接收到my_define的返回值进行生成代码段 ...

    {% for menu in menu_list %}
       <div class="static-menu">
        <a class="{{ menu.class }}" href="{{ menu.url }}"><span class="icon-wrap"><i class="fa fa-map-o" aria-hidden="true"></i></span>{{ menu.title }}</a>
        </div>
    {% endfor %}
    View Code

        /layout.html模板

          菜单是通用内容放在了 ,所以放在了母板中 ,母板中执行my_define ,并展示menu.html

                {% load my_define %}
                {% menu request %}
    View Code

      2)限制到按钮级别 ,展示部门中有新增的一个按钮 ,但是必须拥有add权限的用户才能显示使用 (后续edit也需要修改)

        原理: 通过模板中传过来的request对象与参数(url别名), 在permission_dict字典中判断如果有url别名返回true ,其中url别名是个关键点 ,url别名是专用去匹配前端的参数的 (前面构建数据格式已经将别名转为字典的key值)

    ####校验某个按钮权限my_define.py 
     @register.filter def has_permission(request, name): if name in request.session[settings.PERMISSION_SESSION_KEY]: return True

    ####母版文件中判断是否给该用户展示layout.html
    {% load my_define %}
    {% if request|has_permission:'dep_add' %}
    <a class="btn btn-sm btn-primary" href="{% url 'crm:depadd' %}"><span class="icon-wrap"><i
    class="fa fa-map-o"
    aria-hidden="true"></i></span>新增
    </a>
    {% endif %}

        

             

  • 相关阅读:
    linux系统编程程序员必备
    postgresql查询优化之GIN(Generalized Inverted Index)索引与全文检索
    postgresql并行执行
    lightdb for postgresql日志详解
    PostgreSQL 使用数组类型
    postgresql wal文件误删恢复
    汉语词性标注集
    postgresql----JSON类型、函数及全文检索的支持
    postgresql等待事件之wait_event为空解析
    lightdb for pg查看锁
  • 原文地址:https://www.cnblogs.com/quguanwen/p/11452672.html
Copyright © 2020-2023  润新知