Django 自定义中间件(IP限制频率、用户权限)

【一】用户权限

(1)要求

  • 不同的用户,有不同的操作权限
  • 比如
    • 超级管理员,拥有所有权限
    • 管理员可以增加、查询、修改
    • 普通用户只能查询

(2)逻辑梳理

  • 理论来说,这个权限需要保存在数据库中

    • 但是这里只做演示,在视图函数中定义用户的权限
  • 每个人的权限需要在登录以后才能获得

    • 所以需要创建一个登录界面

    • 在后端的登录逻辑中,根据登录的用户名进行权限查询

    • 由于中间件要进行权限处理,所以这里将权限保存在session中

      • request.session['permission'] = permission
        
  • 中间件逻辑

  • 由于不是所有路径都要限制用户访问

    • 所以需要白名单

    • white_permission_list = ['login', 'register']
      
  • 获取当前访问路径

    • visited_path = request.path
      
  • 由于每次都是直接进入的根路径

    • 所以将根路径也排除在外
  • 循环白名单

    • 使用正则匹配,在白名单的地址不进行权限要求
  • 获取用户可访问的地址

    • user_permission_list = request.session.get("permission")
      user_permission_list = user_permission_list if user_permission_list else []
      
  • 再次循环遍历可访问的地址

    • 使用正则匹配,可访问的地址通过
  • 到最后的就是没有权限的

    • 抛出信息

(3)代码

  • 中间件
import re
import time
from django.http import HttpResponse
from django.utils.deprecation import MiddlewareMixin

class PermissionMiddleWare(MiddlewareMixin):
    """
    限制用户权限
    """
    def process_request(self, request):
        white_permission_list = ['login', 'register']
        # 当前访问路径
        visited_path = request.path
        # 对根路径进行优先处理
        if visited_path == "/":
            return
        # 对白名单路径进行处理
        for white_permission in white_permission_list:
            # 使用正则进行匹配
            pattern = '/' + white_permission + ".*"
            pattern = re.compile(pattern=pattern)
            if re.search(pattern, visited_path):
                return
        # 获取登录用户权限
        user_permission_list = request.session.get("permission")
        user_permission_list = user_permission_list if user_permission_list else []
        for permission in user_permission_list:
            # 使用正则匹配
            pattern = '/' + permission + '.*'
            pattern = re.compile(pattern=pattern)
            if re.search(pattern, visited_path):
                return
        # 前面都没有通过,那就是没有权限
        return HttpResponse("你没有权限访问")

  • 前端
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>

<form action="" method="post">
    <p>username:<input type="text" name="username"></p>
    <p>password:<input type="password" name="password"></p>
    <button class="button">提交</button>
</form>
<br>
<div>
    <div><a href="{% url 'login' %}">登录</a></div>
    <div><a href="{% url 'register' %}">注册</a></div>
    <div><a href="{% url 'add_data' %}">添加</a></div>
    <div><a href="{% url 'delete_data' %}">删除</a></div>
    <div><a href="{% url 'revise_data' %}">修改</a></div>
    <div><a href="{% url 'search_data' %}">查询</a></div>
</div>
</body>
</html>
  • 视图层
from django.shortcuts import render, HttpResponse

user_permission_dict = {
    "super": ['add_data/', 'delete_data/', 'search_data/', 'revise_data/'],
    "admin": ['add_data/', 'search_data/', 'revise_data/'],
    "normal": ['search_data/'],
}

def login(request):
    if request.method == "POST":
        # 获取用户名
        username = request.POST.get("username")
        # 查找用户权限,正常应该取数据库中查找,这里简化了
        permission = user_permission_dict.get(username)
        permission = permission if permission else []
        # 保存权限
        request.session['permission'] = permission
        return HttpResponse("登录成功")
    return render(request, 'login.html', locals())

def register(request):
    return HttpResponse("注册界面")
def add_data(request):
    return HttpResponse("添加界面")
def delete_data(request):
    return HttpResponse("删除界面")
def revise_data(request):
    return HttpResponse("修改界面")
def search_data(request):
    return HttpResponse("查看界面")

  • 路由层
from django.contrib import admin
from django.urls import path
from app01 import views

urlpatterns = [
    path('admin/', admin.site.urls),
    path("", views.login, name='login'),
    path('register/', views.register, name='register'),
    path("add_data/", views.add_data, name='add_data'),
    path("delete_data/", views.delete_data, name='delete_data'),
    path("search_data/", views.search_data, name='search_data'),
    path("revise_data/", views.revise_data, name='revise_data'),
]

【二】IP限制访问频率

(1)要求

  • 同一个地址限定时间内访问超过指定次数会抛出异常

(2)思路

  • 要求是限制访问次数

    • 所以这个中间件需process_request方法
  • 首先需要获取到访问的IP地址

    • request.META.get("REMOTE_ADDR")
      
  • 然后获取访问路径

    • request.get_full_path()
      
  • 然后是当前时间的获取

    • time.time()
      
  • 对IP进行判断

    • 如果没有访问过,添加默认信息并保存
    • 如果访问过
      • 判断地址、判断时间
        • 如果时间超过或地址变了,刷新保存信息
        • 如果在时间范围内
          • 访问次数+1
          • 对访问次数进行判断
            • 超过限定次数,返回HttpResponse信息

(3)代码

import time
from django.http import HttpResponse
from django.utils.deprecation import MiddlewareMixin


class SearchMiddleWare(MiddlewareMixin):
    """
    对同一个地址在限定时间内访问超过指定次会抛出异常
    """
    # {'127.0.0.1': {'start_time': 1711525250.3461394, 'visits': 0, "full_path": "/"}}
    id_data_dict = {}
    # 最大访问次数
    visits_limit = 5
    # 时间间隔
    time_limit = 10

    def process_request(self, request):
        # 获取信息
        user_id = request.META.get("REMOTE_ADDR")
        full_path = request.get_full_path()
        now_time = time.time()
        # 当前IP第一次访问
        if user_id not in self.id_data_dict.keys():
            new_id_dict = {user_id: {"start_time": now_time, "visits": 0, "full_path": full_path}}
            self.id_data_dict.update(new_id_dict)
        else:
            id_data = self.id_data_dict.get(user_id)
            # 不是同一个地址也刷新
            # 超过10秒钟重新刷新
            condition = any(
                [full_path != id_data.get('full_path'),
                 (now_time - id_data.get("start_time")) >= self.time_limit])
            if condition:
                id_data['start_time'] = now_time
                id_data["visits"] = 0
                id_data['full_path'] = full_path
                self.id_data_dict.update({user_id: id_data})
            # 访问间隔时间在10秒以内
            if now_time - id_data.get("start_time") < self.time_limit:
                # 只要来了就+1
                id_data["visits"] += 1
                # 十秒内访问次数超过指定次数,
                if id_data['visits'] >= self.visits_limit:
                    return HttpResponse("访问频率过高,请稍后在尝试")

相关推荐

  1. Django 定义中间(IP限制频率用户权限)

    2024-03-28 07:34:01       46 阅读
  2. Django定义中间

    2024-03-28 07:34:01       40 阅读
  3. django校验token定义中间

    2024-03-28 07:34:01       51 阅读
  4. Django 里解决定义中间的问题

    2024-03-28 07:34:01       28 阅读
  5. 定义中间

    2024-03-28 07:34:01       47 阅读
  6. gin定义中间

    2024-03-28 07:34:01       29 阅读
  7. 速率限制中间AspNetCoreRateLimit

    2024-03-28 07:34:01       25 阅读

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-03-28 07:34:01       94 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-03-28 07:34:01       101 阅读
  3. 在Django里面运行非项目文件

    2024-03-28 07:34:01       82 阅读
  4. Python语言-面向对象

    2024-03-28 07:34:01       91 阅读

热门阅读

  1. 数据结构之搜素二叉树

    2024-03-28 07:34:01       39 阅读
  2. Xxe漏洞

    2024-03-28 07:34:01       38 阅读
  3. MySQL 查询优化与 EXPLAIN 关键字详解

    2024-03-28 07:34:01       44 阅读
  4. Serilog日志框架

    2024-03-28 07:34:01       41 阅读
  5. openGauss系统函数添加指导

    2024-03-28 07:34:01       36 阅读
  6. 浅聊openGauss逻辑架构

    2024-03-28 07:34:01       35 阅读
  7. SBA架构5G核心网

    2024-03-28 07:34:01       43 阅读
  8. Mysql实用SQL例子

    2024-03-28 07:34:01       42 阅读