AntiforgeryValidationException:提供的反伪造令牌是针对与当前用户不同的基于声明的用户

时间:2018-07-12 18:55:22

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

我不确定这是怎么回事,但是我在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);
}

}

2 个答案:

答案 0 :(得分:2)

您的示例中的.AspNetCore.Antiforgery.OnvOIX6Mzn8 cookie是通过调用GetAndStoreTokens生成的。该调用生成两个令牌:

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

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

您拥有的代码似乎直接来自docs,这说明:

  

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

向服务器发送需要根据反伪造规则进行验证的请求时,cookie和相应的标头都是必需的。在验证过程中,ASP.NET Core防伪系统将“请求令牌”和“ Cookie令牌”匹配在一起。

在文档中进一步,您将看到:

  

...建议使用本地存储在客户端上存储防伪令牌并将令牌作为请求标头发送。

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

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

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

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

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

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

鉴于这是管道设置代码中的最后一个调用,因此它将仅在MVC管道之后之后运行,并且仅在MVC管道不处理请求的情况下才运行。要解决此问题,请将调用移到上方 UseMvc上-这可能有点过于激进,因为它将产生比所需更多的新“请求令牌”,但是它将确认是否或那不是你的问题。

答案 1 :(得分:0)

默认情况下,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? 如果是这种情况,您可能会生成并接收两个可能导致问题的不同的防伪令牌