本文目录导读:

在 PHP 项目中统一进行接口权限校验拦截,核心思路是使用 中间件 或 事件驱动 模式,在请求到达具体控制器方法之前完成鉴权,以下是几种主流且高效的实现方案:
Laravel 框架(中间件方案)
Laravel 提供了最完善的中间件机制。
1 创建权限中间件
php artisan make:middleware CheckPermission
2 实现校验逻辑
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
use App\Models\User;
use Illuminate\Support\Facades\Route;
class CheckPermission
{
public function handle(Request $request, Closure $next)
{
// 获取当前用户(假设通过 JWT/Session 已认证)
$user = auth()->user();
if (!$user) {
return response()->json(['message' => '未认证'], 401);
}
// 获取当前路由名称
$routeName = Route::currentRouteName();
// 或者使用:$routeName = $request->route()->getName();
// 定义不需要权限检查的路由(白名单)
$whitelist = ['login', 'register', 'forgot-password'];
if (in_array($routeName, $whitelist)) {
return $next($request);
}
// 检查用户权限(可根据业务定制)
if (!$user->hasPermission($routeName)) {
return response()->json([
'message' => '权限不足',
'required_permission' => $routeName
], 403);
}
return $next($request);
}
}
3 注册中间件
在 app/Http/Kernel.php 中注册:
protected $routeMiddleware = [
// ...
'permission' => \App\Http\Middleware\CheckPermission::class,
];
4 应用到路由组
Route::middleware(['auth:api', 'permission'])->group(function () {
Route::get('/users', [UserController::class, 'index'])->name('users.list');
Route::post('/users', [UserController::class, 'store'])->name('users.create');
Route::put('/users/{id}', [UserController::class, 'update'])->name('users.update');
});
ThinkPHP 框架(中间件方案)
1 定义中间件
<?php
namespace app\middleware;
use Closure;
use think\Request;
use think\Response;
class CheckPermission
{
public function handle($request, Closure $next)
{
// 获取当前用户(假设已认证)
$user = $request->user;
if (!$user) {
return Response::create(['message' => '未认证'], 401);
}
// 获取当前控制器和方法
$controller = $request->controller();
$action = $request->action();
$permissionKey = "{$controller}@{$action}";
// 定义白名单
$whitelist = ['Index@login', 'Index@register'];
if (in_array($permissionKey, $whitelist)) {
return $next($request);
}
// 权限检查
if (!$user->can($permissionKey)) {
return Response::create(['message' => '权限不足'], 403);
}
return $next($request);
}
}
2 注册中间件
在 config/middleware.php 中:
return [
'CheckPermission' => app\middleware\CheckPermission::class,
];
3 应用到路由
Route::group(['middleware' => ['auth', 'CheckPermission']], function () {
Route::get('users', 'User/index');
Route::post('users', 'User/create');
});
原生 PHP 实现(全局拦截器)
如果不使用框架,可以创建一个统一的入口文件:
<?php
// index.php - 统一入口
require_once 'config.php';
require_once 'auth.php';
class Router {
public static function handle() {
$uri = $_SERVER['REQUEST_URI'];
$method = $_SERVER['REQUEST_METHOD'];
// 路由解析
$route = self::parseRoute($uri, $method);
// 权限校验
if (!self::checkPermission($route)) {
http_response_code(403);
echo json_encode(['message' => '权限不足']);
exit;
}
// 执行控制器
$controller = new $route['controller']();
$action = $route['action'];
echo $controller->$action();
}
private static function checkPermission($route) {
// 白名单
$whitelist = [
['controller' => 'AuthController', 'action' => 'login'],
['controller' => 'AuthController', 'action' => 'register'],
];
foreach ($whitelist as $item) {
if ($item['controller'] == $route['controller'] &&
$item['action'] == $route['action']) {
return true;
}
}
// JWT 验证用户
$token = $_SERVER['HTTP_AUTHORIZATION'] ?? '';
$user = validateJWT($token);
if (!$user) {
return false;
}
// 检查权限
return $user->hasPermission($route['controller'], $route['action']);
}
}
Router::handle();
权限管理核心设计
1 权限存储结构
-- 用户-角色-权限 多对多关系
CREATE TABLE permissions (
id INT PRIMARY KEY,
name VARCHAR(100) UNIQUE, -- 'users.list', 'users.create'
display_name VARCHAR(200),
description TEXT
);
CREATE TABLE roles (
id INT PRIMARY KEY,
name VARCHAR(100) UNIQUE,
display_name VARCHAR(200)
);
CREATE TABLE role_permission (
role_id INT,
permission_id INT,
PRIMARY KEY (role_id, permission_id)
);
CREATE TABLE user_role (
user_id INT,
role_id INT,
PRIMARY KEY (user_id, role_id)
);
2 权限检查逻辑示例
class User {
public function hasPermission($permissionName) {
// 超级管理员拥有所有权限
if ($this->is_super_admin) {
return true;
}
$roles = $this->roles()->with('permissions')->get();
foreach ($roles as $role) {
foreach ($role->permissions as $permission) {
if ($permission->name === $permissionName) {
return true;
}
}
}
return false;
}
}
最佳实践建议
1 注解方式(适用于大型项目)
// Laravel 扩展
#[Permission('users.create')]
public function store(Request $request) {
// ...
}
2 缓存优化
// 使用 Redis 缓存用户权限
class UserPermissionCache {
public function hasPermission($userId, $permissionName) {
$key = "user_permissions:{$userId}";
// 检查缓存
if (Cache::has($key)) {
$permissions = Cache::get($key);
return in_array($permissionName, $permissions);
}
// 从数据库加载并缓存(缓存1小时)
$user = User::find($userId);
$permissions = $user->getAllPermissions()->pluck('name')->toArray();
Cache::put($key, $permissions, 3600);
return in_array($permissionName, $permissions);
}
}
3 日志记录
// 在中间件中记录权限检查日志
Log::channel('permission')->info('权限检查', [
'user_id' => $user->id,
'route' => $routeName,
'result' => $hasPermission ? '通过' : '拒绝',
'ip' => $request->ip(),
'time' => now()
]);
接口权限粒度设计
建议采用三级管控:
- 路由级别:控制能否访问某个接口
- 数据级别:控制能看到哪些数据(自己的/部门的/全部的)
- 字段级别:控制能查看/修改哪些字段
示例:
// 数据权限中间件
class DataPermissionMiddleware {
public function handle($request, $next) {
// 获取用户
$user = auth()->user();
// 根据角色决定数据范围
switch ($user->data_scope) {
case 'self':
// 只返回自己的数据
$request->merge(['user_filter' => $user->id]);
break;
case 'department':
// 返回部门数据
$request->merge(['department_filter' => $user->department_id]);
break;
case 'all':
// 返回所有数据
break;
}
return $next($request);
}
}
选择方案时需要考虑:
- 小项目:使用原生 PHP 的全局拦截器
- 中型项目:ThinkPHP/Laravel 的中间件
- 大型分布式:API 网关(如 Kong)+ 微服务中间件
- 性能敏感:配合缓存(Redis)+ 预编译权限位运算
无论哪种方案,核心思想都是:在请求进入业务逻辑之前,通过统一的拦截点完成身份验证和权限校验。