我一直在使用 .NET Core 的防伪 Cookie ( https://learn.microsoft.com/en-us/aspnet/core/security/anti-request-forgery ),并且我总是在登录:
// - HomeController.cs
[HttpPost, ValidateAntiForgeryToken]
public async Task<IActionResult> Login()
{
ClaimsIdentity identity = new ClaimsIdentity("myAuthType");
ClaimsPrincipal principal = new ClaimsPrincipal(identity);
await HttpContext.SignInAsync("myScheme", principal,
new AuthenticationProperties
{
ExpiresUtc = DateTime.UtcNow.AddMinutes(10),
AllowRefresh = true,
IsPersistent = true
});
return View();
}
Run Code Online (Sandbox Code Playgroud)
代码完成后,每个调用/视图都会生成一个新的 X-XSRF-Cookie 并发送到浏览器,浏览器会为某些 Api 调用发送回该值。其代码是:
// - Startup.cs
public void ConfigureServices(IServiceCollection services)
{
services.AddAntiforgery(o => o.HeaderName = CrossSiteAntiForgery.Header);
services.AddMvc(o => {
o.Filters.AddService(typeof(CrossSiteAntiForgery));
});
}
// - CrossSiteAntiForgery.cs
public class CrossSiteAntiForgery : ResultFilterAttribute
{
public const string Cookie = "XSRF-TOKEN";
public const string Header = "X-XSRF-TOKEN";
private IAntiforgery _antiforgery;
public CrossSiteAntiForgery(IAntiforgery antiforgery)
{
_antiforgery = antiforgery;
}
public override void OnResultExecuting(ResultExecutingContext context)
{
AntiforgeryTokenSet tokens = _antiforgery.GetAndStoreTokens(context.HttpContext);
context.HttpContext.Response.Cookies.Append(Cookie, tokens.RequestToken, new CookieOptions() { HttpOnly = false });
}
}
Run Code Online (Sandbox Code Playgroud)
但我想通过 Ajax 登录只是因为。所以我保留了所有内容并仅更改了登录名:
[HttpPost("Api/User/Login"), ValidateAntiForgeryToken, Produces("application/json")]
public async Task<IActionResult> Login()
{
var userFromDB = DB.GetUser(1);
ClaimsIdentity identity = new ClaimsIdentity("myAuthType");
ClaimsPrincipal principal = new ClaimsPrincipal(identity);
await HttpContext.SignInAsync("myScheme", principal,
new AuthenticationProperties
{
ExpiresUtc = DateTime.UtcNow.AddMinutes(10),
AllowRefresh = true,
IsPersistent = true
});
return Json(userFromDB);
}
Run Code Online (Sandbox Code Playgroud)
但是现在我收到错误(错误请求),因为 XSRF 令牌不匹配。经过一些(实际上很多)挖掘后,我想我发现了问题,浏览器Set-Cookie确实从ajax正确接收了标头并设置了新的cookie值,但该值无效,因为似乎每当_antiforgery.GetAndStoreTokens(HttpContext)调用时,它都会使用当前的值用户以某种方式生成令牌。当我返回新视图时,上下文将使用新(登录)用户进行更新,生成令牌并将其发送到浏览器,一切正常。当我使用 ajax 和 call 时HttpContext.SignInAsync(),当前用户尚未更新,因此生成的令牌对于登录用户将无效。
// - Login()
var user = HttpContext.User;
await HttpContext.SignInAsync("Login", principal,
new AuthenticationProperties
{
ExpiresUtc = DateTime.UtcNow.AddMinutes(10),
AllowRefresh = true,
IsPersistent = true
});
var loggedInUser = HttpContext.User;
bool truth = user.Equals(loggedInUser); // - true
// - meaning anything that relies on the new logged in User is invalid from here on.
Run Code Online (Sandbox Code Playgroud)
.NET 控制台中的错误消息是The provided antiforgery token was meant for a different claims-based user。我在这里尝试了这个建议,它看起来很老套,它只适用于第二个请求,这意味着用户需要为每个用户更改(登录和注销)看到第一个“错误请求”。登录后如何获取/更新用户,以便 XSRF 令牌生成正常工作?或者,如果有更好的解决方案,我该如何解决这个问题?
编辑:我现在正在解决这个问题,以防有人遇到同样的问题:
public async Task<IActionResult> Login()
{
// - Login code omitted for brevity.
return RedirectToAction("LoginUnelegantWorkaround");
}
public IActionResult LoginUnelegantWorkaround()
{
var model = DB.FetchModelIWasGoingToReturnBefore();
return Json(model);
// - token is properly generated now since User has been updated, everything works
}
// - Same thing needed for Logout
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
2332 次 |
| 最近记录: |