【问题标题】:AntiforgeryValidationException: The provided anti forgery token was meant for a different claims-based user than the current userAntiforgeryValidationException:提供的防伪令牌用于与当前用户不同的基于声明的用户
【发布时间】:2018-07-12 18:55:22
【问题描述】:

我不确定这里发生了什么,但我在我的 cookie 中看到了 2 个不同的标记。一个是“XSRF-TOKEN”,一个是“.AspNetCore.Antiforgery.OnvOIX6Mzn8”,它们的值不同。

我正在使用 ASP.Net Core 2.1,设置了 SPA(以及前端的 Angular),并且我在 Startup.cs 中有以下内容。

我不知道是什么创建了后一个令牌,因为它似乎不是来自我添加的任何代码。

public void Configure(IApplicationBuilder app, IHostingEnvironment env, IAntiforgery antiforgery)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
    }
    else
    {
        app.UseExceptionHandler(
            builder =>
            {
            builder.Run(
                async context =>
                {
                context.Response.StatusCode = (int)HttpStatusCode.InternalServerError;
                context.Response.Headers.Add("Access-Control-Allow-Origin", "*");

                var error = context.Features.Get<IExceptionHandlerFeature>();
                if (error != null)
                {
                    context.Response.AddApplicationError(error.Error.Message);
                    await context.Response.WriteAsync(error.Error.Message).ConfigureAwait(false);
                }
                });
            });
    }

    app.UseAuthentication();
    app.UseHttpsRedirection();
    app.UseDefaultFiles();
    app.UseStaticFiles();
    app.UseJwtTokenMiddleware();
    app.UseSpaStaticFiles();
    app.UseCookiePolicy();

    app.UseMvc(routes =>
    {
            routes.MapRoute(
                name: "default",
                template: "{controller}/{action=Index}/{id?}");
        });

        app.UseSpa(spa =>
        {
            spa.Options.SourcePath = "ClientApp";
            spa.UseSpaPrerendering(options =>
            {
                options.BootModulePath = $"{spa.Options.SourcePath}/dist/server/main.js";
                options.BootModuleBuilder = env.IsDevelopment()
                    ? new AngularCliBuilder(npmScript: "build:ssr")
                    : null;
                options.ExcludeUrls = new[] { "/sockjs-node" };
            });

            if (env.IsDevelopment())
            {
                spa.UseAngularCliServer(npmScript: "start");
            }
        });

        app.UseMiddleware<AntiForgeryMiddleware>("XSRF-TOKEN");
    }
}

public static class ApplicationBuilderExtensions
{
    public static IApplicationBuilder UseAntiforgeryTokenMiddleware(this IApplicationBuilder builder, string requestTokenCookieName)
    {
        return builder.UseMiddleware<AntiForgeryMiddleware>(requestTokenCookieName);
    }
}

public class AntiForgeryMiddleware
{
    private readonly RequestDelegate next;
    private readonly string requestTokenCookieName;
    private readonly string[] httpVerbs = new string[] { "GET", "HEAD", "OPTIONS", "TRACE" };

public AntiForgeryMiddleware(RequestDelegate next, string requestTokenCookieName)
{
    this.next = next;
    this.requestTokenCookieName = requestTokenCookieName;
}

public async Task Invoke(HttpContext context, IAntiforgery antiforgery)
{
    if (httpVerbs.Contains(context.Request.Method, StringComparer.OrdinalIgnoreCase))
    {
        var tokens = antiforgery.GetAndStoreTokens(context);

        context.Response.Cookies.Append(requestTokenCookieName, tokens.RequestToken, new CookieOptions()
        {
            HttpOnly = false
        });
    }

    await next.Invoke(context);
}

}

【问题讨论】:

  • 我没有使用表单标签助手。我在最初的帖子中没有提到它,但我已经更新它说它有一个 Angular SPA,并且它是预渲染的。我不使用 Razor。

标签: asp.net-mvc asp.net-core asp.net-core-mvc


【解决方案1】:

您示例中的.AspNetCore.Antiforgery.OnvOIX6Mzn8 cookie 是通过调用GetAndStoreTokens 生成的。此调用生成 两个 标记:

  1. “Cookie 令牌”:写成 .AspNetCore.Antiforgery.OnvOIX6Mzn8
  2. “请求令牌”:这是与“Cookie 令牌”配对的单独令牌。您自己将这个值写成 cookie(即XSRF-TOKEN),如您的代码所示。

如果您检查生成值tokens 的值,您可以在示例中看到这一点。这有两个感兴趣的属性:RequestTokenCookieTokenGetAndStoreTokens 调用正在写出 CookieToken 值,而您的代码正在写出 RequestToken 值,这就解释了为什么您会在两个不同的 cookie 中看到两个不同的值。

您拥有的代码似乎直接来自docs,它解释说:

...使用应用主页中的中间件生成防伪令牌并将其作为 cookie 在响应中发送(使用本主题后面描述的默认 Angular 命名约定)...

向需要根据防伪规则验证的服务器发送请求时,cookie 和相应的 header 都需要。 ASP.NET Core Antiforgery 系统在验证过程中将“Request Token”和“Cookie Token”匹配在一起。

在文档的更下方,您会看到:

...使用本地存储在客户端存储防伪令牌并将令牌作为请求标头发送是推荐的方法。

下面的 JavaScript 示例显示了如何使用自定义请求标头(即 RequestVerificationToken)将 RequestToken 值添加到 XHR 请求中。文档还显示,在 ASP.NET Core DI 系统中注册 Antiforgery 服务时可以更改此标头:

services.AddAntiforgery(options => options.HeaderName = "X-CSRF-TOKEN");

您需要在 Angular 应用程序中读取 XSRF-TOKEN cookie 的值,并将其与您的 API 请求一起发送回服务器。您可以使用默认标题名称(如上所述)或自定义它(也如上所述)。正如@Sal 在他的回答中指出的那样,只要 cookie 名称是 XSRF-TOKEN (this is also the same for Angular),AngularJs 就有一个内置机制。

希望这可以解释这两个令牌和一般的防伪过程。但是,鉴于您正在为 Angular 的默认 HttpClient XSRF 保护写出一个正确命名的 cookie,我希望所有这方面的配置都正确。

关于你的问题可能出在哪里,我怀疑这条线的位置:

app.UseMiddleware<AntiForgeryMiddleware>("XSRF-TOKEN");

鉴于这是您的管道设置代码中的最后一次调用,它只会在 MVC 管道之后运行,并且仅在 MVC 管道不处理请求的情况下运行。要解决这个问题,请将调用 above UseMvc - 这可能有点激进,因为它会生成不必要的新“请求令牌”,但它会确认这是否是你的问题。

【讨论】:

  • 我尝试将它放在其他地方(包括在 UseMvc 之上),但我仍然遇到同样的问题。
【解决方案2】:

默认情况下,AspNetCore 会使用“AspNetCore.AntiForgery.XXX”(除非您的配置重命名)创建一个 cookie,以防止 xsrf/csrf 攻击。

我还在MSDN article 中读到,AngularJS 也有一种自动处理 xsrf/csrf 场景的方法:

AngularJS 使用约定来处理 CSRF。如果服务器发送一个名为 XSRF-TOKEN 的 cookie,AngularJS $http 服务会添加 向服务器发送请求时将 cookie 值添加到标头。这 过程是自动的。标头不需要显式设置。 标头名称为 X-XSRF-TOKEN。服务器应检测到此标头 并验证其内容。 对于 ASP.NET Core API 使用此约定: 配置您的应用程序以在名为 XSRF-TOKEN 的 cookie 中提供令牌。 配置防伪服务以查找名为 X-XSRF-TOKEN 的标头。

services.AddAntiforgery(options => options.HeaderName = "X-XSRF-TOKEN");

您的应用程序是否配置为同时生成默认的 AntiForgery cookie 和 XSRF-TOKEN? 如果是这种情况,您可能会生成并接收两个不同的防伪令牌,这可能会导致问题

【讨论】:

  • 我看不到其他 AspNetCore.Antiforgery.XXX 是在哪里生成的。如果我取出我的自定义代码 ("app.UseMiddleware("XSRF-TOKEN");"),它不会生成任何一个令牌。
  • 您能否尝试删除您的自定义代码并尝试从Startup.ConfigureService 自定义令牌,例如:services.AddAntiforgery(options =&gt; { options.CookieName = "XSRF-TOKEN"; options.HeaderName = "XSRF-TOKEN"; });
  • 我没有拿回任何令牌。 AddAntiforgery 代码不会显式发送令牌。 (请注意,我使用的是 SpaPrerendering。)
猜你喜欢
  • 1970-01-01
  • 2018-08-01
  • 2018-01-12
  • 1970-01-01
  • 2016-03-19
  • 2015-02-01
  • 2013-12-18
  • 2020-05-31
  • 2013-04-30
相关资源
最近更新 更多