在ASP.NET Core 3.0中,“ context.Resource as AuthorizationFilterContext”返回null

时间:2019-12-05 14:46:53

标签: c# asp.net

我正在尝试按照教程实施自定义授权要求。这好像是 context.Resource不再包含AuthorizationFilterContext,结果:

var authFilterContext = context.Resource as AuthorizationFilterContext;

返回null,其余逻辑失败。我也无法获取查询字符串值,因为它为null。 以下是代码:

public class CanEditOnlyOtherAdminRolesAndClaimsHandler :
   AuthorizationHandler<ManageAdminRolesAndClaimsRequirement>
    {
        protected override Task HandleRequirementAsync(AuthorizationHandlerContext context,
       ManageAdminRolesAndClaimsRequirement requirement)
        {
            var authFilterContext = context.Resource as AuthorizationFilterContext;
            if (authFilterContext == null)
            {
                return Task.CompletedTask;
            }

            string loggedInAdminId =
                context.User.Claims.FirstOrDefault(c => c.Type == ClaimTypes.NameIdentifier).Value;

            string adminIdBeingEdited = authFilterContext.HttpContext.Request.Query["userId"];

            if (context.User.IsInRole("Admin") &&
                context.User.HasClaim(claim => claim.Type == "Edit Role" && claim.Value == "true") &&
                adminIdBeingEdited.ToLower() != loggedInAdminId.ToLower())
            {
                context.Succeed(requirement);
            }

            return Task.CompletedTask;
        }
    }
}

如何在ASP.NET Core 3.0中解决此问题?

6 个答案:

答案 0 :(得分:4)

这是由于.NET Core 3.0中新的端点路由所致。

引用以下票证。

这是因为在ASP.NET Core 3.0中使用终结点路由时:

Mvc将不再将AuthorizeFilter添加到ActionDescriptor,并且 ResourceInvoker将不会调用AuthorizeAsync() https://github.com/aspnet/AspNetCore/blob/90ab2cb965aeb8ada13bc4b936b3735ca8dd28df/src/Mvc/Mvc.Core/src/ApplicationModels/AuthorizationApplicationModelProvider.cs#L40

Mvc会将所有筛选器作为元数据添加到端点。 https://github.com/aspnet/AspNetCore/blob/5561338cfecac5ca4b1dda2f09a7f66153d0b5fe/src/Mvc/Mvc.Core/src/Routing/ActionEndpointFactory.cs#L348

代替AuthorizationMiddleware调用AuthorizeAsync()和 资源是端点 https://github.com/aspnet/AspNetCore/blob/5561338cfecac5ca4b1dda2f09a7f66153d0b5fe/src/Security/Authorization/Policy/src/AuthorizationMiddleware.cs#L63

新方法。

protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, CookieOrTokenAuthorizationRequirement requirement)
{
    if (context.Resource is Endpoint endpoint)
    {
        if (endpoint.Metadata.OfType<IFilterMetadata>().Any(filter => filter is MyFilter))
        {
            context.Succeed(requirement);
            return Task.CompletedTask;
        }
    }
}

https://github.com/dotnet/aspnetcore/issues/11075

也值得注意的是,使用新上下文将无法像以前一样通过AuthorizationFilterContext访问路由数据。您将需要将IHttpContextAccessor注入AuthorizationHandler中。

// Ensure your handler is registered as scoped
services.AddScoped<IAuthorizationHandler, InvestorRequirementHandler>();


public class InvestorRequirementHandler : AuthorizationHandler<InvestorRequirement>
{
    private readonly IHttpContextAccessor _httpContextAccessor;

    public InvestorRequirementHandler(IHttpContextAccessor httpContextAccessor)
    {
        _httpContextAccessor = httpContextAccessor;
    }

    protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, InvestorRequirement requirement)
    {
        var tenant = httpContextAccessor.HttpContext.GetRouteData().Values[ExceptionHandlerMiddleware.TenantCodeKey].ToString();
    }
}

答案 1 :(得分:2)

公共类CanEditOnlyOtherAdminRolesAndClaimsHandler:AuthorizationHandler { 私有只读IHttpContextAccessor httpContextAccessor; 公共CanEditOnlyOtherAdminRolesAndClaimsHandler(IHttpContextAccessor httpContextAccessor) { this.httpContextAccessor = httpContextAccessor ??抛出新的ArgumentNullException(nameof(httpContextAccessor));

    }

    protected override Task HandleRequirementAsync(AuthorizationHandlerContext context,
                                                   ManageAdminRolesAndClaimsRequirement requirement)
    {

        var loggedInAdminId = context.User.Claims.FirstOrDefault(c => c.Type == ClaimTypes.NameIdentifier).Value.ToString();

        var adminIdBeingEdited = httpContextAccessor.HttpContext.Request.Query["userId"].ToString();

        if (context.User.IsInRole("Admin") &&
            context.User.HasClaim(claim =>
            claim.Type == "Edit Role" && claim.Value == "true") && adminIdBeingEdited.ToLower() != loggedInAdminId.ToLower())
        {
            context.Succeed(requirement);
        }

        return Task.CompletedTask;
    }

}

答案 2 :(得分:0)

    private readonly IHttpContextAccessor httpContextAccessor;
    public CanEditOnlyOtherAdminRolesAndClaimsHandler(IHttpContextAccessor httpContextAccessor)
    {
        this.httpContextAccessor = httpContextAccessor ?? throw new ArgumentNullException(nameof(httpContextAccessor));

    }

答案 3 :(得分:0)

在[Authorize]属性的上下文中,Resource属性仅是AuthorizationFilterContext。

答案 4 :(得分:0)

public class CanEditOnlyOtherAdminRolesAndClaimsHandler :
   AuthorizationHandler<ManageAdminRolesAndClaimsRequirement>
    {
       private readonly IHttpContextAccessor httpContextAccessor;

       public CanEditOnlyOtherAdminRolesAndClaimsHandler(IHttpContextAccessor httpContextAccessor)
       {
            this.httpContextAccessor = httpContextAccessor ?? throw new ArgumentNullException(nameof(httpContextAccessor));
       }

       protected override Task HandleRequirementAsync(AuthorizationHandlerContext context,
       ManageAdminRolesAndClaimsRequirement requirement)
        {
            if (context.User == null || !context.User.Identity.IsAuthenticated)
            {
                context.Fail();
                return Task.CompletedTask;
            }

            string loggedInAdminId =
                context.User.Claims.FirstOrDefault(c => c.Type == ClaimTypes.NameIdentifier).Value;

            string adminIdBeingEdited = httpContextAccessor.HttpContext.Request.Query["userId"].ToString();

            if (context.User.IsInRole("Admin") &&
            context.User.HasClaim(claim => claim.Type == "Edit Role" && claim.Value == "true") &&
            adminIdBeingEdited.ToLower() != loggedInUserId.ToLower())
            {
                context.Succeed(requirement);
            }

            return Task.CompletedTask;
        }
    }
}

然后将以下服务添加到ConfigureServices类的Startup方法中:

 public void ConfigureServices(IServiceCollection services)
        {           
          services.AddAuthorization(options =>
            {
             options.AddPolicy("ManageRolesPolicy", policy => policy.Requirements.Add(new ManageAdminRolesAndClaimsRequirement()));
            }

          services.AddScoped<IAuthorizationHandler, CanEditOnlyOtherAdminRolesAndClaimsHandler>();       
      }

如果您要处理一项需求的多个自定义授权:

CanEditOnlyOtherAdminRolesAndClaimsHandler类中,您检查用户是否具有Admin角色并拥有Edit Role声明。假设您要求用户必须具有Super Admin角色,在这种情况下,您可以:

-将CanEditOnlyOtherAdminRolesAndClaimsHandler类中的条件编辑如下:

if (context.User.IsInRole("Admin") &&
                context.User.HasClaim(claim => claim.Type == "Edit Role" && claim.Value == "true") &&
                adminIdBeingEdited.ToLower() != loggedInUserId.ToLower() || 
                context.User.IsInRole("Super Admin") && adminIdBeingEdited.ToLower() != loggedInUserId.ToLower())
                {
                    context.Succeed(requirement);
                }

-或为新要求(在这种情况下为Super Admin角色)自定义另一个授权处理程序:

创建一个新类并将其命名为ManageRolesAndClaimsSuperAdminHandler,该类的实现应如下所示

public class ManageRolesAndClaimsSuperAdminHandler : AuthorizationHandler<ManageAdminRolesAndClaimsRequirement>
    {
        private readonly IHttpContextAccessor httpContextAccessor;

        public ManageUsersRolesSuperAdminHandler(IHttpContextAccessor httpContextAccessor)
        {
            this.httpContextAccessor = httpContextAccessor ?? throw new ArgumentNullException(nameof(httpContextAccessor));
        }

        protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, ManageAdminRolesAndClaimsRequirement  requirement)
        {
        if (context.User == null || !context.User.Identity.IsAuthenticated)
            {
                context.Fail();
                return Task.CompletedTask;
            }

            string loggedInAdminId = context.User.Claims.FirstOrDefault(c => c.Type == ClaimTypes.NameIdentifier).Value;

            string adminIdBeingEdited = httpContextAccessor.HttpContext.Request.Query["userId"].ToString();

            if (context.User.IsInRole("Super Admin") && adminIdBeingEdited.ToLower() != loggedInUserId.ToLower())
            {
                context.Succeed(requirement);
            }
            return Task.CompletedTask;
        }
    }

现在在ConfigureServices类的Startup方法中注册新的处理程序

public void ConfigureServices(IServiceCollection services)
      {        
          services.AddAuthorization(options =>
            {
             options.AddPolicy("ManageRolesPolicy", policy => policy.Requirements.Add(new ManageAdminRolesAndClaimsRequirement()));
            }

          services.AddScoped<IAuthorizationHandler, CanEditOnlyOtherAdminRolesAndClaimsHandler>();
          services.AddScoped<IAuthorizationHandler, ManageRolesAndClaimsSuperAdminHandler>();
      }

答案 5 :(得分:0)

如果在控制器中使用Rout属性,则startup.cs中的更改 您可以替换

  app.UseEndpoints(endpoints =>
        {
            endpoints.MapControllerRoute(
               name: "home",
               pattern: "{controller=Home}");
        });

在Configure()中与此

        app.UseMvc(routes =>
        {
            routes.MapRoute(
                name: "home",
                template: "{controller=Home}");
        });

并在ConfigureSevices()中禁用端点路由

   services.AddMvc().AddMvcOptions(mvcopt=> { mvcopt.EnableEndpointRouting = false;});

在Asp .Net Core 5中也可以使用