10

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

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
因此,正如@Martin 在下面回答的那样,看起来依赖解析器可以从 Handler 实现派生的IAuthorizationRequirement中推断出实现。AuthorizationHandler<TRequirement>

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

public class DeletePermissionRequirement : IAuthorizationRequirement
{
    // Nothing here
}

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

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

此外,您可以为单个需求添加多个 Handler 实现 -

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

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

再说一遍,依赖解析器是如何选择正确的实现的?

4

2 回答 2

3

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

可以有更多在需求类型上不同的同时授权处理程序。

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

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

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方法实现。

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

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

于 2019-07-28T07:22:03.497 回答
0

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

签名Interface被解析为一个实现——最后一个注册。签名IEnumerable<Interface>被解析到所有注册的实现。

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

于 2020-02-25T16:23:56.223 回答