我建立一个单页的应用程序,并遇到了防伪标记的问题。
我知道为什么会发生问题,我只是不知道如何解决它。
我得到的错误,当出现以下情况:
- 非登录用户加载对话框(与所生成的防伪标记)
- 用户关闭对话框
- 用户登录
- 用户打开同一个对话框
- 用户提交表单对话框
反伪造令牌是为用户“”但目前的用户是“用户名”
出现这种情况的原因是因为我的应用程序是100%的单页,和当用户成功通过Ajax帖子登录/Account/JsonLogin
,我简单地切换出来的当前观点,从服务器返回的“认证的意见”,但不重新加载页面。
我知道这是什么原因,因为如果我简单的重装步骤3和4之间的页面,没有任何错误。
如此看来, @Html.AntiForgeryToken()
在加载的形式,直到重新加载页面仍返回旧用户的令牌。
如何更改@Html.AntiForgeryToken()
返回一个令牌为新的,经过验证的用户?
我注入了新的GenericalPrincipal
使用自定义IIdentity
每Application_AuthenticateRequest
这样的时候@Html.AntiForgeryToken()
被调用HttpContext.Current.User.Identity
,其实我与自定义标识IsAuthenticated
属性设置为true,但@Html.AntiForgeryToken
似乎仍然呈现为老用户令牌,除非我做一个页面重新加载。
Answer 1:
发生这种情况,因为防伪标志嵌入用户的用户名作为更好的验证的加密令牌的一部分。 当你第一次调用@Html.AntiForgeryToken()
的用户没有这样的令牌登录将会对用户名空字符串,在用户登录后,如果不更换防伪令牌也不会通过验证因为最初的标志是为匿名用户,现在我们有一个已知用户名的身份验证的用户。
您有几种选择来解决这个问题:
就在这个时候让你的SPA做了充分的POST并在页面重新加载它会与嵌入式更新的用户名的防伪标记。
曾经使用过的局部视图@Html.AntiForgeryToken()
和登录之后,做另外一个AJAX请求并经请求的响应替换现有的防伪标记。
只是禁用的身份检查防伪验证执行。 以下内容添加到您的Application_Start方法: AntiForgeryConfig.SuppressIdentityHeuristicChecks = true
。
Answer 2:
为了修正这个错误,你需要放置OutputCache
数据标注在获取ActionResult
登录页面为:
[OutputCache(NoStore=true, Duration = 0, VaryByParam= "None")]
public ActionResult Login(string returnUrl)
Answer 3:
它发生了很多次我的申请,所以我决定谷歌吧!
我发现这个错误的一个简单的解释! 用户在双击按钮登录! 你可以看到其他用户谈论下面的链接:
MVC 4提供的防伪标记是为那些用户“”但目前的用户是“用户”
我希望它能帮助! =)
Answer 4:
我有同样的问题,而这个肮脏的黑客得到它固定,至少要等到我可以在一个更清洁的方式解决它。
public ActionResult Login(string returnUrl)
{
if (AuthenticationManager.User.Identity.IsAuthenticated)
{
AuthenticationManager.SignOut();
return RedirectToAction("Login");
}
...
Answer 5:
我在注册过程中的相当具体的还没有类似的问题。 一旦用户发送到他们的电子邮件中的链接上点击,他们会先登录并直接发送到一个帐户详细信息屏幕填写一些信息。 我的代码是:
Dim result = Await UserManager.ConfirmEmailAsync(userId, code)
If result.Succeeded Then
Dim appUser = Await UserManager.FindByIdAsync(userId)
If appUser IsNot Nothing Then
Dim signInStatus = Await SignInManager.PasswordSignInAsync(appUser.Email, password, True, shouldLockout:=False)
If signInStatus = SignInStatus.Success Then
Dim identity = Await UserManager.CreateIdentityAsync(appUser, DefaultAuthenticationTypes.ApplicationCookie)
AuthenticationManager.SignIn(New AuthenticationProperties With {.IsPersistent = True}, identity)
Return View("AccountDetails")
End If
End If
End If
我发现,返回查看(“AccountDetails”)是给我的令牌例外,我猜是因为ConfirmEmail功能与使用AllowAnonymous装饰,但AccountDetails功能有ValidateAntiForgeryToken。
更改返回到返回RedirectToAction(“AccountDetails”)解决了这个问题对我来说。
Answer 6:
我有发生的大部分时间在生产服务器上相同的异常。
为什么会发生?
它发生时,具有有效凭据的用户登录和登录后,然后重定向到另一个页面,并在压制后的后退按钮会显示登录页面,再次考入有效凭据时,将会出现此异常。
怎么解决?
只需添加这条线和工作完美,没有出现错误。
[OutputCache(NoStore = true, Duration = 0, VaryByParam = "None")]
Answer 7:
[OutputCache(NoStore=true, Duration=0, VaryByParam="None")]
public ActionResult Login(string returnUrl)
您可以通过将一个破发点上的登录(获取)行动的第一线进行测试。 之前添加OutputCache指令断点将在第一负载被击中,但经过点击浏览器的后退按钮,它不会。 添加指令后,您应该结束了断点被击中每一次,所以AntiForgeryToken将是corect一个,而不是空单。
Answer 8:
我有一个单页的ASP.NET MVC的核心应用同样的问题。 我通过设置解决它HttpContext.User
,其中改变当前的身份声明(因为MVC不仅会为后续的请求,作为讨论的所有控制器的动作在这里 )。 我用了一个结果,而不是过滤器中间件的防伪饼干追加到我的反应,这让确保他们的MVC行动已经返回后只产生。
控制器(NB我管理用户与ASP.NET核心身份):
[Authorize]
[ValidateAntiForgeryToken]
public class AccountController : Controller
{
private SignInManager<IdentityUser> signInManager;
private UserManager<IdentityUser> userManager;
private IUserClaimsPrincipalFactory<IdentityUser> userClaimsPrincipalFactory;
public AccountController(SignInManager<IdentityUser> signInManager, UserManager<IdentityUser> userManager, IUserClaimsPrincipalFactory<ApplicationUser> userClaimsPrincipalFactory)
{
this.signInManager = signInManager;
this.userManager = userManager;
this.userClaimsPrincipalFactory = userClaimsPrincipalFactory;
}
[HttpPost]
[AllowAnonymous]
public async Task<IActionResult> Login(string username, string password)
{
if (username == null || password == null)
{
return BadRequest(); // Alias of 400 response
}
var result = await signInManager.PasswordSignInAsync(username, password, false, lockoutOnFailure: false);
if (result.Succeeded)
{
var user = await userManager.FindByNameAsync(username);
// Must manually set the HttpContext user claims to those of the logged
// in user. Otherwise MVC will still include a XSRF token for the "null"
// user and token validation will fail. (MVC appends the correct token for
// all subsequent reponses but this isn't good enough for a single page
// app.)
var principal = await userClaimsPrincipalFactory.CreateAsync(user);
HttpContext.User = principal;
return Json(new { username = user.UserName });
}
else
{
return Unauthorized();
}
}
[HttpPost]
public async Task<IActionResult> Logout()
{
await signInManager.SignOutAsync();
// Removing identity claims manually from the HttpContext (same reason
// as why we add them manually in the "login" action).
HttpContext.User = null;
return Json(new { result = "success" });
}
}
结果过滤器附加防伪饼干:
public class XSRFCookieFilter : IResultFilter
{
IAntiforgery antiforgery;
public XSRFCookieFilter(IAntiforgery antiforgery)
{
this.antiforgery = antiforgery;
}
public void OnResultExecuting(ResultExecutingContext context)
{
var HttpContext = context.HttpContext;
AntiforgeryTokenSet tokenSet = antiforgery.GetAndStoreTokens(context.HttpContext);
HttpContext.Response.Cookies.Append(
"MyXSRFFieldTokenCookieName",
tokenSet.RequestToken,
new CookieOptions() {
// Cookie needs to be accessible to Javascript so we
// can append it to request headers in the browser
HttpOnly = false
}
);
}
public void OnResultExecuted(ResultExecutedContext context)
{
}
}
Startup.cs提取:
public partial class Startup
{
public Startup(IHostingEnvironment env)
{
//...
}
public IConfigurationRoot Configuration { get; }
public void ConfigureServices(IServiceCollection services)
{
//...
services.AddAntiforgery(options =>
{
options.HeaderName = "MyXSRFFieldTokenHeaderName";
});
services.AddMvc(options =>
{
options.Filters.Add(typeof(XSRFCookieFilter));
});
services.AddScoped<XSRFCookieFilter>();
//...
}
public void Configure(
IApplicationBuilder app,
IHostingEnvironment env,
ILoggerFactory loggerFactory)
{
//...
}
}
Answer 9:
当你登录时,你已经验证显示的消息。
该助手就是来做同样的事情[ValidateAntiForgeryToken]
属性。
System.Web.Helpers.AntiForgery.Validate()
删除[ValidateAntiForgeryToken]
从控制器attribut并把这个帮手行动梅索德。
因此,当用户已经authentificated,重定向到主页,或者如果不继续有效的防伪标记的该验证后的验证。
if (User.Identity.IsAuthenticated)
{
return RedirectToAction("Index", "Home");
}
System.Web.Helpers.AntiForgery.Validate();
要尝试重现错误,步骤如下:如果你是你的登录页面上,你是不是验证。 如果复制的标签,您与第二个选项卡登录。 如果你回到登录页面上的第一个选项卡,并尝试登录,而不会重新加载页面......你有这样的错误。
Answer 10:
已经与互联网商店防伪令牌验证一个问题:用户打开多个标签页(货物)和一个尝试登录后登录另一个和得到了这样AntiForgeryException。 所以,AntiForgeryConfig.SuppressIdentityHeuristicChecks =真没有帮助我,所以我用这么丑hackfix,也许这将是有人帮助:
public class ExceptionPublisherExceptionFilter : IExceptionFilter
{
public void OnException(ExceptionContext exceptionContext)
{
var exception = exceptionContext.Exception;
var request = HttpContext.Current.Request;
if (request != null)
{
if (exception is HttpAntiForgeryException &&
exception.Message.ToLower().StartsWith("the provided anti-forgery token was meant for user \"\", but the current user is"))
{
var isAjaxCall = string.Equals("XMLHttpRequest", request.Headers["x-requested-with"], StringComparison.OrdinalIgnoreCase);
var returnUrl = !string.IsNullOrWhiteSpace(request["returnUrl"]) ? request["returnUrl"] : "/";
var response = HttpContext.Current.Response;
if (isAjaxCall)
{
response.Clear();
response.StatusCode = 200;
response.ContentType = "application/json; charset=utf-8";
response.Write(JsonConvert.SerializeObject(new { success = 1, returnUrl = returnUrl }));
response.End();
}
else
{
response.StatusCode = 200;
response.Redirect(returnUrl);
}
}
}
ExceptionHandler.HandleException(exception);
}
}
public class FilterConfig
{
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new ExceptionPublisherExceptionFilter());
filters.Add(new HandleErrorAttribute());
}
}
认为这将是巨大的,如果防伪令牌生成选项可以设置,以排除用户名或类似的东西。
文章来源: Anti forgery token is meant for user “” but the current user is “username”