【问题标题】:Custom Authorization Filtering Minimal API .Net 6自定义授权过滤最小 API .Net 6
【发布时间】:2022-12-15 03:49:48
【问题描述】:

我正在探索 .Net 6 中的最小 API,并尝试将自定义授权过滤器应用于端点(通过属性或扩展)。 但在我看来,我做错了什么,或者它根本就不是设计成那样工作的(如果是这样的话,我很难过)。 除了最小 API 中的 [Authorize] 属性的 default usage 之外,在文档中找不到任何内容。

这是过滤器

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
public class CustomAuthorizeAttribute : Attribute, IAuthorizationFilter
{
    //Checking tokens
}

如果我尝试在控制器级别应用它,它工作正常

[CustomAuthorize]
public class CustomController : ControllerBase
{
    //Necessary routing
}

但是如果我切换到 Minimap APIs 符号并尝试使用属性

app.MapGet("/customEndpoint", 
        [CustomAuthorize] async ([FromServices] ICustomService customService, Guid id) => 
            await customService.GetCustomStuff(id));

甚至是扩展方法

app.MapGet("/customEndpoint", 
        async ([FromServices] ICustomService customService, Guid id) => 
            await customService.GetCustomStuff(id)).WithMetadata(new CustomAuthorizeAttribute());

它只是行不通。过滤器甚至没有被构建。

我错过了什么或做错了什么? 提前致谢

【问题讨论】:

  • 当您使用控制器和属性时,MVC 框架旨在以指定的方式使用它们。这不适用于您创建的自定义类。您可能想研究是否可以通过其他方式在最小 API 中注入授权层。
  • 你为什么不为此创建自定义中间件,然后将验证每个请求
  • 谢谢,实际上我有这样的中间件,但我很好奇我可以在最小 API 中直接从 MVC 重用什么
  • @Beeeg ASP.NET Core MVC 包括很多不需要的管道和功能(等等不能用) 通过最少的 API - 似乎 [Authorize] 就是其中之一 -不过没关系因为老实说 [Authorize] 属性本身非常糟糕(虽然声明性行为很好,但是有太多的移动部件和基于属性和声明性身份验证的令人沮丧的限制),所以你真的更好地使用中间件.

标签: c# asp.net .net-6.0 minimal-apis


【解决方案1】:

您可以在 .NET 6.0 中为 Minimal API 编写自定义授权过滤器

这是我倾向于使用它的方式-使用Policy-based authorization in ASP.NET Core

第 1 步:创建需求

需求实现IAuthorizationRequirement

public class AdminRoleRequirement : IAuthorizationRequirement
{
     public AdminRoleRequirement(string role) => Role = role;
     public string Role { get; set; }
}

注意:需求不需要具有数据或属性。

第 2 步:创建需求处理程序

需求处理程序实现AuthorizationHandler<T>

 public class AdminRoleRequirementHandler : AuthorizationHandler<AdminRoleRequirement>
 {
    public AdminRoleRequirementHandler(IHttpContextAccessor httpContextAccessor)
    {
        _httpContextAccessor = httpContextAccessor;
    }
    protected override async Task HandleRequirementAsync(AuthorizationHandlerContext context, RoleRequirement requirement)
    {
        if (context.User.HasClaim(c => c.Value == requirement.Role))
        {
            context.Succeed(requirement);
        }
        else
        {

            _httpContextAccessor.HttpContext.Response.StatusCode = StatusCodes.Status401Unauthorized;
            _httpContextAccessor.HttpContext.Response.ContentType = "application/json";
            await _httpContextAccessor.HttpContext.Response.WriteAsJsonAsync(new { StatusCode = StatusCodes.Status401Unauthorized, Message = "Unauthorized. Required admin role." });
            await _httpContextAccessor.HttpContext.Response.CompleteAsync();

            context.Fail();

        }

    }
    private readonly IHttpContextAccessor _httpContextAccessor;
}

注意:HandleRequirementAsync方法没有返回值。成功或失败的状态通过调用context.Succeed(IAuthorizationRequirement requirement)并传递已成功验证的要求或通过调用context.Fail()指示AuthorizationHandlerContext.HasSucceeded永远不会返回true,即使所有要求都得到满足。

第 3 步:在授权服务中配置您的策略

 builder.Services.AddAuthorization(o =>
 {
        o.AddPolicy("AMIN", p => p.AddRequirements(new AdminRoleRequirement("AMIN")));
 });

第 4 步:将您的需求处理程序添加到 DI

 builder.Services.AddSingleton<IAuthorizationHandler, AdminRoleRequirementHandler>();

第 5 步:将策略应用于端点

 app.MapGet("/helloworld", () => "Hello World!").RequireAuthorization("AMIN");

【讨论】:

  • 唯一的问题是 IHttpContextAccessor 的注入。授权处理程序不应该写响应......
  • 注意到@davidfowl。
【解决方案2】:

我认为您将无法在最小的 api 中注入动作过滤器,您可以使用 3 种替代方法。

  1. 创建一个自定义中间件并将其注入到启动类中,它会检查每个请求并像您的过滤器一样完成预期的工作。如果您只需要验证特定的控制器/端点,您可以在那里检查请求路径。

  2. 第二种方法是您可以像这样在最小的 api 中注入 httpcontext,从中提取 jwt 令牌并验证它,如果发现不正常,则拒绝该请求。


     app.MapGet("/customEndpoint", async (HttpContext context, ICustomService service) =>
     {
         var token = context.Request.Headers["Authorization"].FirstOrDefault()?.Split(" ").Last();
         if (string.isNullOrEmpty(token) || <not a valid token>) return Results.Unauthorized();    
         // do some work 
         return Results.Ok(result);
     });
    

    正如@Dai 建议的那样,您也可以通过这种方式提取令牌

    AuthenticationHeaderValue.TryParse(context.Request.Headers["Authorization"], out var parsed ) && parsed.Scheme == "BearerOrWhatever" ? parsed.Parameter : null
    
    1. 您可以从startup.cs 全局注册过滤器。

【讨论】:

  • 是的@Dai 同意,好多了
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-08-22
  • 1970-01-01
  • 2013-03-26
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多