[英]ASP.NET Core WebAPI: Validation of the provided antiforgery token failed. The cookie token and the request token were swapped
[英]ASP.NET Core 3.1 URL link to page that requires authorisation giving error Antiforgery token validation failed
我正在运行一个使用 ASP.NET 核心 3.1.8 的网站,使用 MS Identity 框架。 效果很好,网站已经上线一年了。 我想在电子邮件中向用户提供一个链接,然后当他们单击该链接时,它会将他们带到需要他们登录才能查看的页面。 中间件重定向到登录页面,用户登录,然后返回目标页面。
这工作正常,除非用户已经在他们的浏览器中打开了一个选项卡并且已经登录。用户单击链接并被重定向到登录页面(不确定为什么),然后他们登录到错误页面 400 作为已抛出异常:“防伪令牌验证失败。防伪 cookie 令牌和请求令牌不匹配”
如果我手动将该链接复制并粘贴到浏览器中,它不会重定向到登录页面,而是直接进入该页面(因为我已经登录),即使我只是在 Notepad++ 中写入 URL 并双击它,它直接进入页面。 如果 URL 显示在<a href="https://url-to-page">...</a>
并且我单击它以将我带到我要访问的页面,这似乎只是一个问题得到问题。 我确实想知道是否与跨站点源相关并删除了 CORS 中间件并将 cookie samesite 规则更改为 none,但这没有区别。
这是来自 Startup.cs 的相关部分
public void ConfigureServices(IServiceCollection services)
{
services.Configure<CookiePolicyOptions>(options =>
{
// This lambda determines whether user consent for non-essential cookies is needed for a given request.
options.CheckConsentNeeded = context => true;
options.MinimumSameSitePolicy = SameSiteMode.Strict;
});
if (HostingEnvironment.IsStaging())
{
// For migrations:
// Set environment variable ASPNETCORE_ENVIRONMENT to Staging for MariaDbContext
// $Env:ASPNETCORE_ENVIRONMENT = "Staging"
// Add-Migration -Context MariaDbContext <migration name>
services.AddDbContext<AppDbContext, MariaDbContext>();
}
else
{
// For migrations:
// Set environment variable ASPNETCORE_ENVIRONMENT to Staging for MariaDbContext
// $Env:ASPNETCORE_ENVIRONMENT = "Development"
// Add-Migration -Context AppDbContext <migration name>
services.AddDbContext<AppDbContext>(options =>
{
options.UseSqlServer(Configuration.GetConnectionString("IdentityContextConnection"));
});
}
services.Configure<RecaptchaSettings>(Configuration.GetSection("RecaptchaSettings"));
// Add singleton services
services.AddSingleton<IEmailSenderSTC, EmailSender>();
services.AddSingleton<ISimpleLogger, SimpleLoggerFile>(x => new SimpleLoggerFile(Configuration.GetValue<string>("LogFile"), LogLevel.Information));
// Add transient services
services.AddTransient<INewsRepository, NewsRepository>();
services.AddTransient<IGalleryRepository, GalleryRepository>();
services.AddTransient<ILadderRepository, LadderRepository>();
services.AddTransient<IFileSystem, FileSystem>();
services.AddTransient<ISettingsRepository, SettingsRepository>();
services.AddTransient<IClubSparkMembersRepository, ClubSparkMembersRepository>();
// Add environment specific services
if (HostingEnvironment.IsDevelopment())
{
services.AddSingleton<ISmarterMailRepository, SmarterMailRepoStub>();
services.AddTransient<IRecaptchaService, RecaptchaServiceStub>();
}
else if (HostingEnvironment.IsStaging())
{
services.AddTransient<ISmarterMailRepository, SmarterMailRepoStub>();
services.AddTransient<IRecaptchaService, RecaptchaServiceStub>();
}
else
{
services.AddTransient<ISmarterMailRepository, SmarterMailRepo>();
services.AddTransient<IRecaptchaService, RecaptchaService>();
}
// Staging environment is on Pi2 with HTTP only so cookie security policy needs to be relaxed
var cookieSecurePolicy = HostingEnvironment.IsStaging() ? CookieSecurePolicy.SameAsRequest : CookieSecurePolicy.Always;
services.AddSession(options =>
{
options.Cookie.HttpOnly = true;
options.IdleTimeout = TimeSpan.FromMinutes(20);
options.Cookie.IsEssential = true;
options.Cookie.SameSite = SameSiteMode.Strict;
options.Cookie.SecurePolicy = cookieSecurePolicy;
});
services.AddIdentity<MemberIdentity, IdentityRole>(options =>
{
options.Password.RequireDigit = true;
options.Password.RequiredLength = 8;
options.Password.RequireUppercase = true;
options.Password.RequireNonAlphanumeric = false;
options.User.RequireUniqueEmail = true;
options.SignIn.RequireConfirmedEmail = true;
}).AddEntityFrameworkStores<AppDbContext>()
.AddDefaultTokenProviders()
.AddUserManager<AppUserManager>();
services.ConfigureApplicationCookie(options =>
{
options.AccessDeniedPath = "/Identity/Account/AccessDenied";
options.LoginPath = "/Identity/Account/Login";
options.LogoutPath = "/Identity/Account/Logout";
options.ExpireTimeSpan = TimeSpan.FromMinutes(30);
options.SlidingExpiration = true;
options.Cookie.HttpOnly = true;
options.Cookie.SecurePolicy = cookieSecurePolicy;
options.Cookie.SameSite = SameSiteMode.Strict;
options.ReturnUrlParameter = CookieAuthenticationDefaults.ReturnUrlParameter;
options.Events.OnRedirectToLogin = ctx =>
{
if (ctx.Request.Path.StartsWithSegments("/api") && ctx.Response.StatusCode == StatusCodes.Status200OK)
{
ctx.Response.StatusCode = StatusCodes.Status401Unauthorized;
}
else
{
ctx.Response.Redirect(ctx.RedirectUri);
}
return Task.CompletedTask;
};
});
services.Configure<CookieTempDataProviderOptions>(options =>
{
options.Cookie.IsEssential = true;
options.Cookie.HttpOnly = true;
options.Cookie.SecurePolicy = cookieSecurePolicy;
options.Cookie.SameSite = SameSiteMode.Strict;
});
services.AddCors(options =>
{
options.AddPolicy("CorsPolicy",
builder => builder.AllowAnyOrigin()
.AllowAnyMethod()
.AllowAnyHeader());
});
services.AddMemoryCache();
services.AddControllersWithViews();
services.AddRazorPages();
//services.AddMvc(options =>
//{
// options.EnableEndpointRouting = false;
//});
services.AddHsts(options =>
{
options.Preload = true;
options.MaxAge = TimeSpan.FromDays(365);
options.IncludeSubDomains = true;
});
services.AddHttpsRedirection(options =>
{
options.HttpsPort = 443;
options.RedirectStatusCode = StatusCodes.Status307TemporaryRedirect;
});
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env, AppUserManager userManager,
RoleManager<IdentityRole> roleManager, AppDbContext context, ISettingsRepository settingsRepository)
{
if (!env.IsProduction())
context.Database.Migrate();
if (env.IsDevelopment() || env.IsStaging())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Home/Error");
app.UseHsts();
}
if (!env.IsStaging())
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseCookiePolicy();
app.UseCors("CorsPolicy");
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseSession();
app.UseStatusCodePagesWithRedirects("/Error?code={0}");
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
endpoints.MapRazorPages();
});
}
我有使用属性 [Authorize] 设置 URL 位置的控制器,而 Action 具有 [HttpGet] 属性
app.UseCors("CorsPolicy");
// app.UseSignalR(routes =>
//{
//});
app.UseHttpsRedirection();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Landing}/{id?}");
endpoints.MapRazorPages();
});
这样做是因为在核心 2.2.8 中你可以写
UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
});
但是 3.0 以后的 UseEndPoint 代替了 MVC 的路由映射
好的,所以看起来它可能都与去年左右发生的与浏览器中的 SameSite 更改有关的更改有关。 ASP.Net Core 3.1 中的 SameSite
@Brando Zhang 说:“我猜你的cookie无法通过的原因是你的cookie设置。我建议你可以尝试将options.MinimumSameSitePolicy设置为none,然后再试一次。”
我将 CookiePolicyOption MinimumSameSitePolicy 更改为 SameSiteMode.Lax 并删除了 ConfigureApplicationCookie options.Cookie.SameSite = SameSiteMode.Strict,而不是 'SameSiteMode.None',这样它基本上就会选择 SameSiteMode.Lax 的默认值。 这似乎解决了我的问题。
这是我更新的 ConfigureServices 代码
public void ConfigureServices(IServiceCollection services)
{
if (HostingEnvironment.IsStaging())
{
// For migrations:
// Set environment variable ASPNETCORE_ENVIRONMENT to Staging for MariaDbContext
// $Env:ASPNETCORE_ENVIRONMENT = "Staging"
// Add-Migration -Context MariaDbContext <migration name>
services.AddDbContext<AppDbContext, MariaDbContext>();
}
else
{
// For migrations:
// Set environment variable ASPNETCORE_ENVIRONMENT to Staging for MariaDbContext
// $Env:ASPNETCORE_ENVIRONMENT = "Development"
// Add-Migration -Context AppDbContext <migration name>
services.AddDbContext<AppDbContext>(options =>
{
options.UseSqlServer(Configuration.GetConnectionString("IdentityContextConnection"));
});
}
services.Configure<RecaptchaSettings>(Configuration.GetSection("RecaptchaSettings"));
// Add singleton services
services.AddSingleton<IEmailSenderSTC, EmailSender>();
services.AddSingleton<ISimpleLogger, SimpleLoggerFile>(x => new SimpleLoggerFile(Configuration.GetValue<string>("LogFile"), LogLevel.Information));
// Add transient services
services.AddTransient<INewsRepository, NewsRepository>();
services.AddTransient<IGalleryRepository, GalleryRepository>();
services.AddTransient<ILadderRepository, LadderRepository>();
services.AddTransient<IFileSystem, FileSystem>();
services.AddTransient<ISettingsRepository, SettingsRepository>();
services.AddTransient<IClubSparkMembersRepository, ClubSparkMembersRepository>();
// Add environment specific services
if (HostingEnvironment.IsDevelopment())
{
services.AddSingleton<ISmarterMailRepository, SmarterMailRepo>();
services.AddTransient<IRecaptchaService, RecaptchaServiceStub>();
}
else if (HostingEnvironment.IsStaging())
{
services.AddTransient<ISmarterMailRepository, SmarterMailRepoStub>();
services.AddTransient<IRecaptchaService, RecaptchaServiceStub>();
}
else
{
services.AddTransient<ISmarterMailRepository, SmarterMailRepo>();
services.AddTransient<IRecaptchaService, RecaptchaService>();
}
services.Configure<CookiePolicyOptions>(options =>
{
// This lambda determines whether user consent for non-essential cookies is needed for a given request.
options.CheckConsentNeeded = context => true;
options.MinimumSameSitePolicy = SameSiteMode.Lax;
options.Secure = HostingEnvironment.IsStaging() ? CookieSecurePolicy.SameAsRequest : CookieSecurePolicy.Always;
});
services.AddSession(options =>
{
options.Cookie.HttpOnly = true;
options.IdleTimeout = TimeSpan.FromMinutes(20);
options.Cookie.IsEssential = true;
options.Cookie.SameSite = SameSiteMode.Strict;
});
services.AddIdentity<MemberIdentity, IdentityRole>(options =>
{
options.Password.RequireDigit = true;
options.Password.RequiredLength = 8;
options.Password.RequireUppercase = true;
options.Password.RequireNonAlphanumeric = false;
options.User.RequireUniqueEmail = true;
options.SignIn.RequireConfirmedEmail = true;
}).AddEntityFrameworkStores<AppDbContext>()
.AddDefaultTokenProviders()
.AddUserManager<AppUserManager>();
services.ConfigureApplicationCookie(options =>
{
options.AccessDeniedPath = "/Identity/Account/AccessDenied";
options.LoginPath = "/Identity/Account/Login";
options.LogoutPath = "/Identity/Account/Logout";
options.ExpireTimeSpan = TimeSpan.FromMinutes(30);
options.SlidingExpiration = true;
options.Cookie.HttpOnly = true;
options.ReturnUrlParameter = CookieAuthenticationDefaults.ReturnUrlParameter;
options.Events.OnRedirectToLogin = ctx =>
{
if (ctx.Request.Path.StartsWithSegments("/api") && ctx.Response.StatusCode == StatusCodes.Status200OK)
{
ctx.Response.StatusCode = StatusCodes.Status401Unauthorized;
}
else
{
ctx.Response.Redirect(ctx.RedirectUri);
}
return Task.CompletedTask;
};
});
services.Configure<CookieTempDataProviderOptions>(options =>
{
options.Cookie.IsEssential = true;
options.Cookie.HttpOnly = true;
options.Cookie.SameSite = SameSiteMode.Strict;
});
services.AddCors(options =>
{
options.AddPolicy("CorsPolicy",
builder => builder.AllowAnyOrigin()
.AllowAnyMethod()
.AllowAnyHeader());
});
services.AddMemoryCache();
services.AddControllersWithViews();
services.AddRazorPages();
services.AddHsts(options =>
{
options.Preload = true;
options.MaxAge = TimeSpan.FromDays(365);
options.IncludeSubDomains = true;
});
services.AddHttpsRedirection(options =>
{
options.HttpsPort = 443;
options.RedirectStatusCode = StatusCodes.Status307TemporaryRedirect;
});
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.