繁体   English   中英

ASP.NET Core 3.1 URL 链接到需要授权的页面,给出错误 Antiforgery 令牌验证失败

[英]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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM