Laravel限流按用户ID吗

wen PHP项目 2

本文目录导读:

Laravel限流按用户ID吗

  1. 方式一:使用 throttle 中间件的动态参数(推荐)
  2. 方式二:使用 RateLimiter Facade 手动限流(更灵活)
  3. 方式三:自定义中间件(高级用法)

在 Laravel 中,默认的限流中间件 (throttle) 通常按 IP 地址 进行限流,而不是直接按用户 ID。

要实现按用户 ID 限流,需要手动指定 key。 有两种主要方式:

使用 throttle 中间件的动态参数(推荐)

在路由中,可以将 throttle 中间件的第三个参数(key)设置为用户的 ID,或者一个包含用户 ID 的字符串。

routes/api.php 中:

use Illuminate\Support\Facades\Route;
Route::middleware('auth:sanctum') // 假设使用 Sanctum 认证
    ->group(function () {
        // 关键点:将 'throttle:10,1,user_id' 中的 'user_id' 换成动态的用户 ID
        Route::get('/user-data', function () {
            return auth()->user();
        })->middleware('throttle:10,1,' . auth()->id()); // 直接拼接 user_id
    });

更优雅的写法(使用路由组):

Route::middleware(['auth:sanctum', 'throttle:api'])->group(function () {
    Route::get('/user-data', function () { ... })->name('user.data');
});

然后在 App\Http\Kernel.php$routeMiddlewareApp\Providers\RouteServiceProvider.php 中定义动态限流逻辑:

// 在 RouteServiceProvider 的 boot 方法中
RateLimiter::for('api', function (Request $request) {
    // 如果是认证用户,用用户 ID;否则用 IP
    $key = $request->user() 
                ? 'user_' . $request->user()->id 
                : 'ip_' . $request->ip();
    return Limit::perMinute(10)->by($key);
});

优点:

  • 清晰、易于管理
  • 可以在 HTTP Kernel 中集中配置所有限流逻辑

使用 RateLimiter Facade 手动限流(更灵活)

在控制器中手动调用限流器,并以用户 ID 作为键值。

use Illuminate\Support\Facades\RateLimiter;
class UserController extends Controller
{
    public function getProfile(Request $request)
    {
        $userId = auth()->id(); // 或者 $request->user()->id
        // 定义 key,确保唯一性
        $key = 'user_' . $userId . '_profile';
        // 检查限流
        $executed = RateLimiter::attempt(
            $key,
            $maxAttempts = 5,   // 最多尝试 5 次
            function() {
                return true; // 执行成功
            },
            $decaySeconds = 60 // 60 秒后重置
        );
        if (! $executed) {
            // 限流触发
            $seconds = RateLimiter::availableIn($key);
            return response()->json([
                'error' => 'Too many requests',
                'retry_after' => $seconds
            ], 429);
        }
        // 正常处理请求
        return response()->json(['data' => '...']);
    }
}

优点:

  • 完全自定义限流逻辑(如按用户 ID + 路由 + 方法)
  • 可以返回自定义错误消息和头部信息

自定义中间件(高级用法)

创建一个自定义中间件,在其中根据 user_id + IP 或只根据 user_id 生成限流 key。

// app/Http/Middleware/ThrottleByUser.php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Cache\RateLimiter;
use Symfony\Component\HttpFoundation\Response;
class ThrottleByUser
{
    public function handle($request, Closure $next, $maxAttempts = 60, $decayMinutes = 1)
    {
        $key = 'user_' . (auth()->id() ?: $request->ip());
        $limiter = app(RateLimiter::class);
        if ($limiter->tooManyAttempts($key, $maxAttempts)) {
            return response('Too Many Attempts.', 429);
        }
        $limiter->hit($key, $decayMinutes * 60);
        $response = $next($request);
        // 返回限流头部信息
        return $this->addHeaders(
            $response, $maxAttempts,
            $limiter->attempts($key)
        );
    }
    protected function addHeaders($response, $maxAttempts, $currentAttempts)
    {
        $response->headers->set('X-RateLimit-Limit', $maxAttempts);
        $response->headers->set('X-RateLimit-Remaining', max(0, $maxAttempts - $currentAttempts));
        return $response;
    }
}

然后在 Kernel.php 注册:

protected $routeMiddleware = [
    'throttle.user' => \App\Http\Middleware\ThrottleByUser::class,
];

使用:

Route::middleware(['auth:sanctum', 'throttle.user:10,1'])->group(...);

方式 是否按用户 ID 适用场景
方式一(推荐) ✅ 是 大多数情况,简单、Laravel 原生支持
方式二(手动) ✅ 是 需要精细控制或复杂限流逻辑
方式三(自定义) ✅ 是 需要完全自定义行为或复用中间件

最佳实践建议:

  • 使用 方式一(在 RateLimiter::for 中定义)最为简洁、可维护
  • 如果限流逻辑简单且只在单一路由使用,可以选用方式二
  • 如果需要在多个控制器/路由中复用复杂限流逻辑,使用方式三

抱歉,评论功能暂时关闭!