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

ASP.NET Core 1.1迁移至2.0后自定义Cookie认证异常求助

解决ASP.NET Core 2.0授权失败返回500而非401的问题

从你的日志和代码来看,问题出在ASP.NET Core 2.0对认证系统的重构上——当授权失败触发ForbidResult时,框架需要明确知道使用哪个认证方案来处理"禁止"操作,而你的自定义认证中间件没有配置对应的认证服务选项,导致框架找不到默认的ForbidScheme,从而抛出InvalidOperationException,最终返回500错误。

问题根源

在ASP.NET Core 1.1中,授权系统对认证方案的要求没那么严格;但2.0版本重构了认证体系,当AuthorizeFilter触发授权失败时,会调用ForbidAsync,此时必须指定认证方案,或者配置全局的DefaultForbidScheme,否则就会抛出你看到的异常。你的自定义中间件只是手动设置了context.User,但没有在认证服务中注册对应的scheme,框架无法识别你的"local"认证类型。

解决方案

需要两步修改,让你的自定义认证和ASP.NET Core 2.0的认证系统对齐:

1. 在ConfigureServices中配置认证服务

添加认证服务注册,指定默认的认证和禁止方案为你中间件中使用的"local",并配置对应的Cookie认证处理逻辑:

public void ConfigureServices(IServiceCollection services)
{
    // 其他现有代码...

    // 添加认证配置
    services.AddAuthentication(options =>
    {
        options.DefaultAuthenticateScheme = "local";
        options.DefaultForbidScheme = "local";
    })
    .AddCookie("local", options =>
    {
        // 配置禁止请求的处理逻辑,和你中间件里的逻辑保持一致
        options.Events = new CookieAuthenticationEvents
        {
            OnForbidden = context =>
            {
                if (Utility.IsAjaxRequest(context.Request))
                {
                    context.Response.StatusCode = 401;
                    context.Response.Headers.Add("X-Team-Login", "1");
                }
                else
                {
                    context.Response.Redirect("/Login");
                }
                return Task.CompletedTask;
            }
        };
        // 如果你不需要框架自动处理登录路径,可以保留中间件的跳转逻辑,这里可以不用设置LoginPath
    });

    // 其他现有代码...
}

2. 修改自定义认证中间件的用户认证逻辑

把你手动设置context.User的代码,替换为框架的SignInAsync方法,这样框架会正确记录认证scheme信息:

// 原代码:
// ClaimsPrincipal principal = new ClaimsPrincipal(new ClaimsIdentity(userClaims, "local"));
// context.User = principal;

// 修改为:
ClaimsPrincipal principal = new ClaimsPrincipal(new ClaimsIdentity(userClaims, "local"));
await context.SignInAsync("local", principal);

3. 调整中间件顺序

确保在Configure方法中,app.UseAuthentication()(框架的认证中间件)在app.UseMvc()之前,并且你的自定义app.UseTeamAuthentication()可以放在它之前(如果你的中间件负责token验证,建议放在UseAuthentication之前):

public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
    // 其他现有代码...

    app.UseStaticFiles();
    app.UseTeamDatabaseSelector();
    app.UseTeamAuthentication();
    // 添加框架的认证中间件
    app.UseAuthentication();
    
    var localizationOptions = app.ApplicationServices.GetService<IOptions<RequestLocalizationOptions>>();
    app.UseRequestLocalization(localizationOptions.Value);
    app.UseSession();
    app.UseMvc(routes => { /* 路由配置 */ });
}

为什么这样修改有效

  • 通过AddAuthentication注册了"local"认证scheme,并设置为默认的认证和禁止方案,框架在处理ForbidResult时就知道该用哪个scheme来处理。
  • 使用SignInAsync而不是直接赋值context.User,会让框架的认证系统正确记录用户的认证状态和scheme信息,避免授权时出现scheme缺失的异常。
  • 配置OnForbidden事件,让授权失败时的行为和你原来的中间件逻辑保持一致,确保返回401(AJAX请求)或者重定向到登录页(普通请求),而不是抛出500错误。

做完这些修改后,再测试未授权访问的场景,应该会正确返回401(AJAX请求)或者重定向到登录页(普通请求),不再出现500错误。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.15 07:12:54