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

AuthorizationMiddlewareResultHandler未被调用问题求助

自定义授权失败提示未生效问题排查与解决

尝试实现基于页面的自定义授权失败提示消息,参考Handling the AuthorizationFailureReason相关逻辑,日志显示FailHandler已触发且设置了自定义消息,但系统直接重定向到/Account/AccessDenied,自定义的AuthorizationMiddlewareResultHandler从未执行。


相关代码文件

Startup.cs

using IAuthorizationMiddlewareResultHandler = MyCompany.IAuthorizationMiddlewareResultHandler;

public void ConfigureServices(IServiceCollection services)
{
...
    services.AddAuthentication(sharedOptions =>
    {
        sharedOptions.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
        sharedOptions.DefaultChallengeScheme = WsFederationDefaults.AuthenticationScheme;
    })
    .AddWsFederation(options =>
    {
        options.ClaimsIssuer = claimsIssuerServer;
        options.Wtrealm = _wtrealm;
        options.MetadataAddress = _metaAddress;             

        options.Events.OnRedirectToIdentityProvider = Authentication.OnRedirectToIdentityProvider(claimsIssuerServer, identityProviderServer);
        options.Events.OnSecurityTokenReceived = Authentication.OnSecurityTokenReceived(_audience, validIssuerServer, signingKey);

        options.TokenValidationParameters = new TokenValidationParameters
        {
            ValidAudience = _audience,
            ValidateIssuer = true,
            ValidIssuer = validIssuerServer,
            ValidateIssuerSigningKey = true,
            IssuerSigningKey = signingKey,
        };
        options.RequireHttpsMetadata = true;

    }).AddCookie(cookieoption => {
        cookieoption.Cookie.SecurePolicy = CookieSecurePolicy.Always;
        cookieoption.EventsType = typeof(CustomCookieAuthenticationEvents);
    });

    services.AddScoped<CustomCookieAuthenticationEvents>();

    services.AddRazorPages(options =>
    {
...               
    })
    .AddMvcOptions(o => o.Filters.Add(new AuthorizeFilter())); 

    services.AddAuthorization(options =>
    {
...
        options.AddPolicy("AuthorizedUser", policy =>
        {
            policy.Requirements.Add(new FailRequirement("AuthorizedUser"));
        });
    });

...
    services.AddTransient<IAuthorizationHandler, FailHandler>();
    services.AddSingleton<IAuthorizationMiddlewareResultHandler, AuthorizationMiddlewareResultHandler>();

    services.AddScoped<IClaimsTransformation, AddRolesClaimsTransformation>();
}

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
...
    app.UseAuthentication();
    app.UseAuthorization();

...

    app.UseEndpoints(endpoints =>
    {
        endpoints.MapRazorPages();
    });
}

Create.cshtml.cs

[Authorize("AuthorizedUser")]
public class CreateModel : PageModel
{       
}

AuthorizationMiddlewareResultHandler.cs

public class AuthorizationMiddlewareResultHandler : IAuthorizationMiddlewareResultHandler
{
    private readonly ILogger<AuthorizationMiddlewareResultHandler> _logger;
    private readonly Microsoft.AspNetCore.Authorization.Policy.AuthorizationMiddlewareResultHandler _defaultHandler = new();

    public AuthorizationMiddlewareResultHandler(ILogger<AuthorizationMiddlewareResultHandler> logger)
    {
        _logger = logger;
    }

    public async Task HandleAsync(
        RequestDelegate next,
        HttpContext context,
        AuthorizationPolicy policy,
        PolicyAuthorizationResult authorizeResult)
    {
        var authorizationFailureReason = authorizeResult.AuthorizationFailure?.FailureReasons.FirstOrDefault();
        var message = authorizationFailureReason?.Message;
        _logger.LogInformation("Authorization Result says {Message}",
            message
        );

        if (authorizationFailureReason?.Handler is FailHandler)
        {
            var html = $"<html><h1>{message}</h1></html>";
            context.Response.StatusCode = StatusCodes.Status403Forbidden;
            await Results.Content(html, "text/html").ExecuteAsync(context);
        }
        else
        {
            // Fall back to the default implementation.
            await _defaultHandler.HandleAsync(next, context, policy, authorizeResult);
        }
    }
}

FailHandler.cs

public class FailHandler : AuthorizationHandler<FailRequirement>
{
    protected override Task HandleRequirementAsync(
        AuthorizationHandlerContext context,
        FailRequirement requirement)
    {
        string permission = requirement.RequiredPermission.ToString() ?? "";
        string authorizationFailureMessage = "You are not authorized for this page.";

        if (!context.User.HasClaim(c => c.Type == "Permission" && c.Value == permission))
        {
            switch (permission)
            {
                case "AuthorizedUser":
                    authorizationFailureMessage = "You are not eligible for this service at this time.";
                    break;
            }

            context.Fail(new AuthorizationFailureReason(this, authorizationFailureMessage));
        }
        else
        {
            context.Succeed(requirement); 
        }         

        return Task.CompletedTask;
    }
}   

FailRequirement.cs

public class FailRequirement : IAuthorizationRequirement
{
    public FailRequirement(string requiredPermission) 
    { 
        RequiredPermission = requiredPermission;
    }

    public string RequiredPermission { get; set; }
}   

IAuthorizationMiddlewareResultHandler.cs

public interface IAuthorizationMiddlewareResultHandler
{
    Task HandleAsync(
        RequestDelegate next,
        HttpContext context,
        AuthorizationPolicy policy,
        PolicyAuthorizationResult authorizeResult);
}

日志输出

{"@t":"2023-01-26T19:04:15.7618707Z","@mt":"Authorization failed. {Reason}","Reason":"Fail() was explicitly called.","EventId":{"Id":2,"Name":"UserAuthorizationFailed"},"SourceContext":"Microsoft.AspNetCore.Authorization.DefaultAuthorizationService","RequestId":"80000044-0005-fe00-b63f-84710c7967bb","RequestPath":"/internship/create","ApplicationName":"MyApp","Environment":"Development"}
{"@t":"2023-01-26T19:04:15.7653501Z","@mt":"AuthenticationScheme: {AuthenticationScheme} was forbidden.","AuthenticationScheme":"Cookies","EventId":{"Id":13,"Name":"AuthenticationSchemeForbidden"},"SourceContext":"Microsoft.AspNetCore.Authentication.Cookies.CookieAuthenticationHandler","RequestId":"80000044-0005-fe00-b63f-84710c7967bb","RequestPath":"/internship/create","ApplicationName":"MyApp","Environment":"Development"}
{"@t":"2023-01-26T19:04:15.7654538Z","@mt":"AuthenticationScheme: {AuthenticationScheme} was forbidden.","AuthenticationScheme":"WsFederation","EventId":{"Id":13,"Name":"AuthenticationSchemeForbidden"},"SourceContext":"Microsoft.AspNetCore.Authentication.WsFederation.WsFederationHandler","RequestId":"80000044-0005-fe00-b63f-84710c7967bb","RequestPath":"/internship/create","ApplicationName":"MyApp","Environment":"Development"}
{"@t":"2023-01-26T19:04:19.9125753Z","@mt":"{HostingRequestFinishedLog:l}","@r":["Request finished HTTP/2 GET https://localhost:44386/internship/create - - - 302 - - 9840.7917ms"],"ElapsedMilliseconds":9840.7917,"StatusCode":302,"ContentType":null,"ContentLength":null,"Protocol":"HTTP/2","Method":"GET","Scheme":"https","Host":"localhost:44386","PathBase":"","Path":"/internship/create","QueryString":"","HostingRequestFinishedLog":"Request finished HTTP/2 GET https://localhost:44386/internship/create - - - 302 - - 9840.7917ms","EventId":{"Id":2},"SourceContext":"Microsoft.AspNetCore.Hosting.Diagnostics","RequestId":"80000044-0005-fe00-b63f-84710c7967bb","RequestPath":"/internship/create","ApplicationName":"MyApp","Environment":"Development"}
{"@t":"2023-01-26T19:04:19.9188021Z","@mt":"{HostingRequestStartingLog:l}","@r":["Request starting HTTP/2 GET https://localhost:44386/Account/AccessDenied?ReturnUrl=%2Finternship%2Fcreate - -"],"Protocol":"HTTP/2","Method":"GET","ContentType":null,"ContentLength":null,"Scheme":"https","Host":"localhost:44386","PathBase":"","Path":"/Account/AccessDenied","QueryString":"?ReturnUrl=%2Finternship%2Fcreate","HostingRequestStartingLog":"Request starting HTTP/2 GET https://localhost:44386/Account/AccessDenied?ReturnUrl=%2Finternship%2Fcreate - -","EventId":{"Id":1},"SourceContext":"Microsoft.AspNetCore.Hosting.Diagnostics","RequestId":"80000046-0005-fe00-b63f-84710c7967bb","RequestPath":"/Account/AccessDenied","ApplicationName":"MyApp","Environment":"Development"}


问题原因与解决方案

核心原因

自定义的IAuthorizationMiddlewareResultHandler未被框架正确识别,因为你创建了同名的自定义接口MyCompany.IAuthorizationMiddlewareResultHandler,而ASP.NET Core框架依赖的是Microsoft.AspNetCore.Authorization.Policy.IAuthorizationMiddlewareResultHandler接口。框架无法注入你的实现,因此始终使用默认处理逻辑,导致授权失败后直接重定向到AccessDenied页面。日志中的"AuthenticationScheme was forbidden"是授权失败后认证中间件的默认行为,同样源于自定义结果处理器未生效。

修复步骤

  1. 删除自定义的IAuthorizationMiddlewareResultHandler接口
    移除MyCompany.IAuthorizationMiddlewareResultHandler文件,让自定义处理器直接实现框架提供的接口。

  2. 更新AuthorizationMiddlewareResultHandler的接口引用
    修改类文件,确保实现框架接口:

    using Microsoft.AspNetCore.Authorization.Policy;
    
    public class AuthorizationMiddlewareResultHandler : IAuthorizationMiddlewareResultHandler
    {
        // 现有代码保持不变
    }
    
  3. 调整Startup.cs中的引用与服务注册
    移除自定义接口的别名,使用框架接口引用:

    // 移除:using IAuthorizationMiddlewareResultHandler = MyCompany.IAuthorizationMiddlewareResultHandler;
    using Microsoft.AspNetCore.Authorization.Policy;
    
    // 服务注册代码保持:
    services.AddSingleton<IAuthorizationMiddlewareResultHandler, AuthorizationMiddlewareResultHandler>();
    
  4. 可选:禁用认证中间件的默认禁止重定向
    若需完全避免重定向行为,可在Cookie认证配置中添加以下逻辑(修复后自定义处理器已接管逻辑,此步骤可选):

    .AddCookie(cookieoption => {
        cookieoption.Cookie.SecurePolicy = CookieSecurePolicy.Always;
        cookieoption.EventsType = typeof(CustomCookieAuthenticationEvents);
        cookieoption.Events.OnForbidden = context => Task.CompletedTask;
    });
    

验证

修复后重启应用,授权失败时AuthorizationMiddlewareResultHandler.HandleAsync会被触发,日志将输出自定义消息,并返回包含提示的403页面,不再重定向到/Account/AccessDenied。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.03 16:55:17