用thinkphp6写一个登陆中间件
一、背景与问题
在Web开发中,用户身份验证是系统安全的核心环节。传统做法是通过控制器中重复校验用户登录状态,但这种方式会导致代码冗余、可维护性差。ThinkPHP6的中间件机制提供了优雅的解决方案:通过定义中间件规则,将身份验证逻辑集中管理。
核心问题包括:
- 如何在不破坏原有业务逻辑的前提下实现身份验证
- 如何处理未登录用户的重定向逻辑
- 如何安全地存储和验证用户身份信息
- 如何处理多层级的权限控制需求
二、基本原理
ThinkPHP6的中间件系统基于请求-响应生命周期,其核心机制如下:
- 中间件链式执行:请求按顺序经过多个中间件处理
- 基于中间件组的路由控制:通过
middleware字段定义路由的中间件规则 - 会话管理:通过
session机制存储用户身份信息 - 自定义异常处理:通过
异常处理机制返回统一格式的错误响应
中间件的典型执行流程:
请求到来 -> 中间件1处理 -> 中间件2处理 -> 控制器处理 -> 响应返回三、环境准备
确保环境满足以下条件:
- PHP 7.1+(建议7.4)
- Composer 2.x
- MySQL 5.7+ 或其他支持的数据库
- 安装ThinkPHP6框架
创建项目:
composer create-project --prefer-dist thinkphp6 my_project
cd my_project配置数据库:
// config/database.php
return [
'default' => 'mysql',
'mysql' => [
'type' => 'mysql',
'hostname' => '127.0.0.1',
'database' => 'my_database',
'username' => 'root',
'password' => '',
'hostport' => '3306',
'charset' => 'utf8mb4'
]
];四、核心实现
1. 创建中间件类
// app/middleware/LoginCheck.php
namespace app\middleware;
use think\Request;
use think\Response;
class LoginCheck
{
public function handle($request, \Closure $next)
{
// 获取会话中的用户ID
$userId = session('user_id');
// 检查是否登录
if (!$userId) {
// 未登录时返回JSON格式错误响应
return json(['code' => 401, 'msg' => '未登录']);
}
// 通过验证,继续后续处理
return $next($request);
}
}关键点解析:
- 使用
session()函数获取会话数据 - 返回JSON响应时使用
json()函数 - 通过
$next参数继续执行后续中间件或控制器
2. 中间件注册
// config/middleware.php
return [
'default' => [
// 基础中间件
'think\RequestHandler',
'think\SessionHandler',
// 自定义中间件
'app\middleware\LoginCheck',
],
'except' => [
// 排除不需要验证的路由
'index/index/index',
'user/login',
]
];3. 路由配置
// route/route.php
return [
'hello' => 'index/index/index',
'user/login' => 'user/login',
'user/dashboard' => ['app\middleware\LoginCheck', 'user/dashboard'],
];五、完整案例
1. 用户登录控制器
// app/controller/UserController.php
namespace app\controller;
use think\Request;
class UserController
{
public function login(Request $request)
{
$username = $request->post('username');
$password = $request->post('password');
// 假设从数据库验证用户
if ($this->validateUser($username, $password)) {
// 设置会话信息
session('user_id', 123);
return json(['code' => 200, 'msg' => '登录成功']);
} else {
return json(['code' => 400, 'msg' => '登录失败']);
}
}
private function validateUser($username, $password)
{
// 实际开发中应使用数据库查询
return $username === 'admin' && $password === '123456';
}
}2. 受保护的控制器
// app/controller/DashboardController.php
namespace app\controller;
use think\Request;
class DashboardController
{
public function index(Request $request)
{
return json(['code' => 200, 'data' => '欢迎来到仪表盘']);
}
}3. 前端登录页面
<!-- view/index/index.html -->
<!DOCTYPE html>
<html>
<head>
<title>登录页面</title>
</head>
<body>
<form action="/user/login" method="post">
用户名:<input type="text" name="username" required><br>
密码:<input type="password" name="password" required><br>
<button type="submit">登录</button>
</form>
</body>
</html>六、源码解析
1. 中间件处理逻辑
public function handle($request, \Closure $next)
{
// 检查会话中的用户ID
$userId = session('user_id');
// 未登录处理
if (!$userId) {
return json(['code' => 401, 'msg' => '未登录']);
}
// 通过验证,继续执行后续处理
return $next($request);
}关键点:
- 使用
session()函数获取会话信息 - 返回JSON响应时使用
json()函数 - 通过
$next参数继续处理流程
2. 异常处理机制
在config/app.php中配置异常处理:
return [
'exception_handle' => '\\app\\exception\\Handle',
];自定义异常类:
// app/exception/Handle.php
namespace app\exception;
use think\exception\Handle;
use think\Response;
class Handle extends Handle
{
public function render($request, \Throwable $e)
{
// 自定义异常处理逻辑
if ($e instanceof \Exception) {
return json(['code' => 500, 'msg' => '服务器内部错误']);
}
return parent::render($request, $e);
}
}七、进阶使用
1. 多级权限控制
// app/middleware/PermissionCheck.php
namespace app\middleware;
use think\Request;
use think\Response;
class PermissionCheck
{
public function handle($request, \Closure $next)
{
// 获取用户角色
$role = session('user_role');
// 权限校验逻辑
if ($role !== 'admin') {
return json(['code' => 403, 'msg' => '无权限访问']);
}
return $next($request);
}
}2. JWT支持
// app/middleware/JwtCheck.php
namespace app\middleware;
use think\Request;
use think\Response;
use Firebase\JWT\JWT;
class JwtCheck
{
public function handle($request, \Closure $next)
{
$token = $request->header('Authorization');
if (!$token) {
return json(['code' => 401, 'msg' => '缺少token']);
}
try {
$decoded = JWT::decode($token, 'secret_key', ['HS256']);
session('user_id', $decoded->user_id);
} catch (\Exception $e) {
return json(['code' => 401, 'msg' => '无效token']);
}
return $next($request);
}
}八、性能与工程实践
1. 性能优化
缓存用户信息:
// 使用Redis缓存用户信息 $userId = cache('user:' . $token, 3600);数据库索引优化:
-- 用户表添加索引 ALTER TABLE users ADD INDEX idx_user_id (user_id);中间件拆分:
// 拆分为登录验证和权限验证 [ 'app\middleware\LoginCheck', 'app\middleware\PermissionCheck', ]
2. 安全考虑
防止CSRF攻击:
// 在表单中添加token <input type="hidden" name="_token" value="<?= csrf_token() ?>">防止XSS攻击:
// 使用htmlspecialchars过滤用户输入 echo htmlspecialchars($userInput);会话安全:
// 设置会话参数 session([ 'name' => 'myapp', 'expire' => 3600 * 24 * 7, 'type' => 'file', 'path' => './runtime/session', ]);
九、常见问题与踩坑
1. 中间件未生效
错误示例:
// 错误的中间件注册
'except' => ['user/login'],正确做法:
// 正确的中间件排除
'except' => ['user/login', 'user/register'],2. 会话信息丢失
错误场景:
// 错误的会话设置
session('user_id', 123);正确做法:
// 正确的会话设置
session('user_id', 123, 3600); // 设置过期时间3. 路由配置错误
错误示例:
// 错误的路由配置
'user/dashboard' => ['app\middleware\LoginCheck', 'user/dashboard'],正确做法:
// 正确的路由配置
'user/dashboard' => ['app\middleware\LoginCheck', 'user/dashboard'],十、最佳实践
1. 推荐使用场景
- 需要统一身份验证的API接口
- 多层级权限系统
- 跨域请求的认证机制
- 需要记录用户行为的业务场景
2. 不建议使用场景
- 高频访问的页面(建议使用Token机制)
- 需要实时处理的接口(建议使用JWT)
- 需要多因素认证的复杂场景
3. 推荐的实现方式
- 使用JWT进行分布式系统认证
- 结合Redis缓存提升性能
- 使用中间件链实现多层校验
- 对敏感操作添加二次验证
十一、总结
通过实现登录中间件,我们实现了用户身份验证的核心功能,其原理基于ThinkPHP6的中间件机制,通过会话管理、异常处理、路由控制等技术构建安全的认证系统。在实际开发中,需要根据具体业务场景选择合适的实现方式,处理好性能、安全和可维护性之间的平衡。中间件机制使得身份验证逻辑集中管理,提高了代码复用性和系统可维护性,是构建安全Web应用的重要组成部分。