レートリミットってご存知ですか?
いろいろ言い方はあるかもしれませんが、
「指定期間内に実行できるリクエスト数の制限」
という風に捉えています。
今回、webサービスにてレートリミットを実装したのでメモ。尚要件として、
・ログインしていない場合はCookieベース
・ログインしていたらログインIDベース
で端末を識別するようにしています。
本来ここに「ログイン試行回数は5分間に5回まで(ログインしたらリセット)」や、「パスワード再発行リクエストは1時間に5回まで」みたいな個別のパターンも必要でしょうが、ここでは省きます。
実際のコード
まず、.NET7以上が前提の書き方です。7以上の場合、純正の仕組みが使えます。
Program.csを開き、usingを追加。
using System.Threading.RateLimiting;
もうそのままの名前ですね。
そしてProgram.csにて、builderへの記述と呼び出しの二箇所を追加。
...
builder.Services.AddRateLimiter(options =>
{
options.AddPolicy("ratelimit-policy", httpContext =>
{
...ログイン済みか判定するためにユーザ情報を取得する処理を記述。(プロジェクト依存)
var userId = ---;
if (!string.IsNullOrEmpty(userId))
{
return RateLimitPartition.GetFixedWindowLimiter(userId, key =>
new FixedWindowRateLimiterOptions
{
PermitLimit = 100,
Window = TimeSpan.FromMinutes(1) //直近1分間で100回までOKの意
}
);
}
else
{
// 未認証の場合、クッキーを使って検証する
var cookieId = httpContext.Request.Cookies["deviceId"];
if (string.IsNullOrEmpty(cookieId))
{
// クッキーが存在しない場合、新しいIDを生成してクッキーにセット
cookieId = Guid.NewGuid().ToString();
httpContext.Response.Cookies.Append("deviceId", cookieId);
}
return RateLimitPartition.GetFixedWindowLimiter(cookieId, key =>
new FixedWindowRateLimiterOptions
{
PermitLimit = 100,
Window = TimeSpan.FromMinutes(1)
}
);
}
});
// ブロック時のステータスコード
options.RejectionStatusCode = 429;
});
--Buildする前に記述。
var app = builder.Build();
...
app.UseAuthorization();
--認証ポリシーの後に記述
app.UseRateLimiter();
...
そしてコントローラにてポリシーを適用します。
[EnableRateLimiting("ratelimit-policy")]
public class HogeController : ControllerBase
...
試しにリミット値を厳しめにして、対象URLにブラウザリロードやSwaggerでアクセスしてみてください。
まとめ
公式が対応してくれただけあって簡単ですね。ただこれだけでは足りないところもありますので、自身の開発するサービスと照らし合わせて、どこに個別対応が必要か考えてください。
コメント