Django【十七】权限管理与路径导航

摘要:
1.URL权限管理设计表1.设计表系统中有多少条路径;哪些用户使用它;用户在公司中的角色;为角色分配权限(哪些角色可以访问哪些路径);2.向表中添加数据并分配角色权限。3.成功登录,设置会话,并保存用户可以从会话中的表访问的URL。4.设置中间件进程_请求,设置白名单释放登录名和admin:释放所有以admin开头的路径#设置白名单发布信息
1、url权限管理

设计表

复制代码
1、设计表
    系统一共有多少个路径;
    有哪些用户使用;
    用户在公司的角色;
    对角色进行权限分配(什么样的角色可以访问什么样的路径);
2、往表中添加数据,分配角色权限
3、登录成功,设置session,并将该用户可以访问的url从表中取出保存在session中,
4、设置中间件rocess_request,
复制代码

设置白名单放行登录和admin:

以admin开头的所有路径都放行

复制代码
# 设置白名单放行
for i in ["/login/","/register/","/logout/","/code/","/admin/.*"]:
    # 请求的路径能够和i匹配
    # 匹配不到返回None
    print(request.path)
    ret = re.search(i,request.path)
    if ret:
       print(ret)
       return None
复制代码

登录认证:

# 登录认证,判断当前用户是否携带session来访问
if not request.user.username:
    return redirect("login")

权限认证:

session中的路径是用户可以访问的路径,

request.path是当前访问路径,与session中的路径进行匹配,search成功return None

中间件继续往下执行,search不到ret为None,如果当前访问的路径都不在这个列表中证明该用户没有访问这个路径的权限,返回对应的错误(权限不够,无法访问)

复制代码
# 权限认证
for item in request.session["permisson_list"]:
     res = "^%s$"%item
     ret = re.search(res,request.path)
     if ret:
        return None
return HttpResponse("您的权限不够不能访问该页面!!!")
复制代码
2、左侧菜单栏权限分配

动态显示左侧一级菜单栏

权限表模型

复制代码
# 权限表
class Permission(models.Model):
    title = models.CharField(max_length=32)
    url = models.CharField(max_length=128)
    is_menu = models.BooleanField(default=False, verbose_name='是否是菜单')
    icon = models.CharField(max_length=32, verbose_name='图标', null=True, blank=True)
    class Meta:
        verbose_name_plural="权限表"
        verbose_name = '权限表'
    def __str__(self):
        return self.title
复制代码

Django【十七】权限管理与路径导航第9张

admin显示:

展示的时候直接可以编辑:

Django【十七】权限管理与路径导航第10张

Django【十七】权限管理与路径导航第11张 admin文件中配置

显示表的名字:

Django【十七】权限管理与路径导航第12张

Django【十七】权限管理与路径导航第13张Django【十七】权限管理与路径导航第14张
# 权限表
class Permission(models.Model):
    title = models.CharField(max_length=32)
    url = models.CharField(max_length=128)
    is_menu = models.BooleanField(default=False, verbose_name='是否是菜单')
    icon = models.CharField(max_length=32, verbose_name='图标', null=True, blank=True)
    class Meta:
        verbose_name_plural="权限表"
        verbose_name = '权限表'
    def __str__(self):
        return self.title

model中的verbose_name_plural
model中的verbose_name_plural

具体流程:

复制代码
1、获取登录用户拥有的所有权限,用户菜单栏数据(用户权限的url中有哪些是菜单栏);
2、将菜单栏的数据注入到session中;
3、不管是公有客户还是私有客户菜单栏都是通过继承的模板,所以我们只需要在模板中动态生成菜单栏即可,哪些用户有哪些菜单栏;
4、为了加强代码的复用性,我们使用自定义标签来生成左侧菜单栏,,并给标签添加选中样式;
5、自定义标签把我们事先保存在session中的菜单栏数据取出来渲染到指定页面,这里我们用的inclusion_tag("result.html");
6、在后端给选中的标签添加样式,用request.path与我们取出的菜单栏数据的url进行匹配,匹配成功表示这个url就是我们点击的标签,对应的给它添加"class":"active";
复制代码

用户菜单栏数据注入:

复制代码
# 判断用户输入的用户名和密码与数据库中的是否匹配
user_obj = auth.authenticate(username=username, password=password)
if user_obj and str_code.upper()==code.upper():
    # 登录成功设置session
    auth.login(request, user_obj)
    # 获取该用户的所有权限并保存在session中,并去重
    permissions = models.Permission.objects.filter(role__userinfo__username=user_obj.username).distinct()
    # 将该用户可以访问的所有url和菜单栏数据都添加到session中
    permisson_list = []
    permisson_is_menu_list = []
    for item in permissions:
        # 用户权限
        permisson_list.append(item.url)
     # 如果路径是否是菜单栏 if item.is_menu: # 用户菜单栏数据(用户权限的url中有哪些是菜单栏) permisson_is_menu_list.append({ "title": item.title, "url": item.url, "icon":item.icon, }) request.session["permisson_list"] = permisson_list request.session["permisson_is_menu_list"] = permisson_is_menu_list print("permisson_list-->",permisson_list) print("permisson_is_menu_list-->",permisson_is_menu_list) # permisson_list--> ['/home/', '/customers/'] # permisson_is_menu_list--> [{'title': '首页', 'url': '/home/', 'icon': 'fa fa-link'}, {'title': '公共客户信息', 'url': '/customers/', 'icon': 'fa fa-link'}]
复制代码

自定义标签:

复制代码
from django import template
register = template.Library()
import re
@register.inclusion_tag("result.html")
def create_label(request):
    menu_list = request.session.get("permisson_is_menu_list")
    # 给点击的标签添加选中样式
    for item in menu_list:
        # 请求路径与当前用户的菜单栏数据中的url匹配成功说明这个路径就是当前用户点击的a标签发起的请求
        if re.match("^{}$".format(item["url"]),request.path):
            # match 匹配开头的,匹配失败返回None,匹配成功可通过group(0)返回匹配成功的字符串
            # setch  扫描整个字符串,匹配不到返回None
            item["class"]="active"
            break
    # 将菜单栏的数据交给result页面去渲染,选然后将结果当作组件返回给调用create_label的页面
    return {"menu_list":menu_list}
复制代码

inclusion_tag指定的result页面:

{% for foo in menu_list %}
     <li   ><a href="http://t.zoukankan.com/{{ foo.url }}" ><i class="{{ foo.icon }}"></i>
         <span>{{ foo.title }}</span></a></li>
{% endfor %}

模板中引入自定义标签:

复制代码
<ul   data-widget="tree">
    <li class="header">HEADER</li>
    <!-- Optionally, you can add icons to the links -->

        {% load mytag %}            <!--引用自定义标签-->
        {% create_label request %}  <!--调用自定义标签,并将request传过去-->

</ul>
复制代码
3、二级菜单

二级菜单和一级菜单不同的是:

Django【十七】权限管理与路径导航第23张

1、设计表结构添加数据

一级菜单与权限表一对多关联,权限表设置自关联

复制代码
# 菜单栏
class Menu(models.Model):
    title = models.CharField(max_length=32,verbose_name='一级菜单',null=True,blank=True)
    icon = models.CharField(max_length=32, verbose_name='图标', null=True, blank=True)

# 权限表
class Permission(models.Model):
    title = models.CharField(max_length=32)
    url = models.CharField(max_length=128)
    menu = models.ForeignKey("Menu",null=True)
    pid = models.ForeignKey("self",null=True)     # 自关联
    class Meta:
        verbose_name_plural="权限表"
        verbose_name = '权限表'
    def __str__(self):
        return self.title
复制代码

Django【十七】权限管理与路径导航第26张

Django【十七】权限管理与路径导航第27张

2、登陆成功后获取当前登录用户的所有权限,将用户所有可以访问的url和菜单栏数据保存在session中

# 获取该用户的所有权限并保存在session中,并去重
permissions = models.Permission.objects.filter(role__userinfo__username=user_obj.username).values("title","url","pk","pid","menu__title","menu__icon","menu_id").distinct()
# 将该用户可以访问的所有url和菜单栏数据都添加到session中
input_permission(request, permissions
Django【十七】权限管理与路径导航第28张Django【十七】权限管理与路径导航第29张
def input_permission(request, permissions):
    # 将该用户可以访问的所有url和菜单栏数据都添加到session中
    permisson_list = []
    permisson_is_menu_dict = {}
    for item in permissions:
        # 用户权限
        permisson_list.append({
            "pk":item["pk"],
            "url":item["url"],
            "pid":item["pid"],
        })
        # 判断是否是一级菜单
        if item["menu_id"]:
            # menu_id同属于一个 一级菜单(下一个a标签的menu_id也是当前的一级标签)
            if item["menu_id"] in permisson_is_menu_dict:
                permisson_is_menu_dict[item["menu_id"]]["children"].append({
                    "pk":item["pk"],"title": item["title"], "url": item["url"]
                })
            else:
                # 设计菜单栏数据结构(方便前端渲染)
                permisson_is_menu_dict[item["menu_id"]] = {
                    "title": item["menu__title"],
                    "icon": item["menu__icon"],
                    "children": [
                        { "pk":item["pk"],"title": item["title"], "url": item["url"]},
                    ]
                }

    request.session["permisson_list"] = permisson_list
    request.session["permisson_is_menu_dict"] = permisson_is_menu_dict
View Code

中间件取提取权限列表进行验证,并将当前访问路径的pid封装在request中

Django【十七】权限管理与路径导航第30张Django【十七】权限管理与路径导航第31张
import re
from django.shortcuts import HttpResponse,redirect,render
from django.utils.deprecation import MiddlewareMixin

class PermissonMiddleware(MiddlewareMixin):
    def process_request(self,request):
        # 设置白名单放行
        for i in ["/login/","/register/","/logout/","/code/","/admin/.*"]:
            # 请求的路径能够和i匹配
            # 匹配不到返回None
            ret = re.search(i,request.path)
            if ret:
                return None
        # 登录认证,判断当前用户是否携带session来访问
        if not request.user.username:
            return redirect("login")

        # 权限认证
        for item in request.session["permisson_list"]:
            res = "^%s$"%item["url"]
            ret = re.search(res,request.path)
            if ret:
                # 当前访问路径的pid 给request对象动态添加了个属性
                request.show_id = item["pid"]
                return None
        return HttpResponse("您的权限不够不能访问该页面!!!")
View Code

3、菜单栏的数据我们通过自定义标签来渲染

根据我们构建的数据格式在前端进行渲染

复制代码
    '1': {
        'title': '信息管理',
        'icon': 'fa fa-link',
        'children': [{
            'pk': 5,
            'title': '首页',
            'url': '/home/'
        }, {
            'pk': 6,
            'title': '公共客户信息',
            'url': '/customers/'
        }, {
            'pk': 7,
            'title': '我的客户',
            'url': '/my_customers/'
        }]
    },
复制代码

自定义标签

复制代码
from django import template
register = template.Library()

@register.inclusion_tag("result.html")
def create_label(request):
    menu_dict = request.session.get("permisson_is_menu_dict")
    for key, item in menu_dict.items():
        # 默认给二级标签添加隐藏样式
        item["class"] = "hide"
        for child in item["children"]:
            # if re.match("^{}$".format(child["url"]), request.path):
            # 当前访问路径的pid如果等于它二级菜单的id证明他俩是属于同一个一级菜单,
            # 如果是属于同一个一级菜单,我们就让这个二级菜单一直显示,并给它添加选中样式;
            if request.show_id == child["pk"]:
                item["class"] = ""
                child["class"] = "active"
                break
    return  {"menu_dict" :menu_dict }
复制代码

Django【十七】权限管理与路径导航第36张

我们通过权限表自关联的方式来区分他们的从属关系;

bug:通过正则匹配无法确定从属关系

Django【十七】权限管理与路径导航第37张

 Django【十七】权限管理与路径导航第38张

复制代码
那么我们为什么要区分他们的从属关系呢:
        默认二级标签是隐藏状态的,我们为了在点击我的客户或当前一级标签的其他二级标签时都是展开的状态,我们通过正则当前请求的路径request.path与二级菜单进行匹配,匹配成功证明当前用户请求的url在二级菜单中,我们设置它父级标签的class属性值为空,让其展开;
    
        当我们点击我的客户中的其他功能时,因为这些功能的路径都不在二级菜单中,所以无法通过正则的方式进行匹配,也就是说我们在点击这些功能时左侧的菜单栏会隐藏起来,为了避免这种bug出现我们扩展了权限表添加了pid字段,并通过自关联的方式解决了bug
复制代码

当前请求路径的pid与菜单栏id进行判断:

Django【十七】权限管理与路径导航第41张

result.html生成二级标签

复制代码
<div class="multi-menu">
    {% for item in menu_dict.values %}
    <div class="item">
        <div class="title">
            <i class="{{ item.icon }}"></i>{{ item.title }}
        </div>
        <div class="body {{ item.class }}">
        {% for child in item.children %}
            <div  class="body">
                <a href="http://t.zoukankan.com/{{ child.url }}" class="{{ child.class}}">{{ child.title }}</a>
            </div>
        {% endfor %}
        </div>
    </div>
    {% endfor %}
</div>
复制代码
4、按钮权限分配

用户有没有这个按钮就看看他有没有这个按钮的权限

通过过滤器判断请求的url值否在权限列表里

{% load mytag %}
{% if "/add_customers/"|haspermission:request %}
      <a href="http://t.zoukankan.com/{% url "add_customers" %}" class="btn btn-primary pull-right">添加</a>
{% endif %}
复制代码
@register.filter
# 分配按钮权限
def haspermission(base_url,request):
    for item in request.session["permisson_list"]:
        ret = "^%s$"%item["url"]
        res = re.search(ret,base_url)
        if res:
            return True
    return False
复制代码

通过过滤器判断用户权限的url中有没有该按钮的url,有就说明该用户有这个按钮的权限,让其显示;反之隐藏

复制代码
{% if "/edit_customers/1/"|haspermission:request or "/delete_customers/1/"|haspermission:request %}
    <td>
        {% if "/edit_customers/1/"|haspermission:request %}
             <a href="http://t.zoukankan.com/{% url "edit_customers" customers.pk %}"
           class="btn btn-primary btn-sm">编辑</a>
        {% endif %}
        {% if "/delete_customers/1/"|haspermission:request %}
            <a href="http://t.zoukankan.com/{% url "delete_customers" customers.pk %}"
           class="btn btn-danger btn-sm">删除</a>
        {% endif %}

    </td>
{% endif %}
复制代码
5、路径导航

页面效果:

Django【十七】权限管理与路径导航第48张

点击我的客户页面中的其他功能:基于我的客户后面添加

Django【十七】权限管理与路径导航第49张

1、首页是默认的,无论点击哪个菜单路径导航第一个都是首页;
2、点击左侧菜单路径导航显示对应的名称,
3、点击左侧菜单栏页面中对应的功能时路径导航基于菜单栏的路径继续往后添加;

实现思路:

我们只需要判断请求的路径是不是二级菜单:
如果是二级菜单取出对应的路径title和url在前端直接渲染即可;
如果不是二级菜单那么就是二级菜单的功能请求,我们取出功能请求的title和url并需要根据已知条件查出它父级菜单的title和url,在前端渲染时放在自己的前面;

代码:

每次请求都会走一遍中间件,权限认证通过之后判断是二级菜单还是二级菜单中的功能请求;

Django【十七】权限管理与路径导航第50张 在中间件中,权限认证通过之后

 用bootstrap给最后一层添加灰色样式

复制代码
<ol class="breadcrumb">
    {% for foo in  request.path_navigation %}
        {% if forloop.last %}
            <li class="active">{{ foo.title }}</li>
        {% else %}
            <li><a href="http://t.zoukankan.com/{{ foo.url }}">{{ foo.title }}</a></li>
        {% endif %}

    {% endfor %}

</ol>
复制代码

免责声明:文章转载自《Django【十七】权限管理与路径导航》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇前端也能玩的图片隐写术Linux模拟网络延迟、丢包等下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

.net读取Windows登录用户信息(downmoon)

前天,在CodeProject看到一篇文章http://www.codeproject.com/KB/system/LSAEnumUserSessions.aspx 如何读取windows 当前登录用户的状态信息。 主要代码分享如下: 一:导入dll Code /**//******************************************...

python_18(Django基础)

第1章 web框架的本质 1.1 socket 1.2 空格后面是主体内容 1.3 HTTP协议 1.3.1 响应流程 1.4 HTTP请求方法 1.5 HTTP工作原理 1.6 URL 1.7 HTTP请求格式 1.8 HTTP响应格式 1.9 根据不同的路径返回不同内容 1.10 进阶版 1.11 根据访问的路径返回不同的动态页面 第2章 Django...

使用COOKIE实现登录 VS 使用SESSION实现登录

注:本文使用的代码基于PHP,其他语言逻辑同理。 一:使用COOKIE实现登录验证 使用cookie实现登录的方式,主要通过一些单向的加密信息进行验证。比如admin用户登录了之后,服务端生成一个cookie值:admin_1533006028_ bbf2c2b1ec5cfb62d0a30438d8d0305c ,这个cookie值包含用户名,cookie...

Django组件-forms组件

form组件和 ModelForm 和auth模块 和中介模型  form组件 form组件出现的原因 当我们用普通的form表单提交时会刷新页面,如果这个我们表单中的某项填错了,刷新后我们正确的选项也没有了. 传统的form表单需要我们自己亲自校验每一项.工作量太大 Django 中form组件的2大功能:       1 验证(在前端页面显示我们...

Javaweb容器的四种作用域

几乎所有web应用容器都提供了四种类似Map的结构:application session request page,Jsp或者Servlet通过向着这四个对象放入数据,从而实现Jsp和Servlet之间数据的共享。 application:整个应用  对应servlet中ServletContext session:会话    对应servlet中Htt...

DRF基础操作流程

Django Rest_Framework 核心思想: 缩减编写api接口的代码 ——>DRF Django REST framework是一个建立在Django基础之上的Web 应用开发框架,可以快速的开发REST API接口应用。在REST framework中,提供了序列化器serializers的定义,可以帮助我们简化序列化与反序列化的过程,...