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

如何从IAuthorizationHandler读取AuthorizationFailureReason并返回自定义ExceptionDto失败响应

How to Return Custom ExceptionDto with AuthorizationFailureReason Message on Policy Validation Failure

我明白你的需求——在自定义授权处理器验证失败时,要把自定义的失败消息封装成ExceptionDto返回给调用方,同时返回403状态码。之前你尝试的几个方案各有问题,我来给你推荐最适配的解决方案,以及解决你遇到的疑问。

核心解决方案:自定义 IAuthorizationMiddlewareResultHandler

官方推荐的处理授权结果的方式就是实现IAuthorizationMiddlewareResultHandler,它可以精准拦截授权结果,并且你可以轻松过滤只处理你关心的PermissionGranterPolicy,同时正确读取AuthorizationFailureReason里的自定义消息。

步骤1:创建自定义授权结果处理器

创建一个类实现IAuthorizationMiddlewareResultHandler,在里面判断授权结果是否失败,并且是否是你的目标Policy,然后构造自定义的ExceptionDto返回:

public class CustomAuthorizationResultHandler : IAuthorizationMiddlewareResultHandler
{
    private readonly AuthorizationMiddlewareResultHandler _defaultHandler = new();

    public async Task HandleAsync(
        RequestDelegate next,
        HttpContext context,
        AuthorizationPolicy policy,
        PolicyAuthorizationResult authorizeResult)
    {
        // 只处理我们自定义的PermissionGranterPolicy的失败情况
        if (policy.Name == "PermissionGranterPolicy" && !authorizeResult.Succeeded)
        {
            // 获取自定义的失败消息(过滤出MyPermissionHandler产生的原因)
            var failureMessage = authorizeResult.Failure?.FailureReasons
                .FirstOrDefault(r => r.HandlerType == typeof(MyPermissionHandler))
                ?.Message ?? "Permission denied";

            // 构造自定义ExceptionDto
            var exceptionDto = new ExceptionDto
            {
                StatusCode = StatusCodes.Status403Forbidden,
                Message = failureMessage,
                Timestamp = DateTime.UtcNow
            };

            // 设置响应
            context.Response.StatusCode = StatusCodes.Status403Forbidden;
            context.Response.ContentType = "application/json";
            await context.Response.WriteAsJsonAsync(exceptionDto);
            return;
        }

        // 其他情况交给默认处理器处理
        await _defaultHandler.HandleAsync(next, context, policy, authorizeResult);
    }
}

// 你的自定义ExceptionDto类示例
public class ExceptionDto
{
    public int StatusCode { get; set; }
    public string Message { get; set; }
    public DateTime Timestamp { get; set; }
}

步骤2:注册自定义处理器到DI容器

在Startup.cs(或者.NET 6+的Program.cs)中替换默认的授权结果处理器:

// Startup.cs ConfigureServices
services.AddSingleton<IAuthorizationMiddlewareResultHandler, CustomAuthorizationResultHandler>();

// 如果是.NET 6+ Program.cs
builder.Services.AddSingleton<IAuthorizationMiddlewareResultHandler, CustomAuthorizationResultHandler>();

步骤3:确保MyPermissionHandler正确设置失败原因

你的MyPermissionHandler里的代码已经正确,不过可以再确认下context.Fail的调用:

catch (PermissionDeniedException ex)
{
    context.Fail(new AuthorizationFailureReason(this, ex.Message));
}

这样,当授权失败时,自定义处理器就能捕获到这个失败原因,提取出你设置的异常消息。


解决你之前遇到的问题

1. 为什么之前用IAuthorizationMiddlewareResultHandler找不到自定义消息?

因为你没有过滤FailureReasons里的处理器类型,授权失败可能有多个原因,你需要指定只取MyPermissionHandler产生的失败消息,就像上面代码里的r.HandlerType == typeof(MyPermissionHandler)。

2. 为什么直接在MyPermissionHandler里写响应会出现异常?

因为授权中间件之后,ASP.NET Core的管道里还有其他中间件(比如开发环境的DeveloperExceptionPageMiddleware)会尝试修改响应状态码或内容,当你已经调用response.WriteAsync后,响应已经开始,后续中间件再修改就会抛出异常。生产环境关闭DeveloperExceptionPage后,其中一个异常会消失,但另一个可能是因为其他中间件(比如CORS)还会处理响应,所以永远不要在授权处理器里直接写响应,交给结果处理器或中间件处理才是正确的方式。

3. 开发环境的异常是否仅在开发环境出现?

是的,DeveloperExceptionPageMiddleware是仅在开发环境启用的,生产环境你不会启用它,所以那个“无法设置StatusCode”的异常在生产环境不会出现。但即使如此,直接在处理器里写响应也不是规范做法,还是推荐用结果处理器。


你的疑问解答

  1. 何处处理AuthorizationFailureReason?
    最佳位置是自定义的IAuthorizationMiddlewareResultHandler,它专门负责处理授权结果,能直接获取到PolicyAuthorizationResult,里面包含所有失败原因。

  2. 获取消息并返回自定义响应的最佳位置?
    就是上面的自定义IAuthorizationMiddlewareResultHandler,它既能精准过滤你的目标Policy,又能避免直接写响应带来的管道冲突问题。

  3. 第4.1节的异常是否仅在开发环境出现?
    是的,开发环境的UseDeveloperExceptionPage会尝试在授权失败后修改响应,而如果你已经提前写了响应,就会冲突抛出异常。生产环境关闭这个中间件后,该异常会消失,但仍然不推荐直接写响应的方式。


这个方案完全符合你的需求:只处理你定义的PermissionGranterPolicy失败场景,读取自定义的失败消息,返回带403状态码的ExceptionDto,同时遵循ASP.NET Core的管道规范,不会出现异常问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.28 17:47:48