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

ASP.NET Core 6中Azure AD认证结合自定义授权的问题排查

问题描述

已在ASP.NET Core 6中通过标准方式实现Azure AD认证,控制器类上标注[Authorize]属性,相关配置代码builder.Services.AddMicrosoftIdentityWebApiAuthentication(builder.Configuration, "AzureAd");运行正常。

尝试通过TypeFilterAttribute构建自定义授权实现基于角色的访问控制,自定义属性和过滤器代码如下:

public class CustomAuthorizeAttribute : TypeFilterAttribute
{
    public CustomAuthorizeAttribute(params Roles[] roles) : base(typeof(CustomAuthorizeFilter))
    {
        Roles[] _roles = roles;
        Arguments = new object[] { _roles };
    }
}

public class CustomAuthorizeFilter : IAuthorizationFilter
{
    private readonly Roles[] _roles;
    private readonly IUserService _userService;

    public CustomAuthorizeFilter(Roles[] roles, IUserService userService)
    {
        _roles = roles ?? throw new UnauthorizedAccessException("OnAuthorization : Missing role parameter");
        _userService = userService;
    }

    public async void OnAuthorization(AuthorizationFilterContext context)
    {
        if (context != null && context.HttpContext != null &&
            context.HttpContext.User != null &&
            context.HttpContext.User.Identity != null)
        {
            string? userEmailId = context.HttpContext.User.Identity.Name;
            if (string.IsNullOrEmpty(userEmailId))
            {
                context.Result = new ContentResult()
                {
                    Content = "OnAuthorization : Invalid User : Email Id is not present",
                    StatusCode = 401
                };

                return;
            }

            var userDetails = await _userService.GetUserProfile(userEmailId);

            if (userDetails == null)
            {
                context.Result = new ContentResult()
                {
                    Content = "OnAuthorization : Invalid User : User does not exist",
                    StatusCode = 403
                };
            }
        }
    }
}

使用方式为在控制器动作方法上标注[CustomAuthorize(Roles.Admin1,Roles.Admin2)](Roles为枚举类型),但目前遇到问题:CustomAuthorizeAttribute会被调用,但无论自定义授权中的认证结果如何,控制器动作都会被执行。

问题原因分析
  • 异步方法使用错误:OnAuthorization用了async void,ASP.NET Core无法追踪异步操作的完成状态,会直接继续执行后续请求管道,导致即使设置了context.Result,异步逻辑还没处理完就进入控制器动作。
  • 未覆盖所有授权失败分支:当context、HttpContext等为空时,没有设置context.Result;用户存在但角色不匹配时,也没有任何拦截逻辑,请求会直接往下走。
  • 构造函数抛异常不合理:构造函数在应用启动时初始化过滤器实例,此时抛出异常会导致应用启动失败,而非在请求时处理授权失败。
解决方案

1. 改用IAsyncAuthorizationFilter接口

替换IAuthorizationFilter为IAsyncAuthorizationFilter,用async Task实现异步逻辑,让ASP.NET Core能正确等待异步操作完成。

2. 完善所有授权分支的结果设置

确保所有授权失败场景都设置context.Result,同时添加角色匹配的检查逻辑。

修正后的完整代码

public class CustomAuthorizeAttribute : TypeFilterAttribute
{
    public CustomAuthorizeAttribute(params Roles[] roles) : base(typeof(CustomAuthorizeFilter))
    {
        Arguments = new object[] { roles };
    }
}

public class CustomAuthorizeFilter : IAsyncAuthorizationFilter
{
    private readonly Roles[] _roles;
    private readonly IUserService _userService;

    public CustomAuthorizeFilter(Roles[] roles, IUserService userService)
    {
        _roles = roles;
        _userService = userService ?? throw new ArgumentNullException(nameof(userService));
    }

    public async Task OnAuthorizationAsync(AuthorizationFilterContext context)
    {
        // 检查上下文及用户认证状态
        if (context == null || context.HttpContext == null || 
            context.HttpContext.User == null || !context.HttpContext.User.Identity.IsAuthenticated)
        {
            context.Result = new ContentResult()
            {
                Content = "OnAuthorization : Invalid or unauthenticated user",
                StatusCode = 401
            };
            return;
        }

        string? userEmailId = context.HttpContext.User.Identity.Name;
        if (string.IsNullOrEmpty(userEmailId))
        {
            context.Result = new ContentResult()
            {
                Content = "OnAuthorization : Invalid User : Email Id is not present",
                StatusCode = 401
            };
            return;
        }

        var userDetails = await _userService.GetUserProfile(userEmailId);
        if (userDetails == null)
        {
            context.Result = new ContentResult()
            {
                Content = "OnAuthorization : Invalid User : User does not exist",
                StatusCode = 403
            };
            return;
        }

        // 检查用户角色是否匹配(假设userDetails包含Roles属性)
        bool hasMatchingRole = _roles.Any(role => userDetails.Roles.Contains(role));
        if (!hasMatchingRole)
        {
            context.Result = new ContentResult()
            {
                Content = "OnAuthorization : Insufficient permissions",
                StatusCode = 403
            };
            return;
        }
    }
}

3. 额外注意事项

  • 确保IUserService已正确注册到依赖注入容器中;
  • 构造函数仅做依赖检查,授权校验逻辑全部放在OnAuthorizationAsync方法中;
  • 如果Azure AD返回的用户标识信息中,Identity.Name不是邮箱,需调整获取用户唯一标识的方式(比如从Claims中获取email声明)。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.23 00:24:25