如何查看哪个 AuthorizeAttribute 失败 ASP.NET Core

How can I see which AuthorizeAttribute Failed ASP.NET Core

我正在尝试找出是否有一种简单的方法让 ASP.NET 核心记录哪个 [Authorize] 属性失败。我混合使用了“角色”和“策略”授权属性,但只要有一个失败,日志就会显示:

显然这是正确的行为,它不会让拥有不正确权限的人进入,但是如果您有多个属性,那么必须去找出哪个失败是一件很痛苦的事情。如果日志只显示 Authorization failed for Policy X 那么就很容易找到失败的原因。

有谁知道目前是否可以通过一些我不知道的选项来实现这一点?

编辑: 例如:如果我有 [Authorize(Policy = "Policy 1")][Authorize(Policy = "Policy 2")] 并且只有“策略 2”失败。我希望看到一些信息告诉我是“策略 2”失败了。

编辑: 对于仍然遇到此问题的任何人,此问题现在已由 Microsoft 实施并且是 .NET 5.0 的一部分,请参阅问题 https://github.com/aspnet/AspNetCore/issues/7789

你可以在里面处理和记录这个 Middlewares

public class AuthHandlerMiddleware
{
    private readonly RequestDelegate _next;
    private readonly ILogger<ErrorHandlingMiddleware> _logger;

    public AuthHandlerMiddleware(RequestDelegate next)
    {
        _next = next;
    }

    public async Task Invoke(HttpContext context, IHostingEnvironment env /* other scoped dependencies */)
    {  

        await _next(context);

        if (context.Response.StatusCode == 401)
           _logger.LogInformation($"'{context.User.Identity.Name}' is unauthorized");
    }
}

在您的启动配置中,

public void Configure(IApplicationBuilder app, ... )
{
      ....
      app.UseMiddleware<AuthHandlerMiddleware>();
}

对于 RolesPolicy,它们被转换为 RolesAuthorizationRequirement 等要求或 MinimumAgeRequirement 等您的自定义要求。

对于 Authorization failed.,这是由 DefaultAuthorizationServiceAuthorizeAsync 中记录的,您可能无法获得像 Policy 1Policy 2 这样的确切名称。您可以获得 Policy 的要求。

尝试检查以下解决方法是否符合您的要求。

  1. 实施自定义 DefaultAuthorizationService

    public class CustomAuthorizationService : DefaultAuthorizationService, IAuthorizationService
    {
        private readonly AuthorizationOptions _options;
        private readonly IAuthorizationHandlerContextFactory _contextFactory;
        private readonly IAuthorizationHandlerProvider _handlers;
        private readonly IAuthorizationEvaluator _evaluator;
        private readonly IAuthorizationPolicyProvider _policyProvider;
        private readonly ILogger _logger;
    
        public CustomAuthorizationService(IAuthorizationPolicyProvider policyProvider
            , IAuthorizationHandlerProvider handlers
            , ILogger<DefaultAuthorizationService> logger
            , IAuthorizationHandlerContextFactory contextFactory
            , IAuthorizationEvaluator evaluator
            , IOptions<AuthorizationOptions> options) 
            : base(policyProvider, handlers, logger, contextFactory, evaluator, options)
        {
            _options = options.Value;
            _handlers = handlers;
            _policyProvider = policyProvider;
            _logger = logger;
            _evaluator = evaluator;
            _contextFactory = contextFactory;
        }
    
        public new async Task<AuthorizationResult> AuthorizeAsync(ClaimsPrincipal user, object resource, IEnumerable<IAuthorizationRequirement> requirements)
        {
            if (requirements == null)
            {
                throw new ArgumentNullException(nameof(requirements));
            }
    
            var authContext = _contextFactory.CreateContext(requirements, user, resource);
            var handlers = await _handlers.GetHandlersAsync(authContext);
            foreach (var handler in handlers)
            {
                await handler.HandleAsync(authContext);
                if (!_options.InvokeHandlersAfterFailure && authContext.HasFailed)
                {
                    break;
                }
            }
    
            var result = _evaluator.Evaluate(authContext);
            if (result.Succeeded)
            {
                _logger.LogInformation($"Authorization is succeeded for { JsonConvert.SerializeObject(requirements) }" );
                //_logger.UserAuthorizationSucceeded();
            }
            else
            {
                //var r = result.Failure.FailedRequirements.Select(requirement => new { Requirement = requirement.GetType() });
                var json = JsonConvert.SerializeObject(result.Failure.FailedRequirements);
                _logger.LogInformation($"Authorization is failed for { json }");
                //_logger.UserAuthorizationFailed();
            }
            return result;
        }
    
    }
    
  2. 替换内置DefaultAuthorizationService

    services.AddAuthorization(config =>
    {
        config.AddPolicy("T1", policy => policy.AddRequirements(new MinimumAgeRequirement(21)));
    });
    
    services.Replace(ServiceDescriptor.Transient<IAuthorizationService, CustomAuthorizationService>());
    

Microsoft 在 .NET 5.0 中默认实现此功能,请参阅相关 GitHub 问题了解详细信息和 PR 链接。

https://github.com/aspnet/AspNetCore/issues/7789