【问题标题】:ASP.NET Core AuthorizationHandler not being calledASP.NET Core AuthorizationHandler 未被调用
【发布时间】:2020-04-17 22:34:21
【问题描述】:

我正在尝试添加一些基于authorisation 的自定义角色,但我无法添加 将 Startup 配置为调用我的 AuthorizationHandler

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

我正在使用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:在我的 Startup 中,我获取 url/roles 并将结果存储在全局变量中:

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:我的处理程序:没有被调用

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

【问题讨论】:

  • 您是否在某处使用您的自定义策略?除非您明确要求该策略的授权,或者除非您将该策略设为默认策略,否则不需要满足该要求,这意味着不会调用您的处理程序。
  • @poke 你能举个例子吗?
  • 猜测这里的问题可能是您应该注册您的 PermissionHandler 处理程序,使其依赖于 IAuthorizationHandler 而不是 AuthorizationHandler&lt;UrlRolesRequirement&gt;。试试改成services.AddSingleton&lt;IAuthorizationHandler, PermissionHandler&gt;();
  • @Dennis1679 如果我这样做,我不会丢失被注入的对象吗?我更新了我的问题以显示如何检索和传递 UrlRoles。问题是由于某种原因回调没有发生,我认为我的配置不正确。 poke 提到了一些选项,但还不清楚如何解决这个问题。
  • 我只是想知道是否需要 MVC 或者可以使用端点

标签: c# asp.net-core


【解决方案1】:

如果不告诉 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 中的角色声明中。谢谢!
【解决方案2】:

这是一个经过测试的解决方案,它启用了runtime configuration changes。 也减轻了装饰每个类或动作的负担。

Startup中添加Role Authorization Requirement,同时注册 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 实现 通过在缓存的映射中查找或从数据库中检索数据,针对由允许的 url/角色条目组成的配置映射验证用户声明的角色(JWT 声明)。

一个典型的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;
}

【讨论】:

    猜你喜欢
    • 2021-10-08
    • 1970-01-01
    • 1970-01-01
    • 2018-02-15
    • 1970-01-01
    • 2017-06-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多