ASP.Net Core 6 中具有自定义授权的 Azure AD 身份验证

Var*_*n R 7 c# asp.net-authorization azure-active-directory asp.net-core-webapi

我已使用标准方式在 ASP.Net Core 6 中实现了 Azure AD 身份验证,并在控制器类顶部使用了 [Authorize] 属性。所有这些都工作正常。

builder.Services.AddMicrosoftIdentityWebApiAuthentication(builder.Configuration, "AzureAd");
Run Code Online (Sandbox Code Playgroud)

除了身份验证之外,我还尝试使用 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
                };
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

用法: 对于每个控制器操作方法,我想要可以访问 Api 的角色。在下面的代码片段中,Roles 是一个枚举。基本上,我正在尝试为每个 Api 实现基于角色的访问。

[CustomAuthorize(Roles.Admin1,Roles.Admin2)]
Run Code Online (Sandbox Code Playgroud)

问题: 正在调用 CustomAuthorizeAttribute。但是,控制器操作的调用与 CustomAuthorizeAttribute 中的身份验证无关。

我在这里缺少什么?

Pet*_*ons 4

问题

问题在于方法本身,您正在使用对异步方法的调用

var userDetails = await _userService.GetUserProfile(userEmailId);
Run Code Online (Sandbox Code Playgroud)

这迫使您将方法签名更改为:

public async void OnAuthorization(AuthorizationFilterContext context)
Run Code Online (Sandbox Code Playgroud)

由于无法等待对此方法的调用(您无法将其更改为),因此框架永远不会在及时public async Task OnAuthorization(AuthorizationFilterContext context)之后执行代码。未及时进行await评估。if (userDetails == null)

通过提供脏修复来演示问题

如果你改变线路

var userDetails = await _userService.GetUserProfile(userEmailId);
Run Code Online (Sandbox Code Playgroud)

var userDetails = _userService.GetUserProfile(userEmailId).Result;
Run Code Online (Sandbox Code Playgroud)

您会注意到身份验证已正确执行。但是,使用.Result不是最佳实践,也不建议使用。因此,要么更改GetUserProfile为同步函数,要么使用支持基于任务的方法的授权机制,允许您正确等待方法。

解决方案

有一个IAsyncAuthorizationFilter接口支持基于任务的方法,因此请使用该接口。只需要最少的代码更改:

var userDetails = await _userService.GetUserProfile(userEmailId);
Run Code Online (Sandbox Code Playgroud)

请注意:.Net Core 中引入了执行授权的新方法,请参阅文档。例如,您可以使用基于策略或基于角色的授权,它们本身也支持基于任务的方法,允许您在自定义策略中使用异步方法。

参考