本文目录导读:

在 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 的 $routeMiddleware 或 App\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中定义)最为简洁、可维护 - 如果限流逻辑简单且只在单一路由使用,可以选用方式二
- 如果需要在多个控制器/路由中复用复杂限流逻辑,使用方式三
版权声明:NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播