考虑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
方法实现。
从抽象类继承的授权处理程序与不归结为接口IAuthorizationHandler
的HandleAsync
方法的实现方式的授权处理程序之间的区别AuthorizationHandler<TRequirement>
。AuthorizationHandler<TRequirement>
具有上述默认实现,泛型接口的实现者IAuthorizationHandler
需要提供自己的实现。
关于单个需求的多个处理程序实现的第二部分的答案是,将评估具有给定类型要求的所有处理程序,如果其中任何一个成功并且没有一个显式失败(Fail
方法已在上下文中调用),则该操作将被授权。
ASP .NET Core 依赖项注入容器可以解析为每个接口的单个实现,并且可以根据解析的签名解析为所有已注册的实现。
签名Interface
解析为单个实现 - 最后一个注册的实现。 签名IEnumerable<Interface>
解析为所有已注册的实现。
我假设IAuthorizationHandler
是使用IEnumerable<IAuthorizationHandler>
解决的,因此创建了所有实现。