当前位置: 首页 > article >正文

5. 实现一个中间件

 原文地址: 实现一个中间件 更多内容请关注:php代码框架

理解中间件

中间件(Middleware) 是一种在请求被路由到控制器方法之前或响应返回客户端之前执行的代码。它通常用于处理通用任务,如身份验证、日志记录、CORS 处理等。

在本例中,认证中间件将检查用户是否已登录,若未登录,则重定向到登录页面。


定义中间件接口

首先,定义一个中间件接口,确保所有中间件类都实现该接口。这有助于保持中间件的一致性和可扩展性。

步骤:

  1. 创建 Middleware 接口

    在 src/Middleware/ 目录下创建 Middleware.php 文件,并定义接口:

    <?php
    // src/Middleware/Middleware.phpnamespace MyFrameworkMiddleware;interface Middleware
    {/*** 处理请求** @param array $params 路由参数* @return bool 返回 `true` 继续执行,`false` 中止执行*/public function handle($params);
    }
    ?>

    说明:

    • handle 方法:接收路由参数,执行中间件逻辑。返回 true 表示请求可以继续执行,返回 false 则中止执行。


创建认证中间件

接下来,创建具体的认证中间件,实现上述接口。

步骤:

  1. 创建 AuthenticationMiddleware 类

    在 src/Middleware/ 目录下创建 AuthenticationMiddleware.php 文件:

    <?php
    // src/Middleware/AuthenticationMiddleware.phpnamespace MyFrameworkMiddleware;class AuthenticationMiddleware implements Middleware
    {public function handle($params){session_start();if (isset($_SESSION['user'])) {// 已认证,允许继续return true;} else {// 未认证,重定向到登录页面header('Location: /login');exit();}}
    }
    ?>

    说明:

    • 认证逻辑:检查 $_SESSION['user'] 是否存在,判断用户是否已登录。

    • 未认证处理:若未登录,使用 header 函数重定向到 /login 路由,并使用 exit() 终止脚本执行。


更新路由器以支持中间件

为了让路由器能够识别和执行中间件,需要对现有的 Router 类进行修改。

步骤:

  1. 扩展路由定义以包含中间件

    修改 Router 类,使其能够接受中间件参数。

  2. 更新 Router.php 文件

    在 src/Router.php 中进行如下修改:

    <?php
    // src/Router.phpnamespace MyFramework;use MyFrameworkMiddlewareMiddleware;class Router
    {private $routes = [];/*** 添加路由规则** @param string $method HTTP 方法(GET, POST, etc.)* @param string $uri 请求的 URI,支持参数,例如 '/user/{id}/profile'* @param string $action 控制器和方法,例如 'UsersController@showProfile'* @param array $middlewares 中间件列表*/public function add($method, $uri, $action, $middlewares = []){// 转换 URI 模式为正则表达式,并提取参数名称$pattern = preg_replace_callback('/{([a-zA-Z0-9_]+)(?)?}/', function ($matches) {$param = $matches[1];$optional = isset($matches[2]) && $matches[2] === '?';if ($optional) {return '(?P<' . $param . '>[a-zA-Z0-9_-]+)?';} else {return '(?P<' . $param . '>[a-zA-Z0-9_-]+)';}}, $uri);// 支持可选参数后的斜杠$pattern = preg_replace('#//+#', '/', $pattern);$pattern = '#^' . $pattern . '(/)?$#';// 提取参数名称$params = $this->extractParams($uri);$this->routes[] = ['method'      => strtoupper($method),'pattern'     => $pattern,'action'      => $action,'params'      => $params,'middlewares' => $middlewares];}/*** 分发请求到相应的控制器方法** @param string $requestMethod HTTP 方法* @param string $requestUri 请求的 URI*/public function dispatch($requestMethod, $requestUri){foreach ($this->routes as $route) {if ($route['method'] === strtoupper($requestMethod)) {if (preg_match($route['pattern'], $requestUri, $matches)) {// 提取命名参数$params = [];foreach ($route['params'] as $param) {if (isset($matches[$param]) && $matches[$param] !== '') {$params[$param] = $matches[$param];}}// 执行中间件foreach ($route['middlewares'] as $middlewareClass) {$middleware = new $middlewareClass();if ($middleware instanceof Middleware) {if (!$middleware->handle($params)) {// 中间件中止请求return;}}}$this->executeAction($route['action'], $params);return;}}}// 如果没有匹配的路由,返回 404$this->sendNotFound();}// ... 其他方法保持不变 ...
    }
    ?>

    说明:

    • 路由定义扩展:add 方法现在接受一个可选的 $middlewares 数组,用于指定该路由需要执行的中间件。

    • 中间件执行:在匹配路由后,遍历中间件列表,实例化并调用其 handle 方法。如果任何中间件返回 false,则中止请求处理。


应用中间件到路由

现在,指定哪些路由需要执行认证中间件。

步骤:

  1. 添加需要认证的路由

    在 index.php 中,定义需要认证的路由,并为其指定认证中间件。例如:

    <?php
    // index.phpuse MyFrameworkRouter;
    use MyFrameworkMiddlewareAuthenticationMiddleware;// ... 之前的代码 ...// 定义带有中间件的路由
    $router->add('GET', '/users', 'UsersController@list', [AuthenticationMiddleware::class]);
    $router->add('GET', '/user/{id}', 'UsersController@show', [AuthenticationMiddleware::class]);
    $router->add('GET', '/user/{id}/info/{info?}', 'UsersController@info', [AuthenticationMiddleware::class]);// 定义无需认证的登录路由
    $router->add('GET', '/login', 'AuthController@showLoginForm');
    $router->add('POST', '/login', 'AuthController@login');// 其他无需认证的路由
    $router->add('GET', '/', 'HomeController@index');
    $router->add('GET', '/about', 'HomeController@about');
    $router->add('GET', '/contact', 'HomeController@contact');
    $router->add('POST', '/submit', 'HomeController@submit');// 处理请求
    $router->dispatch($requestMethod, $requestUri);
    ?>

    说明:

    • 指定中间件:为需要认证的路由添加 [AuthenticationMiddleware::class],这将确保这些路由在执行控制器方法前进行身份验证。

    • 登录路由例外:登录相关的路由(如 /login)无需认证,因此不指定中间件。

  2. 创建认证控制器

    需要确保有一个 AuthController,包含 showLoginForm 和 login 方法,用于显示登录表单和处理登录逻辑。

    <?php
    // src/Controllers/AuthController.phpnamespace MyFrameworkControllers;use MyFrameworkController;
    use MonologLogger;
    use MonologHandlerStreamHandler;class AuthController extends Controller
    {private $logger;public function __construct(){$this->logger = new Logger('auth');$this->logger->pushHandler(new StreamHandler(__DIR__ . '/../../logs/app.log', Logger::DEBUG));}public function showLoginForm(){// 显示登录表单echo '<form method="POST" action="/login"><label>用户名: <input type="text" name="username" required></label><br><label>密码: <input type="password" name="password" required></label><br><button type="submit">登录</button></form>';}public function login(){// 处理登录逻辑$username = $_POST['username'] ?? '';$password = $_POST['password'] ?? '';// 简单示例:假设用户名和密码均为 'admin'if ($username === 'admin' && $password === 'admin') {session_start();$_SESSION['user'] = $username;$this->logger->info("用户登录成功", ['username' => $username]);header('Location: /users');exit();} else {$this->logger->warning("用户登录失败", ['username' => $username]);echo "<p>登录失败,请重试。</p>";$this->showLoginForm();}}public function logout(){session_start();unset($_SESSION['user']);session_destroy();header('Location: /login');exit();}
    }
    ?>

    说明:

    • 登录逻辑:在 login 方法中验证用户凭证,成功后将用户信息存入 $_SESSION,并重定向到受保护的路由。

    • 登出功能:可选,提供 logout 方法以允许用户登出。


处理认证逻辑

在认证中间件和认证控制器中正确处理用户会话和认证状态。

关键点:

  1. 会话管理

    • 在中间件和控制器中使用 session_start() 来管理会话。

    • 确保在每个需要访问 $_SESSION 的地方调用 session_start()

  2. 保护受限路由

    • 使用认证中间件保护所有需要认证的路由。

    • 确保登录路由不受认证中间件保护,以防止死循环重定向。

  3. 重定向逻辑

    • 未认证用户访问受限路由时,中间件将其重定向到登录页面。

    • 登录成功后,用户被重定向到之前尝试访问的受限页面,或默认的受限页面(如 /users)。


总结

通过以上步骤,在自定义的 PHP 框架中成功添加了身份认证的中间件,实现了以下功能:

  1. 中间件接口:定义了一个通用的中间件接口,确保所有中间件的一致性。

  2. 认证中间件:实现了 AuthenticationMiddleware,用于检查用户是否已登录。

  3. 路由器增强:更新了 Router 类,使其能够识别和执行路由中指定的中间件。

  4. 路由定义更新:在路由定义中为需要认证的路由指定了认证中间件,确保这些路由在访问前进行身份验证。

  5. 认证控制器:创建了 AuthController,包含显示登录表单和处理登录逻辑的方法。

扩展建议:

  1. 中间件堆栈:支持为路由指定多个中间件,按顺序执行。

  2. 全局中间件:实现全局中间件功能,对所有路由统一应用某些中间件(如日志记录)。

  3. 中间件参数:允许为中间件传递参数,以增强中间件的灵活性。

  4. 更复杂的认证机制:集成更安全的认证机制,如密码哈希、令牌验证等。

  5. 错误处理:增强中间件中的错误处理,提供更友好的错误信息和页面。

相关文章:

5. 实现一个中间件

原文地址: 实现一个中间件 更多内容请关注&#xff1a;php代码框架 理解中间件 中间件&#xff08;Middleware&#xff09; 是一种在请求被路由到控制器方法之前或响应返回客户端之前执行的代码。它通常用于处理通用任务&#xff0c;如身份验证、日志记录、CORS 处理等。 在…...

JVM 为什么不使用引用计数算法?——深入解析 GC 策略

在 Java 中&#xff0c;垃圾回收&#xff08;Garbage Collection, GC&#xff09;是一个至关重要的功能&#xff0c;它能够自动管理内存&#xff0c;回收不再使用的对象&#xff0c;从而防止内存泄漏。然而&#xff0c;在垃圾回收的实现上&#xff0c;JVM 并未采用引用计数算法…...

【HarmonyOS NEXT】EventHub和Emitter的使用场景与区别

一、EventHub是什么&#xff1f; 移动应用开发的同学应该比较了解EventHub&#xff0c;类似于EventBus。标准的事件广播通知&#xff0c;订阅&#xff0c;取消订阅的处理。EventHub模块提供了事件中心&#xff0c;提供订阅、取消订阅、触发事件的能力。 类似的框架工具有很多…...

01-系统编程

一、程序和进程的区别&#xff1a; window系统&#xff1a; 1、程序存储在硬盘中&#xff0c;文件格式为.exe后缀&#xff0c;静态的 2、进程运行在内存中&#xff0c;动态的 Linux系统 1、程序存储在硬盘中&#xff0c;文件格式为.ELF&#xff08;可执行的链接文件&#…...

Linux编译器gcc/g++使用完全指南:从编译原理到动静态链接

一、gcc/g基础认知 在Linux开发环境中&#xff0c;gcc和g是我们最常用的编译器工具&#xff1a; gcc&#xff1a;GNU C Compiler&#xff0c;专门用于编译C语言程序g&#xff1a;GNU C Compiler&#xff0c;用于编译C程序&#xff08;也可编译C语言&#xff09; &#x1f4cc…...

UMI-OCR Docker 部署

额外补充 Docker 0.前置条件 部署前&#xff0c;请检查主机的CPU是否具有AVX指令集 lscpu | grep avx 输出如下即可继续部署 Flags: ... avx ... avx2 ... 1.下载dockerfile wget https://raw.githubusercontent.com/hiroi-sora/Umi-OCR_runtime_linux/main/Do…...

26考研|数学分析:定积分及应用

这一部分作为数学分析的灵魂&#xff0c;在数学分析的计算中&#xff0c;绝大部分的问题都可以转换成定积分的计算问题&#xff0c;所以在这部分的学习中&#xff0c;一定要注意提升计算能力&#xff0c;除此之外&#xff0c;由积分引出的相关积分不等式也是分析的重点和难点&a…...

React Hooks使用方法:useState,useRef,useEffect,useReducer,useContext用法实战案例

react hooks介绍&#xff0c;包括了state&#xff0c;ref&#xff0c;effect&#xff0c;reducer&#xff0c;context等常见hooks&#xff0c;也包括forwardRef和createContext用法&#xff0c;下面看代码吧&#xff0c;我用的是js写的。每个hook都做了个案例。 // 使用state来…...

线程池详解:在SpringBoot中的最佳实践

线程池详解&#xff1a;在SpringBoot中的最佳实践 引言 在Java并发编程中&#xff0c;线程池是一种非常重要的资源管理工具&#xff0c;它允许我们在应用程序中有效地管理和重用线程&#xff0c;从而提高性能并降低资源消耗。特别是在SpringBoot等企业级应用中&#xff0c;正…...

扩展卡尔曼滤波

1.非线性系统的线性化 标准卡尔曼滤波 适用于线性化系统&#xff0c;扩展卡尔曼滤波 则扩展到了非线性系统&#xff0c;核心原理就是将非线性系统线性化&#xff0c;主要用的的知识点是 泰勒展开&#xff08;我另外一篇文章的链接&#xff09;&#xff0c;如下是泰勒展开的公式…...

AI作为学术评审专家有哪些优缺点?

大家好这里是AIWritePaper官方账号&#xff0c;官网&#x1f449;AIWritePaper论文完成初稿之后&#xff0c;一般情况下&#xff0c;宝子们还需要找专家给我们提出评审意见。找专家评审其实并不容易&#xff0c;即使对老师来说&#xff0c;找人评审论文也是一件苦活。我们这个时…...

微信小程序登录和获取手机号

目录 准备工作 实现流程 实现代码 公共部分 通过code获取openid等信息 解密手机号 扩展 不借助工具类实现解密 借助工具类获取access_token 准备工作 需要小程序账号&#xff08;可以去微信公众平台创建一个测试号或者正式号&#xff09; appid&#xff1a;小程序id …...

4.Matplotlib:基础绘图

一 直方图 1.如何构建直方图 将值的范围分段&#xff0c;将整个值的范围分成一系列间隔&#xff0c;然后计算每个间隔中有多少值。 2.直方图的适用场景 一般用横轴表示数据类型&#xff0c;纵轴表示分布情况。 直方图可以用于识别数据的分布模式和异常值&#xff0c;以及观察数…...

与Aspose.pdf类似的jar库分享

如果你在寻找类似于 Aspose.PDF 的 JAR 库&#xff0c;这些库通常用于处理 PDF 文档的创建、编辑、转换、合并等功能。以下是一些类似的 Java 库&#xff0c;它们提供 PDF 处理的功能&#xff0c;其中一些是收费的&#xff0c;但也有开源选项&#xff1a; 1. iText (iText PDF…...

VSCode 市场发现恶意扩展正在传播勒索软件!

在VSCode 市场中发现了两个隐藏着勒索软件的恶意扩展。其中一个于去年 10 月出现在微软商店&#xff0c;但很长时间没有引起注意。 这些是扩展ahban.shiba 和 ahban.cychelloworld&#xff0c;目前已从商店中删除。 此外&#xff0c;ahban.cychelloworld 扩展于 2024 年 10 月…...

工作流引擎Flowable介绍及SpringBoot整合使用实例

Flowable简介 Flowable 是一个轻量级的业务流程管理&#xff08;BPM&#xff09;和工作流引擎&#xff0c;基于 Activiti 项目发展而来&#xff0c;专注于提供高性能、可扩展的工作流解决方案。它主要用于企业级应用中的流程自动化、任务管理和审批流等场景。 Flowable 的核心…...

K8s证书--运维之最佳选择(K8s Certificate - the best Choice for Operation and Maintenance)

K8s证书--运维之最佳选择 No -Number- 01 一个月速通CKA 为了速通CKA&#xff0c;主要办了两件事情 1. 在官方的Killercoda上&#xff0c;练习CKA的题目。把命令敲熟悉。 // https://killercoda.com/killer-shell-ckad 2. 使用K3s在多台虚拟机上快速搭建了K8s集群&…...

MySQL 8.0.41源码目录深度解析:探索数据库内核的架构蓝图

文章目录 MySQL 8.0.41源码目录深度解析&#xff1a;探索数据库内核的架构蓝图一、MySQL 8.0.41 目录结构总览1.1 安装目录核心子目录1.2 数据目录关键组件 二、核心源码模块剖析2.1 SQL 引擎核心&#xff08;sql / 目录&#xff09;2.1.1 核心组件2.1.2 架构亮点 2.2 存储引擎…...

Leaflet.js+leaflet.heat实现热力图

Leaflet热力图 #mermaid-svg-I1zXN0OrNCBGKEWy {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-I1zXN0OrNCBGKEWy .error-icon{fill:#552222;}#mermaid-svg-I1zXN0OrNCBGKEWy .error-text{fill:#552222;stroke:#5522…...

通过git文件查看大模型下载链接的解决方案

大家好,我是爱编程的喵喵。双985硕士毕业,现担任全栈工程师一职,热衷于将数据思维应用到工作与生活中。从事机器学习以及相关的前后端开发工作。曾在阿里云、科大讯飞、CCF等比赛获得多次Top名次。现为CSDN博客专家、人工智能领域优质创作者。喜欢通过博客创作的方式对所学的…...

多源最短路:Floyd算法の暴力美学

多源最短路求解的是图中的任意两个节点之间的最短路。 前文我们已经讲过单源最短路&#xff0c;我们完全可以做n次单源最短路算法&#xff0c;求出任意两节点的最短距离。最快的堆优化版的 dijkstra 算法的时间复杂度为o&#xff08;m * logm&#xff09;&#xff0c;枚举n次时…...

初教六双机一飞冲天动作要领

初教六双机一飞冲天动作要领 初教六双机“一飞冲天”是典型的垂直爬升特技动作&#xff0c;要求双机以近乎垂直的姿态同步高速爬升&#xff0c;展现飞机的动力性能与编队协同能力。以下是该动作的详细技术解析与执行要点&#xff1a; 一、动作定义与特点 基本形态 双机以相同速…...

qtcore在docker容器中运行

FROM ubuntu # 设置时区环境变量 ENV TZAsia/Shanghai RUN echo "${TZ}" > /etc/timezone \ && ln -sf /usr/share/zoneinfo/${TZ} /etc/localtime \ && apt update \ && apt install -y tzdata \ && rm -rf /var/lib/apt…...

simpleITK - Setup - Pythonic Syntactic Sugar

Pythonic Syntactic Sugar Image Basics Notebook 非常简单&#xff0c;与 ITK 的 C 接口非常接近。 Sugar非常棒&#xff0c;它能让你精力充沛&#xff0c;更快地完成任务&#xff01;SimpleITK 也应用了大量Sugar来帮助更快地完成任务。 %matplotlib inline import matplo…...

【leetcode hot 100 215】数组中的第K个最大元素

解法一&#xff1a;维护最大最小值 -> 堆 -> k个元素的最小值堆 class Solution {public int findKthLargest(int[] nums, int k) {// 维护最大最小值 -> 堆 -> k个元素的最小值堆PriorityQueue<Integer> heap new PriorityQueue<>((n1, n2) -> n…...

下载vmware17

我用VMware10安装ubuntu24&#xff0c;死活不能成功&#xff0c;要么突然退出&#xff0c;要么装着装着&#xff0c;眼看完成&#xff0c;居然卡住不动&#xff0c;一查日志&#xff0c;提示光盘读取失败&#xff08;用的ISO文件&#xff0c;居然装模作样的说光驱读取失败&…...

德昂观点:如何看待MicroStrategy改名为Strategy?

2025年2月&#xff0c;纳斯达克上市公司MicroStrategy&#xff08;股票代码&#xff1a;MSTR&#xff09;宣布更名为“Strategy”&#xff0c;并同步启用全新品牌标识与橙色主视觉。这不仅是品牌形象的更新&#xff0c;更是公司战略方向的明确宣示。德昂作为MSTR中国区BI合作伙…...

嵌入式八股RTOS与Linux---网络系统篇

前言 关于计网的什么TCP三次握手 几层模型啊TCP报文啥的不在这里讲,会单独分成一个计算机网络模块   这里主要介绍介绍lwip和socket FreeRTOS下的网络接口–移植LWIP 实际上FreeRTOS并不自带网络接口,我们一般会通过移植lwip协议栈让FreeRTOS可以通过网络接口收发数据,具体可…...

Django 生成 ssl 安全证书,切换 https、wss协议(daphne 、nginx)

Django 普通 http 协议不够安全&#xff0c;无法支持连接本地摄像头&#xff08;虽然在本地 localhost 上能连&#xff09;&#xff0c;此时需要切换成 https 协议&#xff08;先提个醒&#xff0c;我这个方法最后失败了&#xff0c;不过对您应该也有帮助&#xff09; 目录 配置…...

告别Win10强制更新:永久关闭系统更新指南

你是否厌倦了Win10在开关机时的强制自动更新&#xff1f;无论你是在赶时间还是专注于工作&#xff0c;那突如其来的更新提示总是让人不胜其烦。屏幕上那句“正在更新&#xff0c;请勿关闭电源”的提示&#xff0c;仿佛是对你无奈的嘲笑。别担心&#xff0c;今天我将教你如何永久…...