"context.Resource as AuthorizationFilterContext"在 ASP.NET 核心 3.0 中返回空值



我正在尝试按照教程实现自定义授权要求。好像context.Resource不再包含AuthorizationFilterContext,因此:

var authFilterContext = context.Resource as AuthorizationFilterContext;

返回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中解决此问题?

这是由于 .NET Core 3.0 中的新终结点路由。

引用下面的票证。

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

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();
}
}
public class CanEditOnlyOtherAdminRolesAndClaimsHandler :
AuthorizationHandler<ManageAdminRolesAndClaimsRequirement>
{
private readonly IHttpContextAccessor httpContextAccessor;
public CanEditOnlyOtherAdminRolesAndClaimsHandler(
IHttpContextAccessor httpContextAccessor)
{
this.httpContextAccessor = 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(c => c.Type == "Edit Role" && c.Value == "true")
&& adminIdBeingEdited.ToLower() != loggedInAdminId.ToLower())
{
context.Succeed(requirement);
}
return Task.CompletedTask;
}
}
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;
}
}
}

然后将以下服务添加到类ConfigureServicesStartup方法:

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>();
}

启动中的更改.cs如果在控制器中使用 Rout 属性 您可以替换此

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

在配置(( 中使用它

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

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

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

也适用于 Asp .Net Core 5

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

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

最新更新