zoukankan      html  css  js  c++  java
  • 权限组件(1):一级菜单

    一级菜单效果图

    权限表:

    from django.db import models
    
    
    class Permission(models.Model):
        """
        权限表
        """
        title = models.CharField(verbose_name='标题', max_length=32)
        url = models.CharField(verbose_name='含正则的URL', max_length=128)
    
        is_menu = models.BooleanField(verbose_name='是否可以做菜单', default=False)
        icon = models.CharField(verbose_name='图标', max_length=32, null=True, blank=True)
    
        def __str__(self):
            return self.title
    
    
    class Role(models.Model):
        """
        角色表
        """
        name = models.CharField(verbose_name='角色名称', max_length=32)
        permissions = models.ManyToManyField(verbose_name='所拥有的权限', to='Permission', blank=True)
    
    
    class UserInfo(models.Model):
        """
        用户表
        """
        name = 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)
    
        def __str__(self):
            return self.username

    一、 先登录,访问login

    业务app/views/accounts.py

    from django.shortcuts import render, redirect
    
    from web.forms.accounts import LoginForm
    from rbac import models
    from rbac.service.init_permission import init_permission
    
    
    def login(request):
        if request.method == 'GET':
            forms = LoginForm()
            return render(request, 'login.html', {'forms': forms})
    
        forms = LoginForm(data=request.POST)
        errors = forms.errors.get('__all__')
        if forms.is_valid():
            username = request.POST.get('name')
            password = request.POST.get('password')
            current_user = models.UserInfo.objects.filter(name=username, password=password).first()
    
            init_permission(current_user, request)
    
            return redirect('/customer/list/')
    
        context = {
            'forms': forms,
            'errors': errors
        }
        return render(request, 'login.html', context)

    业务app/forms/accounts.py  login的form表单验证。用户名和密码是否正确也在这里验证。

    from django import forms
    
    from rbac.models import UserInfo
    
    
    class BaseBootStrapForm(forms.ModelForm):
        def __init__(self, *args, **kwargs):
            super(BaseBootStrapForm, self).__init__(*args, **kwargs)
            for name, field in self.fields.items():
                field.widget.attrs['class'] = 'form-control'
    
    
    class LoginForm(BaseBootStrapForm):
        class Meta:
            model = UserInfo
    
            fields = ('name', 'password',)
    
            label = {
                'name': '用户名',
                'password': '密码',
            }
    
        def clean(self):
            input_username = self.cleaned_data.get('name')
            input_password = self.cleaned_data.get('password')
    
            if input_username and input_password:
                user_obj = UserInfo.objects.filter(name=input_username, password=input_password).first()
                print(user_obj)
                if not user_obj:
                    raise forms.ValidationError('用户名或密码错误')
                else:
                    return self.cleaned_data

    二、登录成功后初始化用户权限

    rbac/service/init_permission.py

    from django.conf import settings
    
    
    def init_permission(current_user, request):
        """
        用户权限的初始化
        :param current_user:  当前登录用户
        :param request:
        :return:
        """
        permission_queryset = current_user.roles.filter(permissions__isnull=False).values(
            'permissions__id',
            'permissions__title',
            'permissions__is_menu',
            'permissions__icon',
            'permissions__url'
        ).distinct()
    
        # 获取权限 + 菜单信息
    
        menu_list = []
        permission_list = []
        for item in permission_queryset:
            permission_list.append(item['permissions__url'])  # 权限
            if item['permissions__is_menu']:
                menu_info = {
                    'title': item['permissions__title'],
                    'icon': item['permissions__icon'],
                    'url': item['permissions__url'],
                }
                menu_list.append(menu_info)  # 菜单
    
        # 将权限信息和菜单信息放入到session中
        request.session[settings.PERMISSION_SESSION_KEY] = permission_list
        request.session[settings.MENU_SESSION_KEY] = menu_list

    settings.py 中关于权限的配置

    ...
    
    ########### 权限相关 ##############
    
    PERMISSION_SESSION_KEY = 'permission_url_list_key'
    WHITE_LIST = ['/login/', '/admin/.*']
    
    # 一级菜单
    MENU_SESSION_KEY = 'permission_menu_key'
    
    ...

     

    三、中间件验证权限

    rbac/middlewares/rbac.py

    import re
    
    from django.utils.deprecation import MiddlewareMixin
    from django.shortcuts import HttpResponse
    from django.conf import settings
    
    
    class RbacMiddleware(MiddlewareMixin):
        def process_request(self, request):
            white_list = settings.WHITE_LIST
            current_path = request.path_info
    
            for valid_url in white_list:
                if re.match(valid_url, current_path):
                    return None
    
            permission_list = request.session.get(settings.PERMISSION_SESSION_KEY)
    
            if not permission_list:
                return HttpResponse('请先登录')
    
            has_permission = False
    
            for url in permission_list:
                reg = '^%s$' % url
                if re.match(reg, current_path):
                    has_permission = True
                    break
    
            if not has_permission:
                return HttpResponse('未获取权限,请先获取权限')

    思路: 

    1. 先检查是不是在白名单里,是的话直接返回,就不往下运行了
    2. 获取session里面储存的权限,如果没有的话就说明没有登录,提示用户登录
    3. 检查当前url是否在session里,如果不是的话说明用户没有权限,提示用户先获取权限

    注册middleware

    MIDDLEWARE = [
        ...
        'rbac.middlewares.rbac.RbacMiddleware'
        ...
    ]    

    四、渲染到模板

    现在settings里注册自定义的标签templatetags

    INSTALLED_APPS = [
        'django.contrib.staticfiles', # 静态文件
        'rbac.templatetags', # 自定义标签
     ]

    rbac/templatetags/rbac.py

    import re
    
    from django.template import Library
    
    from django.conf import settings
    
    register = Library()
    
    
    @register.inclusion_tag('rbac/menu.html')
    def menu(request):
        """
        创建一级菜单
        :param request:
        :return:
        """
        menu_list = request.session[settings.MENU_SESSION_KEY]
        current_path = request.path
    
        for item in menu_list:
            reg = '^%s$' % item['url']
            if re.match(reg, current_path):
                item['class'] = 'active'
    
        # menu函数里面的返回值是返回到 rbac/templates/rbac/menu.html
        # menu函数里面的返回值也可以说是返回到inclusion_tag所指向的模版路径
        context = {
            'menu_list': menu_list,
        }
    
        return context

    思路:
    1. 在session中获取一级菜单列表
    2. 循环菜单,让菜单里的url,和当前用户点击的url进行匹配,如果匹配上的话给用户点击的url加上样式
    3. 最后返回一级菜单列表给模板用

    rbac/templates/rbac/menu.html

    <div class="static-menu">
        {% for menu in menu_list %}
            <a href="{{ menu.url }}" class="{{ menu.class }}">
                <span class="icon-wrap"><i class="fa {{ menu.icon }}"></i></span>{{ menu.title }}
            </a>
        {% endfor %}
    </div>

    在业务app/templates/layout.html引入rbac/templates/rbac/menu.html

    {% load staticfiles %}
    {% load rbac %} {# 程序去加载自定义变迁,系统会默认找到templatetags的文件下面的rbac模块  #}
    <!DOCTYPE html>
    <html lang="en">
    <head>
        .......
    </head>
    <body>
    <div class="pg-header">
        <div class="nav">
            <div class="logo-area left">
                <a href="#">
                    <img class="logo" src="{% static 'imgs/logo.svg' %}">
                    <span style="font-size: 18px;">路飞学城 </span>
                </a>
            </div>
    
            <div class="left-menu left">
                <a class="menu-item">资产管理</a>
                <a class="menu-item">用户信息</a>
                <a class="menu-item">路飞管理</a>
                <div class="menu-item">
                    <span>使用说明</span>
                    <i class="fa fa-caret-down" aria-hidden="true"></i>
                    <div class="more-info">
                        <a href="#" class="more-item">管他什么菜单</a>
                        <a href="#" class="more-item">实在是编不了</a>
                    </div>
                </div>
            </div>
    
            <div class="right-menu right clearfix">
    
                <div class="user-info right">
                    <a href="#" class="avatar">
                        <img class="img-circle" src="{% static 'imgs/default.png' %}">
                    </a>
    
                    <div class="more-info">
                        <a href="#" class="more-item">个人信息</a>
                        <a href="#" class="more-item">注销</a>
                    </div>
                </div>
    
                <a class="user-menu right">
                    消息
                    <i class="fa fa-commenting-o" aria-hidden="true"></i>
                    <span class="badge bg-success">2</span>
                </a>
    
                <a class="user-menu right">
                    通知
                    <i class="fa fa-envelope-o" aria-hidden="true"></i>
                    <span class="badge bg-success">2</span>
                </a>
    
                <a class="user-menu right">
                    任务
                    <i class="fa fa-bell-o" aria-hidden="true"></i>
                    <span class="badge bg-danger">4</span>
                </a>
            </div>
    
        </div>
    </div>
    <div class="pg-body">
        <div class="left-menu">
            <div class="menu-body">
                {% menu request %}
                {#   request 是传递给menu的参数              #}
                {#   menu 是templatetags 里面的函数              #}
            </div>
        </div>
        <div class="right-body">
            <div>
                <ol class="breadcrumb no-radius no-margin" style="border-bottom: 1px solid #ddd;">
    
                    <li><a href="#">首页</a></li>
                    <li class="active">客户管理</li>
    
                </ol>
            </div>
            {% block content %} {% endblock %}
        </div>
    </div>
    
    
    <script src="{% static 'js/jquery-3.3.1.min.js' %} "></script>
    <script src="{% static 'plugins/bootstrap/js/bootstrap.js' %} "></script>
    {% block js %} {% endblock %}
    </body>
    </html>
    </html>
  • 相关阅读:
    D3.js比例尺 定量比例尺 之 线性比例尺(v3版本)
    D3.js的基础部分之数组的处理 集合(Set)(v3版本)
    D3.js的基础部分之数组的处理 映射(Map)(v3版本)
    D3.js (v3)+react框架 基础部分之认识选择集和如何绘制一个矢量图
    D3.js的一些基础部分 (v3版本)
    Flask 教程 第九章:分页
    Flask 教程 第八章:粉丝
    Flask 教程 第七章:错误处理
    Flask 教程 第六章:个人主页和头像
    Flask 教程 第五章:用户登录
  • 原文地址:https://www.cnblogs.com/lshedward/p/10490108.html
Copyright © 2011-2022 走看看