当用户通过身份验证时,post 方法上的防伪异常(为不同的基于声明的用户提供的防伪令牌)
Antiforgery exception (Provided antiforgery token meant for a different claims-based user) on post method, when user is authenticated
我正在使用 ASP.NET Core 2.2 和 AspNetCore.Authentication.
我正在尝试在我的 cshtml
_Layout 视图组件的表单中使用 Post
调用:
<form asp-controller="Home" asp-action="SearchResults" method="post" enctype="multipart/form-data" asp-antiforgery="true" novalidate>
(...)
</form>
我的意图是用搜索属性掩盖 URL,使网络抓取和 抓取 更加困难。我的 Post
方法正在更新我的控制器的静态字段,并重定向到我的 Index Get
方法,其中显示搜索结果:
private static SearchViewModel _searchModel;
private static string _sortOrder;
private static int? _pageNumber;
(...)
[HttpPost]
[AllowAnonymous]
public IActionResult SearchResults(SearchViewModel searchModel, string sortOrder, int? pageNumber = 1)
{
_searchModel = searchModel;
_sortOrder = sortOrder;
_pageNumber = pageNumber;
return RedirectToAction(nameof(Index));
}
和Get
方法签名:
[HttpGet]
[AllowAnonymous]
public async Task<IActionResult> Index()
{
//refering to updated fields
}
灵感来自 this SO reply。
和Setup.cs identity/authentication设置:
public void ConfigureServices(IServiceCollection services)
{
services.AddIdentity<AppUser, IdentityRole>(opts => {
opts.User.RequireUniqueEmail = true;
opts.User.AllowedUserNameCharacters = null; //disable validation
opts.Password.RequiredLength = 8;
opts.Password.RequireNonAlphanumeric = false;
opts.Password.RequireLowercase = false;
opts.Password.RequireUppercase = false;
opts.Password.RequireDigit = true;
})
.AddEntityFrameworkStores<ApplicationDbContext>()
.AddDefaultTokenProviders();
services.AddAntiforgery(x => x.HeaderName = "X-CSRF-TOKEN");
.Services.ConfigureApplicationCookie(options =>
{
//previous cookies not valid
options.SlidingExpiration = true;
options.ExpireTimeSpan = TimeSpan.FromDays(1);
});
services.AddMvc(options =>
{
options.Filters.Add(new AutoValidateAntiforgeryTokenAttribute());
options.Filters.Add<AuditUserActionFilter>();
});
(...)
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env, IAntiforgery antiforgery)
{
(...)
app.Use(next => context =>
{
if (context.Request.Path == "/")
{
var tokens = antiforgery.GetAndStoreTokens(context);
context.Response.Cookies.Append("CSRF-TOKEN", tokens.RequestToken, new CookieOptions { HttpOnly = false });
}
return next(context);
});
app.UseAuthentication();
}
}
我相信这是一个关键: 实际上,当我作为访客(未验证)提交 Post
表单时,一切正常。但是在登录后,在提交表单时我收到了一个异常:
Provided antiforgery token meant for a different claims-based user
另外请看下面我的用户认证过程:
if (user != null)
{
await _signInManager.SignOutAsync();
var result = await _signInManager.PasswordSignInAsync(user, details.Password, false, false);
if (result.Succeeded)
{
if (!string.IsNullOrEmpty(returnUrl) && Url.IsLocalUrl(returnUrl))
return Redirect(returnUrl);
else
return RedirectToAction(nameof(Account));
}
else if (result.IsLockedOut || result.IsNotAllowed)
{
ViewBag.Message = "You are not allowed to sign in.";
return View("Error");
}
else
{
ModelState.AddModelError(nameof(UserLoginViewModel.Password), "Incorrect password.");
return View("Index");
}
}
我尝试了 here, and 中的几种方法,但没有结果。但它们适用于 ASP.NET Core 的旧版本。我不确定我应该如何为经过身份验证的用户更新我的防伪令牌?
我的问题的可能原因:
- 对Post的误解 -> 获取表单调用。
- 不了解身份验证 cookie 在 ASP.NET 核心身份验证中的工作原理。
- 对一般身份验证过程的误解。
P.S.
当我删除
options.Filters.Add(new AutoValidateAntiforgeryTokenAttribute());
来自:
services.AddMvc(options =>
{
options.Filters.Add(new AutoValidateAntiforgeryTokenAttribute());
options.Filters.Add<AuditUserActionFilter>();
});
我可以拨打 Post
电话。但我相信,这并不奇怪。
编辑:
由于某种原因,当我从另一个视图(表单在 _Layout 的视图组件中)提交表单而不是 Home/Index(我显示搜索结果的地方)时,没有防伪异常。
是因为HomeController
的Index
动作是Get
吗?添加 Post / Get
属性后:
[AllowAnonymous]
[HttpGet, HttpPost]
public async Task<IActionResult> Index()
我有和以前一样的异常。添加后:
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Index()
我根本无法加载视图,因为它正在请求令牌。
如何绕过它?
在 itminus 建议后我明白了,我必须将我的 Configure
方法和我的 app.UseAuthentication();
移动到顶部。此外,在阅读 documentation I realized, that after using AddMvc 之后,我不再需要引用 "CSRF-TOKEN"
.
的代码
最后我的 Startup.cs 看起来像:
public void ConfigureServices(IServiceCollection services)
{
services.AddIdentity<AppUser, IdentityRole>(opts => {
opts.User.RequireUniqueEmail = true;
opts.User.AllowedUserNameCharacters = null; //disable validation
opts.Password.RequiredLength = 8;
opts.Password.RequireNonAlphanumeric = false;
opts.Password.RequireLowercase = false;
opts.Password.RequireUppercase = false;
opts.Password.RequireDigit = true;
})
.AddEntityFrameworkStores<ApplicationDbContext>()
.AddDefaultTokenProviders();
.Services.ConfigureApplicationCookie(options =>
{
//previous cookies not valid
options.SlidingExpiration = true;
options.ExpireTimeSpan = TimeSpan.FromDays(1);
});
services.AddMvc(options =>
{
options.Filters.Add(new AutoValidateAntiforgeryTokenAttribute());
options.Filters.Add<AuditUserActionFilter>();
});
(...)
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env, IAntiforgery antiforgery)
{
app.UseAuthentication();
(...)
}
}
我正在使用 ASP.NET Core 2.2 和 AspNetCore.Authentication.
我正在尝试在我的 cshtml
_Layout 视图组件的表单中使用 Post
调用:
<form asp-controller="Home" asp-action="SearchResults" method="post" enctype="multipart/form-data" asp-antiforgery="true" novalidate>
(...)
</form>
我的意图是用搜索属性掩盖 URL,使网络抓取和 抓取 更加困难。我的 Post
方法正在更新我的控制器的静态字段,并重定向到我的 Index Get
方法,其中显示搜索结果:
private static SearchViewModel _searchModel;
private static string _sortOrder;
private static int? _pageNumber;
(...)
[HttpPost]
[AllowAnonymous]
public IActionResult SearchResults(SearchViewModel searchModel, string sortOrder, int? pageNumber = 1)
{
_searchModel = searchModel;
_sortOrder = sortOrder;
_pageNumber = pageNumber;
return RedirectToAction(nameof(Index));
}
和Get
方法签名:
[HttpGet]
[AllowAnonymous]
public async Task<IActionResult> Index()
{
//refering to updated fields
}
灵感来自 this SO reply。
和Setup.cs identity/authentication设置:
public void ConfigureServices(IServiceCollection services)
{
services.AddIdentity<AppUser, IdentityRole>(opts => {
opts.User.RequireUniqueEmail = true;
opts.User.AllowedUserNameCharacters = null; //disable validation
opts.Password.RequiredLength = 8;
opts.Password.RequireNonAlphanumeric = false;
opts.Password.RequireLowercase = false;
opts.Password.RequireUppercase = false;
opts.Password.RequireDigit = true;
})
.AddEntityFrameworkStores<ApplicationDbContext>()
.AddDefaultTokenProviders();
services.AddAntiforgery(x => x.HeaderName = "X-CSRF-TOKEN");
.Services.ConfigureApplicationCookie(options =>
{
//previous cookies not valid
options.SlidingExpiration = true;
options.ExpireTimeSpan = TimeSpan.FromDays(1);
});
services.AddMvc(options =>
{
options.Filters.Add(new AutoValidateAntiforgeryTokenAttribute());
options.Filters.Add<AuditUserActionFilter>();
});
(...)
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env, IAntiforgery antiforgery)
{
(...)
app.Use(next => context =>
{
if (context.Request.Path == "/")
{
var tokens = antiforgery.GetAndStoreTokens(context);
context.Response.Cookies.Append("CSRF-TOKEN", tokens.RequestToken, new CookieOptions { HttpOnly = false });
}
return next(context);
});
app.UseAuthentication();
}
}
我相信这是一个关键: 实际上,当我作为访客(未验证)提交 Post
表单时,一切正常。但是在登录后,在提交表单时我收到了一个异常:
Provided antiforgery token meant for a different claims-based user
另外请看下面我的用户认证过程:
if (user != null)
{
await _signInManager.SignOutAsync();
var result = await _signInManager.PasswordSignInAsync(user, details.Password, false, false);
if (result.Succeeded)
{
if (!string.IsNullOrEmpty(returnUrl) && Url.IsLocalUrl(returnUrl))
return Redirect(returnUrl);
else
return RedirectToAction(nameof(Account));
}
else if (result.IsLockedOut || result.IsNotAllowed)
{
ViewBag.Message = "You are not allowed to sign in.";
return View("Error");
}
else
{
ModelState.AddModelError(nameof(UserLoginViewModel.Password), "Incorrect password.");
return View("Index");
}
}
我尝试了 here, and
我的问题的可能原因:
- 对Post的误解 -> 获取表单调用。
- 不了解身份验证 cookie 在 ASP.NET 核心身份验证中的工作原理。
- 对一般身份验证过程的误解。
P.S. 当我删除
options.Filters.Add(new AutoValidateAntiforgeryTokenAttribute());
来自:
services.AddMvc(options =>
{
options.Filters.Add(new AutoValidateAntiforgeryTokenAttribute());
options.Filters.Add<AuditUserActionFilter>();
});
我可以拨打 Post
电话。但我相信,这并不奇怪。
编辑: 由于某种原因,当我从另一个视图(表单在 _Layout 的视图组件中)提交表单而不是 Home/Index(我显示搜索结果的地方)时,没有防伪异常。
是因为HomeController
的Index
动作是Get
吗?添加 Post / Get
属性后:
[AllowAnonymous]
[HttpGet, HttpPost]
public async Task<IActionResult> Index()
我有和以前一样的异常。添加后:
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Index()
我根本无法加载视图,因为它正在请求令牌。
如何绕过它?
在 itminus 建议后我明白了,我必须将我的 Configure
方法和我的 app.UseAuthentication();
移动到顶部。此外,在阅读 documentation I realized, that after using AddMvc 之后,我不再需要引用 "CSRF-TOKEN"
.
最后我的 Startup.cs 看起来像:
public void ConfigureServices(IServiceCollection services)
{
services.AddIdentity<AppUser, IdentityRole>(opts => {
opts.User.RequireUniqueEmail = true;
opts.User.AllowedUserNameCharacters = null; //disable validation
opts.Password.RequiredLength = 8;
opts.Password.RequireNonAlphanumeric = false;
opts.Password.RequireLowercase = false;
opts.Password.RequireUppercase = false;
opts.Password.RequireDigit = true;
})
.AddEntityFrameworkStores<ApplicationDbContext>()
.AddDefaultTokenProviders();
.Services.ConfigureApplicationCookie(options =>
{
//previous cookies not valid
options.SlidingExpiration = true;
options.ExpireTimeSpan = TimeSpan.FromDays(1);
});
services.AddMvc(options =>
{
options.Filters.Add(new AutoValidateAntiforgeryTokenAttribute());
options.Filters.Add<AuditUserActionFilter>();
});
(...)
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env, IAntiforgery antiforgery)
{
app.UseAuthentication();
(...)
}
}