gpt4 book ai didi

c# - 如何在.NET Core中正确设置WEB API的策略授权

转载 作者:行者123 更新时间:2023-11-30 20:25:19 32 4
gpt4 key购买 nike

我有这个Web API项目,没有UI。我的appsettings.json文件有一个部分,列出了令牌及其所属的客户端。因此,客户端仅需要在标头中提供匹配的令牌。如果没有提供令牌或令牌无效,则它应该返回401。

在ConfigureServices中,我设置授权

.AddTransient<IAuthorizationRequirement, ClientTokenRequirement>()
.AddAuthorization(opts => opts.AddPolicy(SecurityTokenPolicy, policy =>
{
var sp = services.BuildServiceProvider();
policy.Requirements.Add(sp.GetService<IAuthorizationRequirement>());
}))


从我所看到的部分可以正确触发。
这是ClientTokenRequirement的代码

protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, ClientTokenRequirement requirement)
{
if (context.Resource is AuthorizationFilterContext authFilterContext)
{
if (string.IsNullOrWhiteSpace(_tokenName))
throw new UnauthorizedAccessException("Token not provided");

var httpContext = authFilterContext.HttpContext;

if (!httpContext.Request.Headers.TryGetValue(_tokenName, out var tokenValues))
return Task.CompletedTask;

var tokenValueFromHeader = tokenValues.FirstOrDefault();

var matchedToken = _tokens.FirstOrDefault(t => t.Token == tokenValueFromHeader);

if (matchedToken != null)
{
httpContext.Succeed(requirement);
}
}

return Task.CompletedTask;
}


当我们在 ClientTokenRequirement中并且没有匹配令牌时,它将返回

return Task.CompletedTask;


这是如何在文档中记录的
https://docs.microsoft.com/en-us/aspnet/core/security/authorization/policies?view=aspnetcore-2.1

当有有效令牌时,此方法可以正常工作,但是当没有令牌并且返回 Task.Completed时,则没有401,而是一个异常


  InvalidOperationException:未指定authenticationScheme,也没有找到DefaultChallengeScheme。


我已经阅读了其他有关使用身份验证而非授权的stackoverflow文章,但实际上,此策略“授权”更适合于此目的。因此,我正在寻找有关如何防止这种异常的想法。

最佳答案

有趣的是,我认为这只是身份验证,没有任何授权(至少不在您的问题中)。您当然想对客户端进行身份验证,但是您似乎没有任何授权要求。身份验证是确定谁在发出此请求的过程,而授权则是一旦我们知道谁是请求者(更多here),便确定所述请求者可以做什么。您已经表明要返回401(错误的凭据),而不是403(未授权),我相信这会突出显示差异(更多here)。

为了在ASP.NET Core中使用自己的身份验证逻辑,可以编写自己的AuthenticationHandler,它负责接收请求并确定User。这是您情况的一个示例:

public class ClientTokenHandler : AuthenticationHandler<ClientTokenOptions>
{
private readonly string[] _clientTokens;

public ClientTokenHandler(IOptionsMonitor<ClientTokenOptions> optionsMonitor,
ILoggerFactory loggerFactory, UrlEncoder urlEncoder, ISystemClock systemClock,
IConfiguration config)
: base(optionsMonitor, loggerFactory, urlEncoder, systemClock)
{
_clientTokens = config.GetSection("ClientTokens").Get<string[]>();
}

protected override Task<AuthenticateResult> HandleAuthenticateAsync()
{
var tokenHeaderValue = (string)Request.Headers["X-TOKEN"];

if (string.IsNullOrWhiteSpace(tokenHeaderValue))
return Task.FromResult(AuthenticateResult.NoResult());

if (!_clientTokens.Contains(tokenHeaderValue))
return Task.FromResult(AuthenticateResult.Fail("Unknown Client"));

var claimsPrincipal = new ClaimsPrincipal(new ClaimsIdentity(
Enumerable.Empty<Claim>(),
Scheme.Name));
var authenticationTicket = new AuthenticationTicket(claimsPrincipal, Scheme.Name);

return Task.FromResult(AuthenticateResult.Success(authenticationTicket));
}
}


这是 HandleAuthenticateAsync中发生的情况的描述:


从请求中检索标头 X-TOKEN。如果这是无效的,则表明我们无法对请求进行身份验证(稍后会对此进行更多介绍)。
X-TOKEN标头中检索的值与已知的客户端令牌列表进行比较。如果不成功,则表明身份验证失败(我们不知道这是谁-稍后还将对此进行详细介绍)。
当客户端令牌与 X-TOKEN请求标头匹配时,我们将创建一个新的 AuthenticationTicket / ClaimsPrincipal / ClaimsIdentity组合。这是我们对 User的表示-如果要将其他信息与客户端相关联,则可以包含自己的 Claim而不是使用 Enumerable.Empty<Claim>()


您应该可以在大多数情况下按原样使用它,并进行一些更改(我已简化为既要简短回答又要填补问题中的一些空白):


构造函数将 IConfiguration的实例作为最终参数,然后将其用于从我的示例中的 string[]读取 appsettings.json。您可能会以不同的方式执行此操作,因此您可以根据需要使用DI注入当前正在使用的DI。
我已经将 X-TOKEN硬编码为提取令牌时要使用的标头名称。您可能会为此使用一个不同的名称,并且从您的问题中可以看出您没有对它进行硬编码,这更好。


关于此实现的另一点注意事项是同时使用 AuthenticateResult.NoResult()AuthenticateResult.Fail(...)。前者指示我们没有足够的信息来执行身份验证,而后者指示我们拥有了所需的一切,但身份验证失败。对于像您这样的简单设置,我认为如果愿意,在两种情况下都可以使用 Fail

您需要的第二件事是 ClientTokenOptions类,该类在上面的 AuthenticationHandler<ClientTokenOptions>中使用。对于此示例,这是单线的:

public class ClientTokenOptions : AuthenticationSchemeOptions { }


这用于配置您的 AuthenticationHandler-随时将一些配置移到此处(例如,上面的_clientTokens)。这也取决于您希望它具有怎样的可配置性和可重用性-作为另一个示例,您可以在此处定义标头名称,但这取决于您。

最后,要使用 ClientTokenHandler,您需要将以下内容添加到 ConfigureServices

services.AddAuthentication("ClientToken")
.AddScheme<ClientTokenOptions, ClientTokenHandler>("ClientToken", _ => { });


在这里,我们只是根据自己的自定义 ClientTokenHandler方案将 AuthenticationHandler注册为 ClientToken。我不会在这里像这样对 "ClientToken"进行硬编码,但是,再次,这只是一种简化。最后一个时髦的 _ => { }是一个回调,给了 ClientTokenOptions一个实例以进行修改:我们在这里不需要它,因此实际上它只是一个空的lambda。


  InvalidOperationException:未指定authenticationScheme,也没有找到DefaultChallengeScheme。


错误消息中的“ DefaultChallengeScheme”现已通过上面对 services.AddAuthentication("ClientToken")的调用进行了设置(“ ClientToken”是方案名称)。



如果要使用这种方法,则需要删除 ClientTokenRequirement内容。您可能还会发现浏览Barry Dorrans的 BasicAuthentication项目很有趣-它遵循与官方ASP.NET Core AuthenticationHandler相同的模式,但入门起来更简单。如果您不关心可配置性和可重用性方面,那么我提供的实现应该适合您的目的。

关于c# - 如何在.NET Core中正确设置WEB API的策略授权,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52008000/

32 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com