Django——权限组件(中间件判断用户权限--URL初级)

权限

根据URL进行限制用户可以访问的资源

项目与应用的关系

项目可包含多个应用
应用可包含在多个项目中
RBAC:基于权限的管理系统

项目

先创建一个Django项目

Model

from django.db import models


class UserInfo(models.Model):
    name = models.CharField(max_length=32)
    pwd = models.CharField(max_length=32,default=123)
    email = models.EmailField()
    roles = models.ManyToManyField(to="Role")

    def __str__(self):
        return self.name


class Role(models.Model):
    title =models.CharField(max_length=32)
    permissions = models.ManyToManyField(to="Permission")

    def __str__(self):
        return self.title


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

    def __str__(self):
        return self.title

前端模板

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="x-ua-compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Title</title>
</head>
<body>
<form action="/login/" method="post">
    {% csrf_token %}
    <p>用户名<input type="text" name="user"></p>
    <p>密码<input type="password" name="pwd"></p>
    <p><input type="submit"  value="登录"></p>

</form>
</body>
</html>

URL

urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^login/', views.login),
    url(r'^users/', views.user_list),
    url(r'^orders/', views.role_list),
]

后端

from django.shortcuts import render, HttpResponse, redirect
from rbac.models import UserInfo, Role, Permission


def login(request):
    if request.method == "GET":
        return render(request, "login.html")
    if request.method == "POST":
        username = request.POST.get("user")
        pwd = request.POST.get("pwd")
        user = UserInfo.objects.filter(name=username, pwd=pwd).first()
        if user:
            request.session["user_id"] = user.pk
            permission_list = user.roles.all().values("permissions__url", "permissions__title").distinct()
            temp = []
            for per_url in permission_list:
                temp.append(per_url["permissions__url"])
            request.session["permissions_list"] = temp
            print(temp)
            return HttpResponse("OK")
        else:
            return redirect('/login/')


def user_list(request):
    return HttpResponse("用户列表")


def role_list(request):
    return HttpResponse("订单列表")

后端有很多的视图函数,如果编写装饰器进行判断用户是否有权限访问,有三十个视图函数,就需要在三十个视图函数上添加装饰器函数,因此装饰器的方法不太妥当,取而代之的是中间件的方法

from django.utils.deprecation import MiddlewareMixin  #注意
from django.shortcuts import render,redirect, HttpResponse
from rbac.models import UserInfo
import re  #注意

class M1(MiddlewareMixin):
    def process_request(self,request):
        current_path = request.path_info
        permission_list = request.session.get("permissions_list")
        print(permission_list)
        valid_menu = ["/login/","/reg/","/admin/.*"]  # 如果不设置白名单,admin的url也会被判为无权限,而且不需要验证的函数少, 先设置白名单,
                                        # 如果用户输入的url在白名单中就会return None
        for valid_url  in valid_menu:
            ret = re.match(valid_url,current_path)   #注意
            if ret:
                return None
        if not permission_list:
            return None
        Flage = False
        for per_url in permission_list:
            re_macth = re.match(per_url,current_path)
            if re_macth:
                Flage = True
                break
        if not Flage:
            return HttpResponse("无权限")

再次解耦后端

在rabc应用之service包intiale 模块中创建一个inital_session函数,登录后处理session

from rbac.service.initial import inital_session

def login(request):
    if request.method == "GET":
        return render(request, "login.html")
    if request.method == "POST":
        username = request.POST.get("user")
        pwd = request.POST.get("pwd")
        user = UserInfo.objects.filter(name=username, pwd=pwd).first()
        if user:
            inital_session(request,user)
            return HttpResponse("OK")
        else:
            return redirect('/login/')
url_filter  模块下的代码
def inital_session(request,user):
    request.session["user_id"] = user.pk
    permission_list = user.roles.all().values("permissions__url", "permissions__title").distinct()
    temp = []
    for per_url in permission_list:
        temp.append(per_url["permissions__url"])
    request.session["permissions_list"] = temp

目录结构如下图

创建中间价的步骤

  1、在项目中创建一个应用application,自己命名至于为什么?这是前面提到的:“一个应用可以包含在多个项目中”,方便以后的使用

  2、在项目中创建一个文件夹service,

  3、在service 中创建一个py文件,存放自己中间件类

  4、创建一个类,必须继承  MiddlewareMixin

  5、该类中必须有一个函数,process_request

  6、在该文件下面创建一个inital_session 的模块,处理登录后,session

做好以上步骤,效果如下图

 上面的介绍如何使用中间件控制用户的访问那个函数,下面介绍,根据用户的角色展示菜单

二级菜单

用户登录成功,在cookie中写入用户的权限

import re


def inital_session(request, user):
    request.session["user_id"] = user.pk
    permission_info = user.roles.all().values(
        "permissions__url",  # 权限url
        "permissions__code",  #
        "permissions__title",
        "permissions__id",
        "permissions__permission_group_id",
        "permissions__parent",
        "permissions__parent_id",
        "permissions__permission_group__menu__caption",
        "permissions__permission_group__menu__id",

    ).distinct()
    print(permission_info)
    # 设置用户权限
    dic = {}
    for per_info in permission_info:
        gid = per_info["permissions__permission_group_id"]
        if gid not in dic:
            dic[gid] = {
                "urls": [per_info["permissions__url"]],
                "codes": [per_info["permissions__code"]]
            }
        else:
            dic[gid]["urls"].append(per_info["permissions__url"])
            dic[gid]["codes"].append(per_info["permissions__code"])
    request.session["permissions_dict"] = dic

    ## 设置用户的菜单
    permission_list = []
    for permission_item in permission_info:
       temp = {
           "id":permission_item["permissions__id"],
           "title":permission_item["permissions__title"],
           "url":permission_item["permissions__url"],
           "pid":permission_item["permissions__parent_id"],
           "menu_name":permission_item["permissions__permission_group__menu__caption"],
           "menu_id":permission_item['permissions__permission_group__menu__id'],
       }
       permission_list.append(temp)

    request.session["permission_list"] = permission_list
View Code

由于菜单是通用,每个函数都是需要处理菜单的逻辑,所有单独拿出来进行创建一个处理菜单的模块,使用@register.inclusion_tag 标签。

自定义register.inclusion_tag标签

from django import template

register=template.Library()

@register.inclusion_tag("menu.html")
def get_menu(request):
    permission_list = request.session["permission_list"]

    #############temp_dict:存储所有放到菜单栏中的权限
    temp_dict = {}
    print(permission_list)
    for item in permission_list:
        pid = item["pid"]
        if not pid:
            item["active"] = False
            temp_dict[item["id"]] = item




    #######将需要标中的active设置True
    # print(permission_list)
    current_path = request.path_info
    import re
    for item in permission_list:
        pid = item["pid"]
        url = "^%s$" % item["url"]
        if re.match(url, current_path):
            if pid:
                temp_dict[pid]["active"] = True
            else:
                item["active"] = True

    ########将temp_dict转换为最终的menu_dict的数据格式

    menu_dict = {}
    for item in temp_dict.values():

        if item["menu_id"] in menu_dict:

            temp = {"title": item["title"], "url": item["url"], "active": item["active"]},
            menu_dict[item["menu_id"]]["children"].append(temp)

            if item["active"]:
                menu_dict[item["menu_id"]]["active"] = True
        else:

            menu_dict[item["menu_id"]] = {

                "title": item["menu_name"],
                "active": item["active"],
                "children": [
                    {"title": item["title"], "url": item["url"], "active": item["active"]},
                ]

            }
    print(menu_dict)

    return {"menu_dict":menu_dict}
inclusion_tag标签
def m1(request):
    # # menu_dict = {
    # #     1: {
    # #         "title": "菜单一",
    # #         "active": False,
    # #         "children": [
    # #             {"title": "添加用户", "url": "xxxxxxxxxxx", "active": False},
    # #             {"title": "查看用户", "url": "xxxxxxxxxxx", "active": False},
    # #
    # #         ]},
    # #
    # #     2: {
    # #         "title": "菜单二",
    # #         "active": True,
    # #         "children": [
    # #             {"title": "添加用户", "url": "xxxxxxxxxxx", "active": True},
    # #             {"title": "查看用户", "url": "xxxxxxxxxxx", "active": True},
    # #
    # #         ]
    # #
    # #     }}
    #
    premission_list = request.session["permission_list"]
    print(premission_list)
    #存储放到菜单栏中的权限
    temp_dict = {}
    for item in premission_list:
        if not item["pid"]:
            item["active"] = False  #添加到菜单栏时,添加一个是否展开的标志
            temp_dict[item["id"]]= item

    #将需要标中的active设置为True
    current_path = request.path_info
    import re
    for item in premission_list:
        pid = item["pid"]
        url = "%s$"%item["url"]
        if re.match(url,current_path):
            if pid:                     #判断是不是二级菜单,如果是,就会把该菜单上一级设置为Ture
                temp_dict[pid]["active"]=True
            else:
                item["avtive"] = True   #注意此时的item 和temp_dict 的数据同一条数据,这里修改了True,temp_dict 也会改为True
    print(temp_dict)
    #将数据最终构造成最终的menu_dict数据

    menu_dict = {}
    for item in temp_dict.values():
        if item["menu_id"] in menu_dict:
            temp = {"title":item["title","url":item["url"],"active":item["avtive"]]} #定义一个自己的字典结构体
            menu_dict[item["menu_id"]]["children"].append(temp) #把菜单添加到一级菜单中
            if item["active"] == True:   #如果二级菜单是展开的,那么一级菜单也是展开的
                menu_dict[item["menu_id"]]["active"] = True
        else:
            menu_dict[item["menu_id"]] = {
                "title":item["menu_name"],
                "active":False,
                "children":[
                    {"title":item["title"],"url":item["url"],"active":item["active"],}
                ]
            }
    print(menu_dict)





    return render(request, "m1.html")
注释*函数版本

前端页面

{% load my_tags %}
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Title</title>
    <link rel="stylesheet" href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css">
    <style>
        .header {
             100%;
            height: 50px;
            background-color: #336699;
        }

        .menu, .content {
            float: left;
        }

        .menu {
             200px;
            height: 600px;
            background-color: darkgray;
        }

        .hide {
            display: none;
        }

        .menu .title {
            font-size: 16px;
            color: #336699 !important;
            margin: 20px 0;
        }

        .con a {
            margin-left: 30px;
            color: white;
        }

        .active {
            color: red !important;
        }
    </style>
</head>
<body>

<div class="header"></div>

<div class="box">
    {% mul 1 2 %}
   {% get_menu request %}



    <div class="content">
        {% block con %}

        {% endblock %}
    </div>

</div>


</body>
</html>
View Code
原文地址:https://www.cnblogs.com/huyangblog/p/8508977.html