为什么具有相同防伪验证功能的同一个ASP.NET Core应用程序在一台计算机上而不是另一台计算机上工作?

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了为什么具有相同防伪验证功能的同一个ASP.NET Core应用程序在一台计算机上而不是另一台计算机上工作?相关的知识,希望对你有一定的参考价值。

基本上,我下面有ASP.NET Core MVC应用程序:

Program.cs

public class Program
{
    public static void Main(string[] args) =>
        CreateWebHostBuilder(args)
            .Build()
            .Run();

    public static IWebHostBuilder CreateWebHostBuilder(string[] args) => WebHost.CreateDefaultBuilder(args)
        .ConfigureAppConfiguration(ConfigureAppConfiguration)
        .UseSerilog(ConfigureSerilog)
        .UseKestrel(options => options.AddServerHeader = false)
        .UseIISIntegration()
        .CaptureStartupErrors(true)
        .UseContentRoot(Directory.GetCurrentDirectory())
        .UseStartup<Startup>();

    public static void ConfigureAppConfiguration(WebHostBuilderContext context, IConfigurationBuilder builder)
    {
        builder.SetBasePath(Directory.GetCurrentDirectory()).AddJsonFile("appsettings.json", optional: true, reloadOnChange: true);
        var envFileName = $"appsettings.{context.HostingEnvironment.EnvironmentName}.json";
        builder.AddJsonFile(envFileName, optional: false, reloadOnChange: true);
        builder.AddEnvironmentVariables();
    }

    public static void ConfigureSerilog(WebHostBuilderContext context, LoggerConfiguration builder)
    {
        var elasticSection = context.Configuration.GetSection("serilog:elastic");
        var elasticOptions = new ElasticsearchSinkOptions(new Uri(elasticSection.GetValue<string>("nodeUris")));
        elasticSection.Bind(elasticOptions);

        builder
            .Enrich.WithProperty("EnvironmentName", context.HostingEnvironment.EnvironmentName)
            .Enrich.FromLogContext()
            .Filter.ByExcluding(Matching.WithProperty<string>("RequestPath", p => p.StartsWith("/health")))
            .WriteTo.Elasticsearch(elasticOptions)
            .WriteTo.Console();
    }
}

Startup.cs

public class Startup
{
    private readonly ILogger<Startup> _logger;
    private readonly IWebHostEnvironment _hostingEnvironment;
    public static bool IsDev;

    public Startup(ILogger<Startup> logger, IConfiguration configuration, IWebHostEnvironment hostingEnvironment)
    {
        _logger = logger;
        _hostingEnvironment = hostingEnvironment;
        Configuration = configuration;
        ConfigurationManager.Instance = new ConfigurationManager(Configuration);
    }

    public IConfiguration Configuration { get; }

    public void ConfigureServices(IServiceCollection services)
    {
        if (!_hostingEnvironment.IsProduction())
        {
            services.AddRequestResponseMiddleware();
        }

        _logger.LogInformation("Startup: Configure Services");

        services.AddDbContext<MyAppDataContext>(options =>
            options.UseSqlServer(Configuration.GetConnectionString("MyAppConnectionString")));
        services.AddAntiforgery();
        services
            .AddIdentity<ApplicationUser, IdentityRole>(options =>
            {
                options.SignIn.RequireConfirmedEmail = true;
                options.Password.RequireDigit = true;
                options.Password.RequiredLength = 8;
                options.Password.RequireLowercase = true;
                options.Password.RequireNonAlphanumeric = true;
                options.Password.RequireUppercase = true;
                options.User.RequireUniqueEmail = true;
                options.User.AllowedUserNameCharacters =
                    "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ@-._0123456789";
                options.Lockout.MaxFailedAccessAttempts = 5;
                options.Lockout.AllowedForNewUsers = true;
                options.Lockout.DefaultLockoutTimeSpan = TimeSpan.FromMinutes(5);
                options.Tokens.ProviderMap.Add("Default",
                    new TokenProviderDescriptor(typeof(IUserTwoFactorTokenProvider<ApplicationUser>)));
            })
            .AddEntityFrameworkStores<MyAppDataContext>()
            .AddDefaultTokenProviders();

        services.Configure<DataProtectionTokenProviderOptions>(options =>
        {
            options.Name = "Default";
            var tokenDurationActivationLinkString = ConfigurationManager.Instance["TokenDurationInHoursOfActivationAccountLink"];
            var tokenDurationActivationLink = double.Parse(tokenDurationActivationLinkString);
            options.TokenLifespan = TimeSpan.FromHours(tokenDurationActivationLink);
        });

        services.AddDataProtection().PersistKeysToFileSystem(new DirectoryInfo(Path.GetTempPath()));

        services.AddSession(options =>
        {
            // Set a short timeout for easy testing.
            options.IdleTimeout = TimeSpan.FromSeconds(10);
            options.Cookie.HttpOnly = true;
        });

        services.AddLocalization(options => options.ResourcesPath = "App_LocalResources");

        services
            .AddMvc(options => options.EnableEndpointRouting = false)
            .AddRazorPagesOptions(options =>
            {
                options.Conventions.AuthorizeFolder("/Account/Manage");
                options.Conventions.AuthorizePage("/Account/Logout");
            })
            .AddViewLocalization(LanguageViewLocationExpanderFormat.Suffix);

        var corsOrigin = Configuration.GetSection("corsOrigins:combo").Get<string[]>();

        services.AddCors(options => options
            .AddPolicy("AllowCombo", builder => builder
                .WithOrigins(corsOrigin)
                .AllowAnyHeader()));

        services.AddEFSecondLevelCache();
        services.AddSingleton(typeof(ICacheManager<>), typeof(BaseCacheManager<>));
        services.AddSingleton(typeof(ICacheManagerConfiguration),
            new CacheManager.Core.ConfigurationBuilder()
                .WithJsonSerializer()
                .WithMicrosoftMemoryCacheHandle(instanceName: "MemoryCache1")
                .WithExpiration(ExpirationMode.Absolute, TimeSpan.FromMinutes(10))
                .Build());

        services.AddHealthChecks();
        services.AddMemoryCache();
        services.AddHangfire(configuration => configuration.UseMemoryStorage());
    }

    // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
        _logger.LogInformation("Startup: Configure HTTP Request Pipeline");

        IsDev = env.IsDevelopment();

        app.UseCookiePolicy(new CookiePolicyOptions
        {
            HttpOnly = HttpOnlyPolicy.Always,
            Secure = CookieSecurePolicy.Always
        });

        if (IsDev)
        {
            app.UseDeveloperExceptionPage(new DeveloperExceptionPageOptions());
            app.UseBrowserLink();
        }
        else
        {
            app.UseExceptionHandler("/Home/Error");
        }

        app.UseHsts();
        app.UseHttpsRedirection();

        app.UseStaticFiles();
        app.UseAuthentication();
        app.UseSession();

        var supportedCultures = new[]
        {
            new CultureInfo("en"),
            new CultureInfo("fr"),
        };

        var requestLocalizationOptions = new RequestLocalizationOptions
        {
            DefaultRequestCulture = new RequestCulture("en"),
            SupportedCultures = supportedCultures,
            SupportedUICultures = supportedCultures
        };

        app.UseRequestLocalization(requestLocalizationOptions);

        requestLocalizationOptions.RequestCultureProviders.Insert(0, new UrlRequestCultureProvider());

        if (!_hostingEnvironment.IsProduction())
        {
            app.UseRequestResponseMiddleware();
        }

        app.Use(async (context, next) =>
        {
            await next();
            if (context.Response.StatusCode == 404)
            {
                context.Response.Redirect("/Account/Login");
            }
        });
        app.Use(async (context, next) =>
        {
            context.Response.Headers.Remove("x-powered-by");
            context.Response.Headers.Remove("server");
            context.Response.Headers.Add("X-Frame-Options", "SAMEORIGIN");
            context.Response.Headers.Add("X-Content-Type-Options", "nosniff");
            context.Response.Headers.Add("X-XSS-Protection", "1; mode=block");
            await next();
        });
        app.UseMvc(route =>
        {
            route.MapRoute(
                name: "Areas",
                template: "{area:exists}/{controller=Documents}/{action=Index}/{id?}"
            );
            route.MapRoute(
                name: "default",
                template: "{controller=Account}/{action=Login}/{id?}");
        });

        app.UseCors("AllowCombo");
        app.UseHealthChecks("/health");
        app.UseHangfireServer();
        app.UseHangfireDashboard();

        var logger = app.ApplicationServices.GetService<ILogger<Result>>();
        var job = new UpdateHiPayCompanyIdentificationJob(logger);
        BackgroundJob.Enqueue(() => job.UpdateCompaniesIdentificationAsync());
    }
}

AccountController.cs

[Authorize]
public class AccountController : BaseController
{
    public AccountController(UserManager<ApplicationUser> userManager, SignInManager<ApplicationUser> signInManager,
        ILogger<AccountController> logger, MyAppDataContext dataContext)
        : base(userManager, signInManager, logger, dataContext)
    {
    }

    // GET: /Account/Login
    [AllowAnonymous]
    public async Task<ActionResult> Login(string returnUrl = null)
    {
        if (User.Identity.IsAuthenticated)
        {
            var user = await UserManager.FindByNameAsync(User.Identity.Name);
            return await UserManager.IsInRoleAsync(user, "Admin") ||
                   await UserManager.IsInRoleAsync(user, "Sales")
                ? RedirectToAction("Index", "Companies", new {Area = "Sales"})
                : RedirectToAction("Index", "Documents", new {Area = "Company", id = user.CompanyId});
        }

        await HttpContext.SignOutAsync(IdentityConstants.ExternalScheme);

        return View();
    }

    // POST: /Account/Login
    [HttpPost]
    [AllowAnonymous]
    [ValidateAntiForgeryToken]
    public async Task<ActionResult> Login(LoginViewModel model, string returnUrl)
    {
        if (!ModelState.IsValid)
        {
            return View(model);
        }

        var result = await SignInManager.PasswordSignInAsync(
            model.Email,
            model.Password,
            model.RememberMe,
            true);

        if (result.Succeeded)
        {
            var user = await UserManager.FindByEmailAsync(model.Email);
            return await UserManager.IsInRoleAsync(user, "Admin") ||
                   await UserManager.IsInRoleAsync(user, "Sales")
                ? RedirectToAction("Index", "Companies", new {Area = "Sales"})
                : RedirectToAction("Index", "Documents", new {Area = "Company", id = user.CompanyId});
        }

        if (result.IsLockedOut)
        {
            return View("Lockout");
        }

        if (!result.IsNotAllowed)
        {
            ModelState.AddModelError(string.Empty, Resource.InvalidLoginAttempt);
        }

        return View(model);
    }

    // GET: /Account/ConfirmEmail
    [AllowAnonymous]
    public async Task<ActionResult> ConfirmEmail(string code, string userId)
    {
        if (code == null || userId == null)
        {
            return View("Error");
        }

        await SignInManager.SignOutAsync();
        var user = await UserManager.FindByIdAsync(userId);
        if (user == null)
        {
            Logger.LogDebug("User not found");
            return View("TokenError");
        }

        if (user.EmailConfirmed)
        {
            return RedirectToAction("Login");
        }

        var result = await UserManager.ConfirmEmailAsync(user, code);
        if (result.Succeeded)
        {
            await SignInManager.SignInAsync(await UserManager.FindByIdAsync(userId), true);

            return RedirectToAction("CreatePasswordView");
        }

        return result.Errors.Any() &&
               result.Errors.Any(identityError => identityError.Code == "InvalidToken")
            ? View("TokenError")
            : View(result.Succeeded
                ? "ConfirmEmail"
                : "Error");
    }
}

Login.cshtml

@using MyApp.Core.App_LocalResources
@using Microsoft.AspNetCore.Mvc.Rendering
@model MyApp.Core.Models.Account.LoginViewModel
@using Microsoft.AspNetCore.Mvc.Localization
@inject IViewLocalizer Localizer
@{
    ViewBag.Title = MyApp.Core.App_LocalResources.Views.Account.Login.Account_Login_Title;
}

@using (Html.BeginForm("Login", "Account", new { ViewBag.ReturnUrl }, FormMethod.Post, true, new { role = "form", @class = "form-horizontal contentPane2" }))
{

    @Html.AntiForgeryToken()
        <div class="tab" style="width: 100%">
            <div id="TopHeader" class="documentTabHeaderTop">&nbsp;</div>
            <div class="container-fluid documentTitleHeader text-center documentTabHeaderTd-active">
                <div class="col-xs-12 col-sm-12 col-lg-12 headImage">@Html.ValidationSummary(true)</div>
            </div>
        </div>
        <div class="form-group">
            <div class="col-lg-10 col-md-10 col-sm-10 col-xs-10 col-lg-offset-1 col-md-offset-1 col-xs-offset-1 col-sm-offset-0 buttons-visible">
                <div>
                    <input type="submit" id="ContinueButton" value="@Resource.Continue.ToUpper()" class="buttonItem buttonItem-valid" />
                </div>
            </div>
        </div>
        <div class="form-group">
            &nbsp;
        </div>
        <div class="form-group has-feedback">
            <div class="col-lg-10 col-md-10 col-sm-10 col-xs-10 col-lg-offset-1 col-md-offset-1 col-sm-offset-1 col-xs-offset-1">
                @Html.TextBoxFor(m => m.Email, new { @class = "form-control", @placeholder = Resource.Login, aria_describedby = "EMailStatus" })
                <span class="glyphicon form-control-feedback" aria-hidden="true" id="Email1"></span>
            </div>
            @Html.ValidationMessageFor(m => m.Email, null, new { @class = "sr-only", id = "EMailStatus" })
        </div>
        <div class="form-group has-feedback">
            <div class="col-lg-10 col-md-10 col-sm-10 col-xs-10 col-lg-offset-1 col-md-offset-1 col-sm-offset-1 col-xs-offset-1">
                @Html.PasswordFor(m => m.Password, new { @class = "form-control", @placeholder = Resource.Password, aria_describedby = "PasswordBlock" })
                <span class="glyphicon form-control-feedback" aria-hidden="true" id="Password1"></span>
            </div>
            @Html.ValidationMessageFor(m => m.Password, null, new { @class = "sr-only", id = "PasswordBlock" })
        </div>
        <div class="form-group">
            <div class="col-lg-10 col-md-10 col-sm-10 col-xs-10 col-lg-offset-1 col-md-offset-1 col-sm-offset-1 col-xs-offset-1 col-lg-offset-2 col-md-offset-2 col-sm-offset-2 col-xs-offset-2 content-font-size">
                <label class="checkbox form control">
                    @Html.CheckBoxFor(m => m.RememberMe)
                    @Resource.RememberMe
                </label>
            </div>
        </div>
        <div class="form-group">
            &nbsp;
        </div>
        <div class="form-group">
            <div class="col-lg-12 col-md-12 col-sm-12 col-xs-12 text-right content-font-size">
                @Html.ActionLink(Resource.PasswordForgotten, "ForgotPassword")
            </div>
        </div>
}

该应用程序在我的计算机上运行正常,但是当在另一台计算机上使用时,当我尝试登录时,防伪验证失败:

[10:55:50 INF] Session started; Key:0a87a293-1bd0-6c85-ad7e-457b509dc406, Id:37160380-4c09-0044-e803-2f446ced3880
[10:55:50 INF] Request finished in 652.4502ms 200 text/html; charset=utf-8
[10:55:50 INF] Request starting HTTP/1.1 GET http://localhost:5000/css/contentcss.css
[10:55:50 INF] Request starting HTTP/1.1 GET http://localhost:5000/css/themes/base/themebase.css
[10:55:50 INF] Request starting HTTP/1.1 GET http://localhost:5000/js/knockout-custom-bindings.js
[10:55:50 INF] The file /css/contentcss.css was not modified
[10:55:50 INF] Request finished in 36.7042ms 304 text/css
[10:55:50 INF] Sending file. Request path: '/css/themes/base/themebase.css'. Physical path: 'D:\MyApp\wwwroot\css\themes\base\themebase.css'
[10:55:50 INF] Sending file. Request path: '/js/knockout-custom-bindings.js'. Physical path: 'D:\MyApp\wwwroot\js\knockout-custom-bindings.js'
[10:55:50 INF] Request finished in 73.9864ms 200 application/javascript
[10:55:50 INF] Request starting HTTP/1.1 GET http://localhost:5000/js/MyApp.js?dt=20200220105550
[10:55:50 INF] Request starting HTTP/1.1 GET http://localhost:5000/js/bootstrap.js
[10:55:50 INF] The file /js/bootstrap.js was not modified
[10:55:50 INF] Request finished in 74.3462ms 200 text/css
[10:55:50 INF] Request starting HTTP/1.1 GET http://localhost:5000/js/jquery.validate.js
[10:55:50 INF] Request finished in 16.995ms 304 application/javascript
[10:55:50 INF] Sending file. Request path: '/js/MyApp.js'. Physical path: 'D:\MyApp\wwwroot\js\MyApp.js'
[10:55:50 INF] Request finished in 24.518ms 200 application/javascript
[10:55:50 INF] Request starting HTTP/1.1 GET http://localhost:5000/js/jquery-3.4.1.js
[10:55:50 INF] The file /js/jquery-3.4.1.js was not modified
[10:55:50 INF] Request finished in 11.8633ms 304 application/javascript
[10:55:50 INF] The file /js/jquery.validate.js was not modified
[10:55:50 INF] Request finished in 34.9505ms 304 application/javascript
[10:55:50 INF] Request starting HTTP/1.1 GET http://localhost:5000/js/ui.js
[10:55:50 INF] Request starting HTTP/1.1 GET http://localhost:5000/css/fonts/ttf/vprounded-light-webfont.ttf
[10:55:50 INF] Request starting HTTP/1.1 GET http://localhost:5000/css/fonts/ttf/vprounded-regular-webfont.ttf
[10:55:50 INF] The file /js/ui.js was not modified
[10:55:50 INF] Sending file. Request path: '/css/fonts/ttf/vprounded-light-webfont.ttf'. Physical path: 'D:\MyApp\wwwroot\css\fonts\ttf\vprounded-light-webfont.ttf'
[10:55:50 INF] Request finished in 17.0987ms 304 application/javascript
[10:55:50 INF] Sending file. Request path: '/css/fonts/ttf/vprounded-regular-webfont.ttf'. Physical path: 'D:\MyApp\wwwroot\css\fonts\ttf\vprounded-regular-webfont.ttf'
[10:55:50 INF] Request finished in 4.8517ms 200 application/x-font-ttf
[10:55:50 INF] Request finished in 37.1005ms 200 application/x-font-ttf
[10:55:50 INF] Request starting HTTP/1.1 GET http://localhost:5000/js/modernizr-3.8.0.js
[10:55:50 INF] The file /js/modernizr-3.8.0.js was not modified
[10:55:50 INF] Request finished in 9.9437ms 304 application/javascript
[10:55:51 INF] Request starting HTTP/1.1 GET http://localhost:5000/images/favicon.ico
[10:55:51 INF] Sending file. Request path: '/images/favicon.ico'. Physical path: 'D:\MyApp\wwwroot\images\favicon.ico'
[10:55:51 INF] Request finished in 4.4223ms 200 image/x-icon
[10:55:57 INF] Request starting HTTP/1.1 POST http://localhost:5000/ application/x-www-form-urlencoded 253
[10:55:57 INF] Route matched with {action = "Login", controller = "Account", area = ""}. Executing controller action with signature System.Threading.Tasks.Task`1[Microsoft.AspNetCore.Mvc.ActionResult] Login(MyApp.Core.Models.Account.LoginViewModel, System.String) on controller MyApp.Core.Controllers.AccountController (MyApp.Core).
[10:55:57 INF] Antiforgery token validation failed. The required antiforgery cookie ".AspNetCore.Antiforgery.fKtywT1TTCk" is not present.
Microsoft.AspNetCore.Antiforgery.AntiforgeryValidationException: The required antiforgery cookie ".AspNetCore.Antiforgery.fKtywT1TTCk" is not present.
   at Microsoft.AspNetCore.Antiforgery.DefaultAntiforgery.ValidateRequestAsync(HttpContext httpContext)
   at Microsoft.AspNetCore.Mvc.ViewFeatures.Filters.ValidateAntiforgeryTokenAuthorizationFilter.OnAuthorizationAsync(AuthorizationFilterContext context)
[10:55:57 INF] Authorization failed for the request at filter 'Microsoft.AspNetCore.Mvc.ViewFeatures.Filters.ValidateAntiforgeryTokenAuthorizationFilter'.
[10:55:57 INF] Executing HttpStatusCodeResult, setting HTTP status code 400
[10:55:57 INF] Executed action MyApp.Core.Controllers.AccountController.Login (MyApp.Core) in 136.2078ms
[10:55:57 INF] Http Request Information:

很有趣,即使在另一台计算机上运行,​​页面源代码中也有一个防伪令牌。

虽然不能真正弄清楚为什么验证失败。


[编辑1]

好吧,这似乎是由于HTTPS起作用,但是很奇怪,它们是无关的。似乎没有意义,可能是Startup中的配置有问题。


[编辑2]

然后我调试了防伪中间件:

看来:

var cookieToken = httpContext.Request.Cookies[_options.Cookie.Name];

实际上是null ...

这是在登录页面后面显示实际代码时的某种意外原因:

<form action="/" class="form-horizontal contentPane2" method="post" role="form">        <div class="tab" style="width: 100%">
            <div id="TopHeader" class="documentTabHeaderTop">&nbsp;</div>
            <div class="container-fluid documentTitleHeader text-center documentTabHeaderTd-active">
                <div class="col-xs-12 col-sm-12 col-lg-12 headImage"></div>
            </div>
        </div>
        <div class="form-group">
            <div class="col-lg-10 col-md-10 col-sm-10 col-xs-10 col-lg-offset-1 col-md-offset-1 col-xs-offset-1 col-sm-offset-0 buttons-visible">
                <div>
                    <input type="submit" id="ContinueButton" value="SEND" class="buttonItem buttonItem-valid">
                </div>
            </div>
        </div>
        <div class="form-group">
            &nbsp;
        </div>
        <div class="form-group has-feedback">
            <div class="col-lg-10 col-md-10 col-sm-10 col-xs-10 col-lg-offset-1 col-md-offset-1 col-sm-offset-1 col-xs-offset-1">
                <input aria-describedby="EMailStatus" class="form-control" data-val="true" data-val-email="The Login field is not a valid e-mail address." data-val-required="The Login field is required." id="Email" name="Email" placeholder="Login" type="text" value="">
                <span class="glyphicon form-control-feedback" aria-hidden="true" id="Email1"></span>
            </div>
            <span class="field-validation-valid sr-only" data-valmsg-for="Email" data-valmsg-replace="true" id="EMailStatus"></span>
        </div>
        <div class="form-group has-feedback">
            <div class="col-lg-10 col-md-10 col-sm-10 col-xs-10 col-lg-offset-1 col-md-offset-1 col-sm-offset-1 col-xs-offset-1">
                <input aria-describedby="PasswordBlock" class="form-control" data-val="true" data-val-required="The Password field is required." id="Password" name="Password" placeholder="Password" type="password">
                <span class="glyphicon form-control-feedback" aria-hidden="true" id="Password1"></span>
            </div>
            <span class="field-validation-valid sr-only" data-valmsg-for="Password" data-valmsg-replace="true" id="PasswordBlock"></span>
        </div>
        <div class="form-group">
            <div class="col-lg-10 col-md-10 col-sm-10 col-xs-10 col-lg-offset-1 col-md-offset-1 col-sm-offset-1 col-xs-offset-1 col-lg-offset-2 col-md-offset-2 col-sm-offset-2 col-xs-offset-2 content-font-size">
                <label class="checkbox form control">
                    <input data-val="true" data-val-required="The Remember me? field is required." id="RememberMe" name="RememberMe" type="checkbox" value="true">
                    Remember me
                </label>
            </div>
        </div>
        <div class="form-group">
            &nbsp;
        </div>
        <div class="form-group">
            <div class="col-lg-12 col-md-12 col-sm-12 col-xs-12 text-right content-font-size">
                <a href="/ForgotPassword">Forgot your password?</a>
            </div>
        </div>
<input name="__RequestVerificationToken" type="hidden" value="CfDJ8CvSe49ypctKiIz481LO88tJwZpiwj8bwXlcfyHoKa__RWLQgNEgyiO1xNbDAV0C7SjWeqPJOWciiYMBjfXb039cW50GwqNC5561LzuOYkM8umGaqGo3cV2X935LY5FrVj5OUkPzyFwQp7Y4vCcOGuE"><input name="RememberMe" type="hidden" value="false"></form>
答案

好吧我花了一些时间才弄清楚,但是长话短说...当检查整个反伪造令牌过程shenanigan的源代码时,似乎对cookie的设置方式进行了一致性检查。此处:https://github.com/dotnet/aspnetcore/blob/master/src/Antiforgery/src/Internal/DefaultAntiforgery.cs#L275

private void CheckSSLConfig(HttpContext context)
{
  if (_options.Cookie.SecurePolicy == CookieSecurePolicy.Always && !context.Request.IsHttps)
  {
      throw new InvalidOperationException(Resources.FormatAntiforgery_RequiresSSL(
          string.Join(".", nameof(AntiforgeryOptions), nameof(AntiforgeryOptions.Cookie), nameof(CookieBuilder.SecurePolicy)),
          nameof(CookieSecurePolicy.Always)));
  }
}

因为我的cookie配置是...:

app.UseCookiePolicy(new CookiePolicyOptions
{
    HttpOnly = HttpOnlyPolicy.Always,
    Secure = CookieSecurePolicy.Always
});

然后有点把不安全的http请求弄乱了。

因此,我故意使配置与请求所使用的协议方案相匹配:

app.UseCookiePolicy(new CookiePolicyOptions
{
    HttpOnly = HttpOnlyPolicy.Always,
    Secure = CookieSecurePolicy.SameAsRequest
});

现在它就像一种魅力!

以上是关于为什么具有相同防伪验证功能的同一个ASP.NET Core应用程序在一台计算机上而不是另一台计算机上工作?的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 ASP.NET 和 iframe 跨域对用户进行身份验证?

带有asp .net核心的防伪令牌jquery ajax

Asp.net Mvc:Jquery post 数组 + 防伪令牌

如何在无视图 WebAPI ASP.NET Core 应用程序中使用 [Authorize] 和防伪?

使用防伪令牌将 JSON 模型发布到 ASP.Net MVC3

在具有 3 层架构的 ASP.NET MVC 应用程序中验证业务规则的更好方法是啥?