【问题标题】:Antiforgery exception (Provided antiforgery token meant for a different claims-based user) on post method, when user is authenticated当用户通过身份验证时,发布方法上的防伪异常(为基于声明的不同用户提供的防伪令牌)
【发布时间】:2020-05-31 11:52:09
【问题描述】:

我正在使用 ASP.NET Core 2.2AspNetCore.Authentication

我正在尝试在我的cshtml _Layout 的视图组件表单中使用Post 调用:

<form asp-controller="Home" asp-action="SearchResults" method="post" enctype="multipart/form-data" asp-antiforgery="true" novalidate>

(...)

</form>

我的意图是用搜索属性掩盖 URL,以使网页抓取和 抓取 更加困难。我的Post 方法正在更新我的控制器的静态字段,并重定向到我的索引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 身份/身份验证设置:

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 表单时,一切正常。但是登录后,在提交表单时,我收到了一个异常:

为基于声明的不同用户提供了防伪令牌

还请在下面找到我的用户身份验证过程:

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");
                    }
                }

我尝试了herehere 的几种方法,但没有结果。但它们适用于旧版本的 ASP.NET Core。我不确定我应该如何为经过身份验证的用户更新我的防伪令牌?

我的问题的可能原因:

  1. 对 Post 的误解 -> 获取表单调用。
  2. 不了解身份验证 cookie 在 ASP.NET Core 身份验证中的工作原理。
  3. 对身份验证过程的一般误解。

附言 当我删除

options.Filters.Add(new AutoValidateAntiforgeryTokenAttribute());

来自:

services.AddMvc(options =>
            {
                options.Filters.Add(new AutoValidateAntiforgeryTokenAttribute());
                options.Filters.Add<AuditUserActionFilter>();
            });

我可以拨打Post 电话。但我相信,这并不奇怪。

编辑: 由于某种原因,当我从另一个视图(表单在 _Layout 的视图组件中)而不是 Home/Index(我显示搜索结果的地方)提交表单时,没有防伪异常。

是因为HomeControllerIndex动作是Get吗?添加Post / Get属性后:

[AllowAnonymous]
        [HttpGet, HttpPost]
        public async Task<IActionResult> Index()

我有和以前一样的例外。添加后:

[AllowAnonymous]
        [ValidateAntiForgeryToken]
        public async Task<IActionResult> Index()

我根本无法加载视图,因为它正在请求令牌。

如何绕过?

【问题讨论】:

  • 但登录后,在提交表单时我收到一个异常:请注意,调用您的UseAuthentication() 为时已晚。至少,在调用访问 CSRF 令牌的中间件之前放置它。
  • 谢谢你,其实你帮助我理解了我的错误。

标签: c# authentication cookies asp.net-core-2.2 antiforgerytoken


【解决方案1】:

itminus 建议之后我明白了,我必须将我的 Configure 方法和 app.UseAuthentication(); 移到顶部。另外,在阅读documentation 之后,我意识到,在使用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();

            (...)
        }
    }

【讨论】:

    猜你喜欢
    • 2018-08-01
    • 1970-01-01
    • 1970-01-01
    • 2018-01-12
    • 1970-01-01
    • 2016-03-19
    • 1970-01-01
    • 2015-02-01
    • 2013-12-18
    相关资源
    最近更新 更多