在ASP.NET Core 6上实现Web API的自定义授权逻辑



我必须使用另一个/外部api为我的web api实现授权。因此,我必须从请求中获取JWT,并使用该令牌调用另一个API,以了解用户是否被授权。

目前我的身份验证正在工作,我正在使用

IServiceCollection.AddAuthentication().AddJwtBearer() // removed code to set options

在上面的示例中,我删除了提供选项和设置TokenValidationParameters的代码。因此,我的身份验证逻辑正在按预期工作。

现在我希望实现自定义授权。在我的自定义授权逻辑中,我必须调用另一个/外部API并传递一些参数。对于我的控制器中的不同操作方法,参数将有所不同。外部API将返回bool(指示是否授权(,我不需要在代码中维护任何应用程序角色/声明。

使用动态策略名称和字符串解析,如doc中所述,这是唯一/推荐的选项。

因此,我必须从请求中获取jwttoken,并使用该令牌调用另一个API,以了解用户是否被授权。

对于API收到的每个请求,您应该尽量避免必须发出出站API请求。

您似乎有一个外部身份验证服务,可以让用户登录并返回某种令牌。你需要知道第三方是如何签署他们的代币的,然后从他们那里获得某种形式的(公共(密钥。

使用此密钥,您可以验证令牌是否已由您信任的一方签名。为此,您可以配置适当的TokenValidationParameters,并将其传递给services.AddAuthentication().AddJwtBearer(),以便让Identity使用其密钥验证其令牌。

参见:

  • 在ASP.NET Core中使用特定方案进行授权
  • Microsoft.AspNetCore.Authentication.JwtBearer命名空间

最终,您还需要配置某种后台作业,当外部提供者这样做时,如果他们这样做了,他们希望这样做。


关于您更新的问题:您似乎想使用外部服务进行授权,即谁可以做什么。

你必须自己实施。通常这是使用作用域来完成的,在身份验证过程中检索一次作用域。这些可以包含诸如";金融;以提供对财务控制者的访问;金融:订单:列表金融:产品"。

[RequiredScope("finance:orders", "finance:orders:list")]
public IActionResult Index()
{
return View();
}

如果与您交谈的API无法在身份验证期间检索相关作用域、声明或权限(或每个资源检索一次(,则无法将用户角色缓存到控制器或实体。

您需要意识到,这将导致每次API调用产生额外的开销,并且当身份验证/授权服务关闭时,您的应用程序也会关闭。

如果您仍然想这样做,那么在控制器上进行异步授权的最简单方法就是策略:

public class AuthorizeWithAuthServiceRequirement : IAuthorizationRequirement
{
public const string PolicyName = "external-authorization-service";
}
public class AuthorizeWithAuthServiceHandler : AuthorizationHandler<AuthorizeWithAuthServiceRequirement>
{
private IYourApiService _yourApiService;
public AuthorizeWithAuthServiceHandler(IYourApiService yourApiService/* your DI here */)
{
_yourApiService = yourApiService;
}

protected override async Task HandleRequirementAsync(AuthorizationHandlerContext context, AuthorizeWithAuthServiceRequirement requirement)
{
var httpContext = context.Resource as HttpContext
?? throw new ArgumentException("Can't obtain HttpContext");
// Get the user or their claims or the ID from the route or something
var user = httpContext.User;
var claim = user.FindAll("foo-claim");
var allClaims = user.Claims;
var id = httpContext.Request.RouteValues["id"];
// TODO: response and error handling
var isUserAuthorized = _yourApiService.IsUserAuthorized(user, id, entity, claim, ...);
if (!isUserAuthorized)
{
context.Fail(/* TODO: reason */);
}
}
}

你用DI注册这个,就像这样:

// Register the handler for dependency injection
services.AddSingleton<IAuthorizationHandler, AuthorizeWithAuthServiceHandler>();
// Register the policy
services.AddAuthorization(options =>
{
options.AddPolicy(AuthorizeWithAuthServiceRequirement.PolicyName, x => { x.AddRequirements(new AuthorizeWithAuthServiceRequirement()); });
});

然后将其应用于控制器或操作方法:

[Authorize(Policy = AuthorizeWithAuthServiceRequirement.PolicyName)]
public class FooController : Controller
{
}

如果您想要更细粒度的控制,如每个控制器或操作带有参数的自定义属性(如[CustomAuthorization(ApiPermission.Foo)](,或者如果您想首先加载实体并将其传递给处理程序,请参阅Asp.Net Core中的Ilja:访问AuthorizeHandler中的自定义AuthorizeAttribute属性及其GitHub存储库,演示三种不同的方法。

最新更新