服务器访问 Laravel API 的节流问题

Throttle issue with server accessing a Laravel API

我有一个正在使用 Laravel 的 API,它是从带有 Guzzle 的 Laravel 的另一个实例调用的。

第二个服务器的 IP 地址正在触发 API 上的限制。

我想通过用户的域和 IP 地址从第二个服务器传递到 API。我希望不要重新编码 Throttle 中间件。

我想知道以前是否有人遇到过这个问题,如果遇到过,他们是如何解决的。

API上的中间件组是这样设置的

/**
 * The application's route middleware groups.
 *
 * @var array
 */
protected $middlewareGroups = [
    'api' => [
        'throttle:60,1',
        \Barryvdh\Cors\HandleCors::class,
        'bindings',
    ],
];

relevant throttle code

/**
 * Resolve request signature.
 *
 * @param  \Illuminate\Http\Request  $request
 * @return string
 *
 * @throws \RuntimeException
 */
protected function resolveRequestSignature($request)
{
    if ($user = $request->user()) {
        return sha1($user->getAuthIdentifier());
    }
    if ($route = $request->route()) {
        return sha1($route->getDomain().'|'.$request->ip());
    }
    throw new RuntimeException('Unable to generate the request signature. Route unavailable.');
}
if ($route = $request->route()) {
    return sha1($route->getDomain().'|'.$request->ip());

您可以通过 X_FORWARDED_FOR header 传递客户端的 IP 地址,这样就不会阻止第二个服务器的 IP 地址。

Route::get('/', function (Request $request) {

    $client = new \GuzzleHttp\Client();

    $request = $client->request('GET', '/api/example', [
        'headers' => ['X_FORWARDED_FOR' => $request->ip()]
    ]);

    $response = $request->getBody();

});

在您的主服务器上,您需要将您的第二个服务器作为受信任的代理 (docs) 添加到 App\Http\Middleware\TrustProxies,以便从此 header.[=16] 获取 IP =]

class TrustProxies extends Middleware
{
    /**
     * The trusted proxies for this application.
     *
     * @var array
     */
    protected $proxies = [
        '192.168.1.1', // <-- set the ip of the second server here 
    ];

    //...
}

现在主服务器上对 $request->ip() 的每次调用都将使用原始客户端 IP,而不是第二个服务器的 IP。这也会影响节流。

开箱即用的解决方案,如果你使用的版本>=5.6,就是使用dynamic rate limit

Dynamic Rate Limiting

You may specify a dynamic request maximum based on an attribute of the authenticated User model. For example, if your User model contains a rate_limit attribute, you may pass the name of the attribute to the throttle middleware so that it is used to calculate the maximum request count:

Route::middleware('auth:api', 'throttle:rate_limit,1')->group(function () {
    Route::get('/user', function () {
        //
    });
});

The relevant part of the code

/**
 * Resolve the number of attempts if the user is authenticated or not.
 *
 * @param  \Illuminate\Http\Request  $request
 * @param  int|string  $maxAttempts
 * @return int
 */
protected function resolveMaxAttempts($request, $maxAttempts)
{
    if (Str::contains($maxAttempts, '|')) {
        $maxAttempts = explode('|', $maxAttempts, 2)[$request->user() ? 1 : 0];
    }
    if (! is_numeric($maxAttempts) && $request->user()) {
        $maxAttempts = $request->user()->{$maxAttempts};
    }
    return (int) $maxAttempts;
}

因此,您可以在用户(代表第二个服务器)中添加一个 rate_limit 属性 并传递一个更大的数字

编辑:

如果你不想对调用者进行身份验证,你可以很容易地覆盖 resolveMaxAttempts 方法来根据请求数据动态计算限制(你可以使用任何参数,主机,ip等):

protected function resolveMaxAttempts($request, $maxAttempts)
{
    if (in_array(request->ip(), config('app.bypassThrottleMiddleware')) {
        return PHP_INT_MAX;
    }

    return parent::resolveMaxAttempts($request, $maxAttempts);
}

并在您的 config/app.php 中添加:

'bypassThrottleMiddleware' => ['0.0.0.0'],