ASP.NET Core授权处理程序未被调用。

5

我正在尝试添加一些基于自定义角色的授权,但是我无法使启动配置调用我的AuthorizationHandler

我在GitHub上找到了一些相关的信息:这里。这是一个错误吗?

我正在使用ASP.NET Core 3.1,我的初始化如下:

1: 这将使用Dapper ORM从数据库检索url/角色:

private List<UrlRole> GetRolesRoutes()
{
    var urlRole = DapperORM.ReturnList<UrlRole>("user_url_role_all");
    return urlRole.Result.ToList();
}

2:在我的创业公司中,我获取网址/角色并将结果存储在全局变量中:

public Startup(IConfiguration configuration, IWebHostEnvironment env)
{
    this.environment = env;
    UrlRoles = GetRolesRoutes();
}

3: 我的配置是:注意传递的UrlRoles

 public void ConfigureServices(IServiceCollection services)
 {
     // .. snip   
     services.AddAuthorization(o =>
     o.AddPolicy(_RequireAuthenticatedUserPolicy,
            builder => builder.RequireAuthenticatedUser()));

     services.AddAuthorization(options =>
     {
         options.AddPolicy("Roles", policy =>
         policy.Requirements.Add(new UrlRolesRequirement(UrlRoles)));
     });


    services.AddSingleton<AuthorizationHandler<UrlRolesRequirement>, PermissionHandler>();
}

5: 我的Handler:没有被调用

public class PermissionHandler : AuthorizationHandler<UrlRolesRequirement>
{
    protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, UrlRolesRequirement urlRolesRequirement)
    {
        var pendingRequirements = context.PendingRequirements.ToList();
        foreach (var requirement in pendingRequirements)
        {
        }
        return Task.CompletedTask;
    }
}

6: 我的需求类:

public class UrlRolesRequirement : IAuthorizationRequirement
{
    private List<UrlRole> UrlRoles { get; }

    public UrlRolesRequirement(List<UrlRole> urlRoles)
    {
        UrlRoles = urlRoles;
    }      
}

当我调试ASP.NET CoreAuthorizationHandler时,我从未看到我的自定义需求作为一个需求出现,而我在启动中配置了该需求。我希望能够看到这个需求,并且如果存在这个需求,则我认为“回调”将会发生。但由于某种原因,我的配置未能添加该需求。

public virtual async Task HandleAsync(AuthorizationHandlerContext context)
{
    if (context.Resource is TResource)
    {
        foreach (var req in context.Requirements.OfType<TRequirement>())
        {
            await HandleRequirementAsync(context, req, (TResource)context.Resource);
        }
    }
}

2
猜测问题可能在于您应该使用对IAuthorizationHandler的依赖来注册您的PermissionHandler处理程序,而不是AuthorizationHandler<UrlRolesRequirement>。尝试将其更改为services.AddSingleton<IAuthorizationHandler, PermissionHandler>(); - Dennis VW
@Dennis1679 如果我这样做,不会失去被注入的对象吗?我已经更新了我的问题以展示如何检索和传递UrlRoles。问题是由于某种原因回调没有发生,我认为我的配置不正确。poke提到了一些选项,但尚不清楚如何解决这个问题。 - Wayne
我只是想知道是否需要使用MVC,还是可以使用Endpoints。 - Wayne
@Wayne 你不会失去 UrlRoles。看看Microsoft自己是如何为所有的处理程序依赖于IAuthorizationHandler的?Poke所说的是你需要使用你创建的策略,因为它不会自己执行操作。例如,你可以在控制器或操作级别上添加[Authorize("Roles")]作为属性。 - Dennis VW
@Dennis1679 或许我理解有误。我想要的是在数据库中设置一些预配置映射,而不是为每个控制器或操作进行装饰。然后在管道中验证用户角色或在用户进行身份验证时分配的角色。当用户尝试访问 URL 时,将验证用户角色并授予或拒绝访问权限。身份验证使用 JWT。我可以使其工作/调用:PermissionHandler : IAuthorizationHandler,但无法使用:PermissionHandler : AuthorizationHandler<UrlRolesRequirement> - Wayne
显示剩余6条评论
4个回答

5

2022年,使用dotnet 6+的asp.net core是否遇到了这个问题?

我在代码中遇到的问题是,我注册了我的处理程序,但未指定它们的接口类型为“IAuthorizationHandler”。

例如:

不要这样做:builder.Services.AddScoped<MustHaveAdminManagementScopeHandler>();

而应该这样做:

builder.Services.AddScoped<IAuthorizationHandler, MustHaveAdminManagementScopeHandler>();

此外,请勿将多个处理程序注册为Singletons。尽管我看到Microsoft文档推荐这样做,但由于某种原因,将多个IAuthorizationHandler注册为Singletons会导致异常。


1
手册说明:创建一个同时实现IAuthorizationRequirement和IAuthorizationHandler接口的类,可以通过内置的PassThroughAuthorizationHandler避免在DI中注册处理程序,从而使要求能够自行处理。...不清楚如何使用它。即使实现了两个接口,处理程序也没有被调用。 - Niksr

3
没有告诉ASP.NET Core,它不会使用您配置的策略来授权任何内容。授权策略是为了预定义复杂的授权条件,以便在需要时可以重用此行为。但它默认不适用,也无法考虑到您已经配置了两个策略:哪一个应该适用?所有的都是吗?那么为什么要配置单独的策略呢?
因此,除非您明确告诉框架,否则不会使用任何策略来授权用户。一种常见的方法是使用[Authorize]属性及其策略名称。您可以将其放在控制器操作上,也可以将其放在控制器本身上,以使所有操作都使用此策略进行授权:
[Authorize("Roles")] // ← this is the policy name
public class ExampleController : Controller
{
    // …
}

如果您有一个政策要大多数时间用来授权用户,那么您可以将该政策配置为默认政策:

services.AddAuthorization(options =>
{
    options.DefaultPolicy = new AuthorizationPolicyBuilder()
        .RequireAuthenticatedUser()
        .Build();
}

例如,这将定义一个默认要求经过身份验证的用户策略。因此,每当您使用[Authorize]属性而没有指定显式策略时,它将使用该默认策略。
这仍然需要您以某种方式标记需要授权的路由。除了使用[Authorize]属性外,您还可以在更中心的位置执行此操作:您的Startup类中的app.UseEndpoints()调用。
endpoints.MapControllerRoute(
        name: "default",
        pattern: "{controller=Home}/{action=Index}/{id?}")
    .RequireAuthorization("Roles");

这是控制器的默认路由模板,但使用了RequireAuthorization调用,这将在匹配此路由模板的所有路由上要求Roles授权策略。
您还可以使用此位置为不同的路由配置不同的默认授权策略:通过拆分路由模板,您可以使用多个对MapControllerRoute的调用,每个调用都指定自己的授权策略。

我想,与其装饰每个控制器或操作,我更希望在数据库中有一些预配置映射,然后在管道中验证用户角色或在用户进行身份验证时分配的角色。当用户尝试访问URL时,将验证用户的角色并授予或拒绝访问。

可以将确切的用户授权逻辑移入验证您的需求的授权处理程序中。但是,您仍需要为要测试的所有路由启用具有此要求的策略。
但是,我通常建议不要这样做:授权要求应该是简单的,并且通常情况下,您希望能够不必访问数据库或其他外部资源即可验证它们。您希望直接使用用户声明来快速决定是否授权用户访问某些内容。毕竟,这些检查在每个请求上运行,因此您希望使其快速完成。声明授权的一个主要好处是您不需要在每个请求上访问数据库,因此您应该通过确保用户需要授权的所有内容都可用于其声明来保持该好处。

我正从WebForms转向Razor Pages。你的最后一段话非常有道理,它说服了我放弃尝试使用UserManager来检查角色, 而是让Identity Server将它们放在jwt的角色声明中。谢谢! - AbeyMarquez

0

我立即收到了403响应,我的自定义授权处理程序代码从未被执行。原来我忘记注入它们了(作用域是可以的)。这为我解决了问题。


0

这里有一个经过测试的解决方案,可以实现运行时配置更改。同时也减轻了装饰每个类或操作的负担。

Startup中添加角色授权要求,并注册RoleService,该服务将负责确保特定角色被授权访问特定URL。

这是Startup.cs文件,我们在其中配置了要求和角色服务:

services.AddAuthorization(options =>
{
    options.DefaultPolicy = new AuthorizationPolicyBuilder()
        .RequireAuthenticatedUser()
        .AddRequirements(new UrlRolesRequirement())
        .Build();
});

services.AddSingleton<IUserService, UserService>(); // authenticate
services.AddSingleton<IUserRoleService, UserRoleService>();
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
services.AddSingleton<IAuthorizationHandler, PermissionHandler>(); // authorise

IUserRoleService的角色:- UserRoleService实现验证用户声明的角色(JWT声明)与包含允许的url/role条目的配置映射匹配,可以通过查找缓存映射或从数据库检索数据来进行验证。

典型的url(path) to role映射具有以下格式,从数据库中检索,然后缓存(如果查找失败,则从数据库中检索数据):

/path/to/resource        ROLE
public interface IUserRoleService
{
    public bool UserHasAccess(ClaimsPrincipal user, string path);
}

权限处理程序:

public class PermissionHandler : IAuthorizationHandler
{
    private readonly IUserRoleService userRoleService;
    private readonly IHttpContextAccessor contextAccessor;

    public PermissionHandler(IUserRoleService userRoleService, IHttpContextAccessor contextAccessor)
    {
        this.userRoleService = userRoleService;
        this.contextAccessor = contextAccessor;
    }

    public Task HandleAsync(AuthorizationHandlerContext context)
    {
        var pendingRequirements = context.PendingRequirements.ToList();
        foreach (var requirement in pendingRequirements)
        {
            if (!(requirement is UrlRolesRequirement)) continue;

            var httpContext = contextAccessor.HttpContext;
            var path = httpContext.Request.Path;
            if (userRoleService.UserHasAccess(context.User, path))
            {
                context.Succeed(requirement);
                break;
            }
        }
        return Task.CompletedTask;
    }
}

RolesRequirement - 只是一个 POCO

public class UrlRolesRequirement : IAuthorizationRequirement
{           
}

这是一个部分实现的UserRoleService,它验证JWT声明的角色。
private bool ValidateUser(ClaimsPrincipal user, string path)
{
    foreach (var userClaim in user.Claims)
    {
        if (!userClaim.Type.Contains("claims/role")) continue;

        var role = userClaim.Value;
        var key = role + SEPARATOR + path;

        if (urlRoles.ContainsKey(key))
        {
            var entry = urlRoles[key];
            if (entry.Url.Equals(path) && entry.Role.Equals(role))
            {
                return true;
            }
        }

    }
    Console.WriteLine("Access denied: " + path);
    return false;
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接