ThinkPHP 8中间件练手实录:一个后台登录过滤器是怎么写出来并跑通的

2026-08-20 0 762

刚用TP8的时候,我对“中间件”三个字有点发怵,总觉得是什么高大上的东西。直到有一天要做一个后台的登录校验,我才发现中间件就像一扇门,请求进来先过门,你的登录验证逻辑写在门里,没通过就直接拦在外面。不用在每个控制器里重复写判断,省事太多了。

中间件在TP8里长什么样

简单说,中间件就是一个类,里面有一个 handle 方法。系统在调用控制器之前会先调用这个 handle 方法,你拿到的参数是请求对象 $request 和一个闭包 $next。如果你允许请求继续往下走,就调用 $next($request),否则就返回一个响应(比如重定向或错误提示)。

<?php
namespace appmiddleware;

use Closure;
use thinkRequest;
use thinkResponse;

class AuthCheck
{
    public function handle(Request $request, Closure $next)
    {
        // 这里写你的校验逻辑
        if (session('admin_id')) {
            return $next($request);
        } else {
            return redirect('/admin/login')->with('error', '请先登录');
        }
    }
}

创建中间件

TP8里你可以通过命令行快速生成中间件类。在项目根目录执行:

php think make:middleware AuthCheck

它会自动在 app/middleware 目录下生成一个 AuthCheck.php 文件。如果你更喜欢手动创建,那就在该目录下新建对应的类文件,一样能用。

注册中间件

中间件可以在三个地方使用:全局、路由、控制器。先说最简单的全局注册:在 app/middleware.php 里把中间件加到数组里。

// app/middleware.php
return [
    appmiddlewareAuthCheck::class
];

这样一来,所有请求都会经过 AuthCheck,不管你是访问后台还是首页。不过一般我不会把登录校验全部注册成全局,因为有些页面(如登录页本身)并不需要登录。

更常用的是给路由分组注册。假设你的后台所有路由都在 route/admin.php 中定义,你可以这样写:

// route/admin.php
use thinkfacadeRoute;

Route::group('admin', function () {
    Route::get('dashboard', 'admin.dashboard/index');
    Route::get('order/list', 'admin.order/list');
    Route::get('user/delete', 'admin.user/delete');
})->middleware(appmiddlewareAuthCheck::class);

使用->middleware()方法,就把这个组里的所有路由都挂上了登录校验。

中间件的执行顺序

如果你注册了多个中间件,它们会按照注册顺序依次执行,就像洋葱一样。全局中间件先跑,然后是路由中间件,最后是控制器中间件。每个中间件都可以决定是否放行到下一层。我一般把登录校验放在路由中间件,因为它只针对后台的URL。

实战:完成一个后台登录过滤

下面完整的写一套登录验证,包括中间件和登录控制器。假设后台所有的控制器都放在 appcontrolleradmin 命名空间下。

第一步:创建登录控制器(省略M层)

<?php
namespace appcontrolleradmin;

use thinkController;
use thinkfacadeDb;
use thinkfacadeSession;

class Auth extends Controller
{
    // 显示登录页
    public function login()
    {
        return view('login');
    }

    // 处理登录
    public function doLogin()
    {
        $username = input('post.username');
        $password = input('post.password');

        $user = Db::name('admin')->where('username', $username)->find();
        if ($user && password_verify($password, $user['password'])) {
            Session::set('admin_id', $user['id']);
            Session::set('admin_name', $user['username']);
            return redirect('/admin/dashboard');
        }
        return redirect('/admin/login')->with('error', '用户名或密码错误');
    }

    // 退出
    public function logout()
    {
        Session::delete('admin_id');
        Session::delete('admin_name');
        return redirect('/admin/login');
    }
}

第二步:创建中间件 AuthCheck

<?php
namespace appmiddleware;

use Closure;
use thinkRequest;
use thinkResponse;

class AuthCheck
{
    public function handle(Request $request, Closure $next)
    {
        // 如果已登录就直接放行
        if (session('admin_id')) {
            return $next($request);
        }

        // 没登录时,判断是不是AJAX请求,返回JSON或重定向
        if ($request->isAjax()) {
            return json(['code' => -1, 'msg' => '请先登录'])->code(401);
        }

        // 普通请求重定向到登录页
        return redirect('/admin/login');
    }
}

第三步:注册路由

// route/admin.php
use thinkfacadeRoute;

// 无需登录的请求
Route::get('admin/login', 'admin.Auth/login');
Route::post('admin/logindo', 'admin.Auth/doLogin');
Route::get('admin/logout', 'admin.Auth/logout');

// 需要登录的分组
Route::group('admin', function () {
    Route::get('dashboard', 'admin.Dashboard/index');
    Route::get('user/list', 'admin.User/list');
    Route::post('user/delete', 'admin.User/delete');
})->middleware(appmiddlewareAuthCheck::class);

这里注意,登录页面和登录处理的路由不要放在需要验证的分组里,否则就永远无法访问登录页了。

第四步:测试效果

当你访问 http://你的域名/admin/dashboard 而没有登录时,中间件会把你重定向到 /admin/login。一旦登录成功,后面再访问后台页面就会正常通过。

让中间件更加灵活:支持参数和排除项

有时你希望后台的“用户中心”页面也允许未登录的用户查看,但其他页面不行。这时候你可以给中间件传参数,或者干脆在中间件里判断当前请求的路由地址。

我采取一个相对简单的方法:在中间件里获取当前访问的控制器名,如果是 Auth 或者 Public 就放行。但这样不够优雅。更推荐的方式是写一个“白名单”配置在路由上。比如:

Route::group('admin', function () {
    // 公开路由
    Route::get('public/help', 'admin.Public/help');

    // 受保护路由
    Route::get('dashboard', 'admin.Dashboard/index');
})->middleware(appmiddlewareAuthCheck::class);

但你可能会发现,分组中间件会作用到组内的所有路由,包括公开的 public/help。要是想单独排除,那就不能用分组了。而是单独在单个路由上挂中间件:

Route::get('admin/public/help', 'admin.Public/help');
Route::get('admin/dashboard', 'admin.Dashboard/index')->middleware(appmiddlewareAuthCheck::class);

这样也能实现。

中间件里的前置和后置操作

中间件不止能“过滤”请求,还能在请求处理完后再做一些收尾工作。你可以在 $next($request) 之后写逻辑。比如记录访问日志、追加响应头等。

class LogMiddleware
{
    public function handle(Request $request, Closure $next)
    {
        // 请求之前
        $response = $next($request);

        // 请求之后
        $response->header('X-Powered-By', 'ThinkPHP');

        return $response;
    }
}

在我自己的后台项目里,我用中间件改写过HTTP响应头,用来防止安全隐患,效果很好。

中间件和事件区别

可能你会问:事件也能在操作后记录日志,那和中间件有什么区别?中间件是请求处理流程里的一环,适合做请求级的操作(如权限、跨域、过滤)。事件则类似于观察者模式,更适合解耦业务逻辑,比如订单创建后发通知,和请求流程无关。一般来说,能在中间件做的事就不要丢到事件里,因为中间件能拿到请求和响应对象。

一个容易犯的错误:sesssion在中间件里无法启动

我在一开始测试时,发现中间件里调用 session('admin_id') 拿不到值,后来才明白是因为中间件执行时还未开启session。TP默认在全局中间件中已经启动了session,但如果你在路由中间件或控制器中间件中还没初始化session,可能会出问题。解决办法是在 app/middleware.php 中确保session已经注册。我的做法是使用thinkmiddlewareSessionInit::class作为最早执行的全局中间件:

// app/middleware.php
return [
    thinkmiddlewareSessionInit::class,
    appmiddlewareLogMiddleware::class,
    // 其他全局中间件
];

这样你在后面的中间件中就能安全使用session了。

更复杂的场景:API接口的多语言和鉴权

中间件还经常用于API接口,比如验证签名、频率限制、检测Token。我最近在做一个开放接口,就在中间件里解析JWT,把用户ID存到请求对象中,后续控制器就能直接用 $request->userId 获取当前登录用户。这个方法特别干净。

不过这块就是另一个长话题了。今天先记录一下中间件的基本玩法和后台登录的例子,希望能帮你少踩些坑。你要是也写过有用的中间件,欢迎交流。

ThinkPHP 8中间件练手实录:一个后台登录过滤器是怎么写出来并跑通的
收藏 (0) 打赏

感谢您的支持,我会继续努力的!

打开微信/支付宝扫一扫,即可进行扫码打赏哦,分享从这里开始,精彩与您同在
点赞 (0)

版权声明:
本站资源有的来自互联网收集整理,本站纯免费分享提供学习使用,如果侵犯了您的合法权益,请联系本站我们会及时删除。
本站资源仅供研究、学习交流之用,免费开源项目不代表完全可商用,若商业用途请先咨询开发企业能否商用,否则产生的一切后果将由下载用户自行承担。
原创板块未经允许不得转载,否则将追究法律责任。

淘吗网 thinkphp ThinkPHP 8中间件练手实录:一个后台登录过滤器是怎么写出来并跑通的 https://www.taomawang.com/server/thinkphp/2575.html

下一篇:

已经没有下一篇了!

常见问题

相关文章

猜你喜欢
发表评论
暂无评论
官方客服团队

为您解决烦忧 - 24小时在线 专业服务