加入session支持
public void ConfigureServices(IServiceCollection services)
{
// add session support
services.Configure<CookiePolicyOptions>(options =>
{
options.CheckConsentNeeded = context => false;
options.MinimumSameSitePolicy = Microsoft.AspNetCore.Http.SameSiteMode.None;
});
services.AddSession(options =>
{
options.IdleTimeout = TimeSpan.FromSeconds(100);
options.Cookie.HttpOnly = true;
options.Cookie.IsEssential = true;
});
// 没有下面这句,启动会失败,
// 报这个错: InvalidOperationException: Unable to resolve service for type 'Microsoft.Extensions.Caching.Distributed.IDistributedCache' while attempting to activate 'Microsoft.AspNetCore.Session.DistributedSessionStore
services.AddDistributedMemoryCache();
// 因为PreventDoublePostAttribute用到了IAntiforgery,所以这里还要加
services.AddAntiforgery(options => options.HeaderName = "X-XSRF-TOKEN");
...
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env, IAntiforgery antiforgery)
{
...
app.UseStaticFiles();
// add session pipepline
app.UseSession();
...
}
引入PreventDoublePostAttribute属性
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
public class PreventDoublePostAttribute : ActionFilterAttribute
{
private const string UniqFormuId = "LastProcessedToken";
public override async void OnActionExecuting(ActionExecutingContext context)
{
IAntiforgery antiforgery = context.HttpContext.RequestServices.GetService(typeof(IAntiforgery)) as IAntiforgery;
if (antiforgery == null)
return;
AntiforgeryTokenSet tokens = antiforgery.GetAndStoreTokens(context.HttpContext);
if (!context.HttpContext.Request.Form.ContainsKey(tokens.FormFieldName))
{
return;
}
var currentFormId = context.HttpContext.Request.Form[tokens.FormFieldName].ToString();
var lastToken = "" + context.HttpContext.Session.GetString(UniqFormuId);
if (lastToken.Equals(currentFormId))
{
context.ModelState.AddModelError(string.Empty, "Looks like you accidentally submitted the same form twice.");
return;
}
context.HttpContext.Session.Remove(UniqFormuId);
context.HttpContext.Session.SetString(UniqFormuId, currentFormId);
await context.HttpContext.Session.CommitAsync();
}
}
使用
[HttpPost]
[PreventDoublePost]
public ActionResult<TodoItem> Create([FromForm] TodoItem item)
{
AllItems.Add(item);
return CreatedAtRoute("GetTodo", new { id = item.Id }, item);
}
测试
# test post
POST https://localhost:44352/TodoItem/ HTTP/1.1
content-type: application/x-www-form-urlencoded
cache-control: no-cache
__RequestVerificationToken=form_id_1
&id=86336
&Summary=ttt
###
既然PreventDoublePostAttribute属性只使用了tokens.FormFieldName,那么hardcode一个hidden formid应该也可以,这样就不需要AntiForgery了。
再说AntiForgery也不是这么用的。