具有多个注册的 IAuthorizationHandler - 依赖项解析器如何选择正确的实现?



考虑Startup类的ConfigureServices方法中的以下代码 -

services.AddTransient<IAuthorizationHandler, BlockUsersHandler>();
services.AddTransient<IAuthorizationHandler, BlockClaimHandler>();
services.AddAuthorization(option =>
{                
option.AddPolicy("NotHacker", policy =>
{
policy.AddRequirements(new BlockUsersRequirement("Hacker"));
});                
option.AddPolicy("NotThatClaim", policy =>
{
policy.AddRequirements(new BlockClaimRequirement(new Claim("ThatClaimType", "ThatClaim")));
});
});

这些是自定义类实现 -

public class BlockUsersRequirement : IAuthorizationRequirement
{
// Code goes here
}
public class BlockUsersHandler : AuthorizationHandler<BlockUsersRequirement>
{
protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, BlockUsersRequirement requirement)
{
// Code goes here
}
}
public class BlockClaimRequirement : IAuthorizationRequirement
{
// Code goes here
}
public class BlockClaimHandler : AuthorizationHandler<BlockClaimRequirement>
{
protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, BlockClaimRequirement requirement)
{            
// Code goes here
}
}

我的理解是,每当面临对服务的依赖时,内置的依赖解析器都会提供为该服务注册的具体实现,如果我注册了服务的多个实现,那么最后一次注册将生效。

在上面的代码中,为IAuthorizationHandler注册了两个实现,并且两个授权策略可以正常工作。

那么,依赖关系解析器如何决定何时选择哪个实现?凭什么?

编辑 - 2019.07.28<</strong>br/>因此,正如下面@Martin回答的那样,看起来依赖解析器可以从AuthorizationHandler<TRequirement>中的IAuthorizationRequirement推断实现,处理程序实现是从中派生的。

但实际上,您可以通过直接实现IAuthorizationHandler接口来创建 Handler 类,而无需从AuthorizationHandler<TRequirement>派生 -

public class DeletePermissionRequirement : IAuthorizationRequirement
{
// Nothing here
}
public class DeletePermissionHandler : IAuthorizationHandler
{
public Task HandleAsync(AuthorizationHandlerContext context)
{
// Code goes here
}
}

因此,现在处理程序的签名中没有IAuthorizationRequirement可以推断。

此外,您可以为单个需求添加多个处理程序实现 -

public class BuildingEntryRequirement : IAuthorizationRequirement
{
// Nothing here
}
public class BadgeEntryHandler : AuthorizationHandler<BuildingEntryRequirement>
{
protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, BuildingEntryRequirement requirement)
{
// Code goes here
}
}
public class TemporaryPassHandler : AuthorizationHandler<BuildingEntryRequirement>
{
protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, BuildingEntryRequirement requirement)
{
// Code goes here
}
}

考虑到这些新实现,ConfigureServices方法中的代码如下所示 -

services.AddTransient<IAuthorizationHandler, BlockUsersHandler>();
services.AddTransient<IAuthorizationHandler, BlockClaimHandler>();
services.AddTransient<IAuthorizationHandler, DeletePermissionHandler>();
services.AddTransient<IAuthorizationHandler, BadgeEntryHandler>();
services.AddTransient<IAuthorizationHandler, TemporaryPassHandler>();
services.AddAuthorization(option =>
{                
option.AddPolicy("NotHacker", policy =>
{
policy.AddRequirements(new BlockUsersRequirement("Hacker"));
});                
option.AddPolicy("NotThatClaim", policy =>
{
policy.AddRequirements(new BlockClaimRequirement(new Claim("ThatClaimType", "ThatClaim")));
});
option.AddPolicy("CanDelete", policy =>
{
policy.AddRequirements(new DeletePermissionRequirement());
});
option.AddPolicy("BadgeEntry", policy =>
{
policy.AddRequirements(new BuildingEntryRequirement());
});
});

当然,所有授权策略都运行良好。

那么,依赖关系解析器如何选择正确的实现?

它使用需求的类型来决定使用哪个处理程序。

可以有更多同时授权处理程序,这些处理程序的要求类型不同。

可以在一个策略中检查更多要求。

此外,当调用授权服务时,它会选择正确的处理程序:

IAuthorizationService _authorizationService; // injected
_authorizationService.AuthorizeAsync(User, resourceId, new MyAuthorizationRequirement(UserAccessScope.Account, resourceId, requiredRole));

更新

默认行为为

  • 对于给定的AuthorizationHandlerContext将评估所有已注册的IAuthorizationHandler处理程序
  • 对于这些处理程序中的每一个,方法HandleAsync称为
  • 派生自抽象AuthorizationHandler<TRequirement>类的处理程序HandleAsync如下方式实现该方法:
public virtual async Task HandleAsync(AuthorizationHandlerContext context)
{
foreach (var req in context.Requirements.OfType<TRequirement>())
{
await HandleRequirementAsync(context, req);
}
}

这意味着处理程序按类型筛选要求。这也意味着不派生自抽象类的处理程序AuthorizationHandler<TRequirement>具有自己的HandleAsync方法实现。

从抽象类继承的授权处理程序与不归结为接口IAuthorizationHandlerHandleAsync方法的实现方式的授权处理程序之间的区别AuthorizationHandler<TRequirement>AuthorizationHandler<TRequirement>具有上述默认实现,泛型接口的实现者IAuthorizationHandler需要提供自己的实现。

关于单个需求的多个处理程序实现的第二部分的答案是,将评估具有给定类型要求的所有处理程序,如果其中任何一个成功并且没有一个显式失败(Fail方法已在上下文中调用),则该操作将被授权。

ASP .NET Core 依赖项注入容器可以解析为每个接口的单个实现,并且可以根据解析的签名解析为所有已注册的实现。

签名Interface解析为单个实现 - 最后一个注册的实现。 签名IEnumerable<Interface>解析为所有已注册的实现。

我假设IAuthorizationHandler是使用IEnumerable<IAuthorizationHandler>解决的,因此创建了所有实现。

相关内容

  • 没有找到相关文章

最新更新