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
相关产品推荐
相关产品推荐

