You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

ASP.NET Core 3.1站点iOS Safari访问[Authorize]路由间歇性403错误如何解决?

问题根因

该偶发403错误由ASP.NET Core默认的Cookie SameSite策略与Safari浏览器的兼容性问题导致:

  • ASP.NET Core 3.0及以上版本默认将Cookie SameSite属性设为Lax,部分iOS Safari、旧版桌面Safari对SameSite规则的实现存在缺陷,会意外拦截合法的身份验证Cookie、防伪令牌Cookie
  • Cookie被拦截后,服务端无法识别用户身份,访问需要授权的路由直接返回403,若匿名页面包含防伪表单,防伪校验失败也会返回403
  • 清理网站数据后Cookie会重新下发,所以临时恢复正常,待浏览器再次触发SameSite校验逻辑后问题复现

修复步骤

1. 新增Cookie策略配置与兼容性处理

在Startup.cs中按如下规则调整代码:

public class Startup
{
    // 原有构造函数、属性保持不变

    public void ConfigureServices(IServiceCollection services)
    {
        // 新增Cookie策略配置,放在AddAuthentication之前
        services.Configure<CookiePolicyOptions>(options =>
        {
            options.MinimumSameSitePolicy = SameSiteMode.Unspecified;
            options.OnAppendCookie = cookieContext =>
                CheckSameSite(cookieContext.Context, cookieContext.CookieOptions);
            options.OnDeleteCookie = cookieContext =>
                CheckSameSite(cookieContext.Context, cookieContext.CookieOptions);
        });

        // 原有AddAuthentication、AddCookie逻辑调整
        services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
            .AddCookie(CookieAuthenticationDefaults.AuthenticationScheme, o =>
            {
                // 原有配置保持不变
                o.ExpireTimeSpan = TimeSpan.FromDays(90);
                o.AccessDeniedPath = "/Samadhi/SignIn/";
                o.LoginPath = "/Samadhi/SignIn/";
                o.LogoutPath = "/Samadhi/SignOut/";
                o.SlidingExpiration = true; // 新增:开启滑动过期,用户活跃时自动续期Cookie

                // 新增Cookie属性配置
                o.Cookie.HttpOnly = true;
                o.Cookie.SecurePolicy = CookieSecurePolicy.Always;
                o.Cookie.SameSite = SameSiteMode.Unspecified;

                // 原有OnRedirectToLogin逻辑保持不变
                var defaultCallback = o.Events.OnRedirectToLogin;
                o.Events.OnRedirectToLogin = context =>
                {
                    if (context.Request.Path.StartsWithSegments(new PathString("/samadhi"), StringComparison.OrdinalIgnoreCase))
                    {
                        context.RedirectUri = "/Samadhi/SignIn/";
                        context.Response.Redirect(context.RedirectUri);
                    }
                    else if (context.Request.Path.StartsWithSegments(new PathString("/rbk"), StringComparison.OrdinalIgnoreCase))
                    {
                        context.RedirectUri = "/Rbk/SignIn/";
                        context.Response.Redirect(context.RedirectUri);
                    }
                    return defaultCallback(context);
                };                        
                o.Validate();
            });

        // 原有防伪服务配置补充SameSite设置
        services.AddAntiforgery(options =>
        {
            options.Cookie.SameSite = SameSiteMode.Unspecified;
            options.Cookie.SecurePolicy = CookieSecurePolicy.Always;
            options.Cookie.HttpOnly = true;
        });

        // 剩余原有配置(Identity、设备检测、验证码、MVC配置等)保持不变
    }

    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
        // 原有环境判断、Https跳转逻辑保持不变
        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
        }
        else
        {
            app.UseExceptionHandler("/Home/Error");
            app.UseHsts();
        }

        app.UseHttpsRedirection();

        // 新增:Cookie策略中间件,必须放在UseAuthentication之前
        app.UseCookiePolicy();

        // 原有中间件顺序保持不变
        app.UseRouting();
        app.UseAuthentication();
        app.UseAuthorization();

        // 原有端点配置保持不变
        app.UseEndpoints(endpoints =>
        {
            endpoints.MapControllerRoute(
                name: "default",
                pattern: "{controller=Home}/{action=Index}/{id?}");
        });
    }

    // 新增:SameSite兼容性判断方法
    private static void CheckSameSite(HttpContext httpContext, CookieOptions options)
    {
        if (options.SameSite == SameSiteMode.None)
        {
            var userAgent = httpContext.Request.Headers["User-Agent"].ToString();
            // 匹配所有不支持SameSite=None的Safari及旧版浏览器
            if (userAgent.Contains("CPU iPhone OS 12") || 
                userAgent.Contains("iPad; CPU OS 12") ||
                (userAgent.Contains("Macintosh; Intel Mac OS X 10_14") && userAgent.Contains("Version/") && userAgent.Contains("Safari")) ||
                userAgent.Contains("Chrome/5") ||
                userAgent.Contains("Chrome/6"))
            {
                options.SameSite = SameSiteMode.Unspecified;
            }
        }
    }
}

2. 冗余代码清理(可选,不影响功能)

SignIn方法中存在冗余的ClaimsIdentity创建逻辑,可以调整为直接复用第一次创建的实例:

// 原有创建identity逻辑保持不变
var identity = new ClaimsIdentity(claims, CookieAuthenticationDefaults.AuthenticationScheme);
// 删掉重复创建ClaimsIdentity的逻辑,直接使用已创建的identity
ClaimsPrincipal principal = new ClaimsPrincipal(identity);

验证方式

部署后可以通过Safari开发者工具的「网络」面板查看请求的Cookie携带情况,「存储」面板查看站点Cookie的SameSite属性是否正常,确认没有被浏览器标记为拦截。


内容的提问来源于stack exchange,提问作者Ross Kelly

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.01 15:09:02