英文:
Conditional Authorization
问题
我想通过JwtBearer实现身份验证,并通过配置文件键添加条件到该令牌。当我跳过身份验证部分时,出现错误:**未注册身份验证处理程序。您是否忘记调用AddAuthentication().Add[SomeAuthHandler]("Bapi", ...)?**这个错误是在我调用API时(作为JSON响应)出现的。
我有一些控制器带有这个装饰:
[Authorize(AuthenticationSchemes = Security.AuthenticationSchemes.Bapi)]
以下是身份验证的代码:
serviceCollection.AddAuthorization(...).AddAuthentication(...).AddJwtBearer(...)
这在startup.cs中注册:
services.AddBapiAuthorization(Configuration);
如果我在AddAuthorization中使用配置键值进行条件判断,就会出现错误。该如何处理?
我已经尝试过在startup中添加“空”授权或删除services.AddBapiAuthorization(Configuration);
,但仍然出现相同的错误。
感谢您的帮助
编辑:
添加条件结构
public void ConfigureServices(IServiceCollection serviceCollection)
{
if (securityActivated)
{
serviceCollection.AddAuthentication()
.AddJwtBearer(options =>
{
// ...
});
serviceCollection.AddAuthorization(options =>
{
// ...
});
}
}
英文:
I would like to implement Authentication trough JwtBearer and add condition to that bearer with config file key. I have the error when I skip the authentication part: No authentication handlers are registered. Did you forget to call AddAuthentication().AddSomeAuthHandler? that come when I call the API (as json response)
I have some controllers have that decoration:
[Authorize(AuthenticationSchemes = Security.AuthenticationSchemes.Bapi)]
and here is the code for authentication:
serviceCollection.AddAuthorization(...).AddAuthentication(...).AddJwtBearer(...)
This is registered in the startup.cs:
services.AddBapiAuthorization(Configuration);
If I do a if
with the configkey value into the AddAuthorization to apply or not the authorization, I've got the error. How deal with it?
I've already tried to put a "empty" authorization or remove the services.AddBapiAuthorization(Configuration); into startup but it gives me the same error.
Thank you for the help
EDIT:
Add conditional structure
public void ConfigureServices(IServiceCollection serviceCollection)
{
if (securityActivated)
{
serviceCollection.AddAuthentication()
.AddJwtBearer(options =>
{
// ...
});
serviceCollection.AddAuthorization(options =>
{
// ...
});
}
}
</details>
# 答案1
**得分**: 1
如果您想有条件地使用`Authorization`,您可以在`Configure`方法中添加`if`条件,而不是在`ConfigureServices`中添加。这意味着,您将添加配置,但`Authorization`/`Authentication`中间件将仅根据您的条件运行。
<details>
<summary>英文:</summary>
If you want to use `Authorization` conditionally, you can add `if` condition in `Configure` method instead of `ConfigureServices`.
Which means, you'll add the configuration but the `Authorization`/`Authentication` middleware will run only based on your condition.
</details>
# 答案2
**得分**: 0
**始终开启安全性**
与其在开发期间禁用安全性,一种本地测试的选项是使用带有测试私钥签名的JWT访问令牌调用您的API,然后将您的API指向包含公钥的JWKS URI。
这使您能够以用户级别的访问令牌测试所有与安全性和身份相关的逻辑,就像[我的这些示例测试](https://github.com/gary-archer/oauth.apisample.netcore/blob/master/test/IntegrationTests.cs#L96)一样。
**自定义身份验证处理程序**
听起来您想要中间件本身具有动态行为。[自定义身份验证处理程序](https://github.com/gary-archer/oauth.apisample.netcore/blob/master/src/host/startup/Startup.cs#L81)可以实现这一点。也许可以考虑从[JwtBearerHandler类](https://github.com/dotnet/aspnetcore/blob/main/src/Security/Authentication/JwtBearer/src/JwtBearerHandler.cs)继承,并根据当前是否激活安全性(例如,通过检查某些全局状态)来重写`HandleAuthenticateAsync`方法。
**授权**
如果从处理程序返回`AuthenticateResult.NoResult()`,将创建一个空的ClaimsPrincipal。然后,在没有身份验证时,您需要绕过授权。
**总结**
安全功能开关相当不标准,会增加复杂性并可能具有危险性。如果可能的话,应该考虑为始终开启的API安全性重新设计。这也会导致更简单的代码。
<details>
<summary>英文:</summary>
**ALWAYS-ON SECURITY**
Instead of disabling security, eg during development, one option for local testing is to call your API with JWT access tokens signed with a test private key, then point your API to a JWKS URI containing the public key.
This enables you to test all of the security and identity related logic productively, with user level access tokens, as in [these example tests](https://github.com/gary-archer/oauth.apisample.netcore/blob/master/test/IntegrationTests.cs#L96) of mine.
**CUSTOM AUTHENTICATION HANDLER**
Sounds like you want the middleware itself to have dynamic behaviour though. A [custom authentication handler](https://github.com/gary-archer/oauth.apisample.netcore/blob/master/src/host/startup/Startup.cs#L81) can enable that. Maybe look into inheriting from the [JwtBearerHandler class](https://github.com/dotnet/aspnetcore/blob/main/src/Security/Authentication/JwtBearer/src/JwtBearerHandler.cs) and overriding the `HandleAuthenticateAsync` method based on whether security is currently activated, eg by checking some global state.
**AUTHORIZATION**
If you return `AuthenticateResult.NoResult()` from the handler, an empty ClaimsPrincipal will be created. You would then need to bypass authorization when there is no identity.
**SUMMARY**
Security feature switches are pretty non-standard, add complexity and could be dangerous. Aim to redesign for always-on API security if you can. It will also lead to simpler code.
</details>
# 答案3
**得分**: 0
以下是已翻译的内容:
是不可能的,无法在运行时按请求添加或删除身份验证或授权。 AddAuthentication() 在启动时将所有内部注册为**单例**,这意味着一旦添加到服务集合中,就会永远存在。无论中间件和依赖项是在启动时启用还是不启用。
如果一个标志(配置、环境变量等)控制了何时以及如何启用(不是何时,它只在启动时或首次请求时启用一次),那么使用它的代码编写是不正确的,这不是什么高深的科学,只是一个 if/else 问题,重启应用而不进行部署是改变 if/else 初始评估的唯一方法。
其次,恕我直言,**这里问错了问题**,你需要同时启用Bearer和Bapi,并且**授权**中间件会根据资源/端点所需的访问策略来确定是否使用了适当的模式。
```csharp
[Authorize(AuthenticationSchemes = "Bapi")] // 这不是有条件的,不管是否注册了Bapi身份验证模式
解决方案是在多身份验证设置中使用身份验证中间件,并使用策略模式,在身份验证生成器选项中可以定义实际协商。
// 在配置多个身份验证模式时设置默认NegotiateScheme
services.AddAuthentication(options =>
{
options.DefaultAuthenticateScheme = "NegotiateScheme";
options.DefaultChallengeScheme = "NegotiateScheme"; // 或者更严格的访问可以使用Bearer
}).AddPolicyScheme("NegotiateScheme", "使用Bearer或Bapi或x-api-key basic进行授权", options =>
{
options.ForwardDefaultSelector = context =>
{
string authHeader = context.Request.Headers["Authorization"].FirstOrDefault();
string basicXApiKeyHeader = context.Request.Headers["x-api-key"].FirstOrDefault();
if (string.IsNullOrEmpty(basicXApiKeyHeader) == false)
{
return "x-api-key";
}
if (authHeader?.StartsWith("Bearer ", StringComparison.InvariantCultureIgnoreCase) == true)
{
return "Bearer";
}
if (authHeader?.StartsWith("Bapi ", StringComparison.InvariantCultureIgnoreCase) == true)
{
return "Bapi";
}
return "Bearer"; // 默认
};
})
.AddJwtBearer(...)
.AddJwtBapi(...)
.AddXApiKey(...);
免责声明:上述代码只是一个示例,请确保在使用之前进行编译、运行并满足您的需求。
英文:
Is impossible, cannot add or remove at runtime per request Authentication or Authorization. AddAuthentication() register all internals as singletons at startup, meaning once added to service collection is for eternity. Either middlewares and dependencies are enabled at startup either are not.
If a flag (config, env variable, whatever) controls what and how is enabled (not when, it only one time at startup or when is first time requested), code used it is written incorrectly, it's a if/else problem not rocket science, restarting the app without deployment is the only way to change that if/else initial evaluation.
Secondary, excuse the arrogance, but here wrong question is asked, you need to have both (Bearer and Bapi) enabled at same time and Authorization middleware determine if proper schema was use based on whatever policy access is required on the resource/endpoint.
[Authorize(AuthenticationSchemes = "Bapi")] // this is not conditional, doesn't matter that Bapi authentication scheme was registered or not
Solution is to have Authentication middleware with a negotiate default scheme for a multi authentication setup, with a policy scheme where the actual negotiation ca be defined inside options of authentication builder.
// Set a default NegotiateScheme when multiple authentication schemes are configured
services.AddAuthentication(options =>
{
options.DefaultAuthenticateScheme = "NegotiateScheme";
options.DefaultChallengeScheme = "NegotiateScheme"; // it can be Bearer for a more restrictive access
}).AddPolicyScheme("NegotiateScheme", "Authorization with Bearer or Bapi or x-api-key basic", options =>
{
options.ForwardDefaultSelector = context =>
{
string authHeader = context.Request.Headers["Authorization"].FirstOrDefault();
string basicXApiKeyHeader = context.Request.Headers["x-api-key"].FirstOrDefault();
if (string.IsNullOrEmpty(basicXApiKeyHeader) == false)
{
return "x-api-key";
}
if (authHeader?.StartsWith($"Bearer ", StringComparison.InvariantCultureIgnoreCase) == true)
{
return "Bearer";
}
if (authHeader?.StartsWith($"Bapi ", StringComparison.InvariantCultureIgnoreCase) == true)
{
return "Bapi";
}
return "Bearer"; // default
};
})
.AddJwtBearer(...)
.AddJwtBapi(...)
.AddXApiKey(...);
Disclaimer: above code is an example, make sure it compile, runs and fits your needs before using.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论