ASP.NET Core如何限制AntiForgery Token单次使用并阻止无页面访问调用?
针对Razor Pages中AntiForgery Token的两个问题解决方案
一、阻止未访问页面就调用POST接口
微软原生的AntiForgery机制仅验证Token与Cookie的匹配性,不检查用户是否加载过目标页面。要实现该限制,需结合会话(Session)标记用户的页面访问状态:
步骤1:启用Session服务
在Program.cs中配置Session:builder.Services.AddSession(options => { options.IdleTimeout = TimeSpan.FromMinutes(30); options.Cookie.HttpOnly = true; options.Cookie.IsEssential = true; }); // 在app.UseRouting()之后添加 app.UseSession();步骤2:在页面的GET方法中标记访问状态
public IActionResult OnGet() { // 标记用户已访问当前页面 HttpContext.Session.SetString("HasVisitedTargetPage", "true"); return Page(); }步骤3:在POST方法中校验访问标记
[ValidateAntiForgeryToken] public IActionResult OnPost() { // 检查是否已访问过页面 if (HttpContext.Session.GetString("HasVisitedTargetPage") != "true") { return Forbid(); // 返回403禁止访问 } // 执行业务逻辑 return RedirectToPage("/Success"); }
二、实现AntiForgery Token单次使用
微软没有现成的单次使用Token方案,需自行扩展,核心思路是在服务器端记录Token的使用状态,验证通过后立即失效:
基于Session的简单实现(适合单服务器场景)
在GET方法中生成并存储Token:
public IActionResult OnGet() { var antiForgery = HttpContext.RequestServices.GetRequiredService<IAntiforgery>(); var tokens = antiForgery.GetAndStoreTokens(HttpContext); // 将当前请求的Token存入Session HttpContext.Session.SetString("SingleUseAntiForgeryToken", tokens.RequestToken); return Page(); }在POST方法中校验并销毁Token:
[ValidateAntiForgeryToken] public async Task<IActionResult> OnPostAsync() { var antiForgery = HttpContext.RequestServices.GetRequiredService<IAntiforgery>(); try { // 先执行原生的AntiForgery验证 await antiForgery.ValidateRequestAsync(HttpContext); // 获取请求中的Token和Session中存储的Token var requestToken = Request.Form["__RequestVerificationToken"]; var storedToken = HttpContext.Session.GetString("SingleUseAntiForgeryToken"); if (string.IsNullOrEmpty(storedToken) || storedToken != requestToken) { return Forbid(); } // 验证通过后立即删除Session中的Token,禁止重复使用 HttpContext.Session.Remove("SingleUseAntiForgeryToken"); } catch { return Forbid(); } // 业务逻辑处理 return RedirectToPage("/Success"); }
分布式场景优化
如果是多服务器部署的系统,建议用Redis等分布式缓存代替Session存储Token,避免Session无法跨节点共享的问题。核心逻辑一致:生成Token时存入缓存,验证时取出对比,通过后删除缓存。
注意事项
- 两种方案可结合使用,既确保用户必须先访问页面,又保证Token仅能使用一次。
- 会话或缓存的过期时间需合理设置,避免影响正常用户的操作体验。
- 高并发场景下,要注意分布式缓存的性能和原子性操作(比如用Redis的
DEL命令确保Token删除的原子性)。
内容的提问来源于stack exchange,提问作者Jun Yan
相关产品推荐
相关产品推荐

