使用 .net core Web API 验证 .net 控制台应用程序

A_d*_*per 2 c# authentication jwt asp.net-web-api asp.net-core-webapi

我有一个.net core 3.1 Web API ,它是使用JWT 身份验证构建的,它与 Angular UI 集成,并且按预期工作。

以下是我的 JWT 身份验证中间件


services.AddAuthentication(options =>
{
    options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
    options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
    options.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
})

// Adding Jwt Bearer
.AddJwtBearer(options =>
{
    options.SaveToken = true;
    options.RequireHttpsMetadata = false;
    options.IncludeErrorDetails = true;
    options.TokenValidationParameters = new TokenValidationParameters()
    {
        ValidateIssuer = false,
        ValidateAudience = false,       
        IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Configuration["JWT:Secret"]))    
    };
    options.Events = new JwtBearerEvents
    {
        OnAuthenticationFailed = context =>
        {
            if (context.Exception.GetType() == typeof(SecurityTokenExpiredException))
            {
                context.Response.Headers.Add("Token-Expired", "true");
            }
            return Task.CompletedTask;
        }
    };
});
Run Code Online (Sandbox Code Playgroud)

现在我需要创建更多的 Web API 方法,这些方法将由Angular UI以及一些现有的计划任务(将使用 Web API 方法的.net 控制台应用程序)使用,这些任务是为内部操作创建的,并将在背景。

我的 API 控制器用[Authorize]属性装饰。它与 Angular UI 配合得很好,其中身份验证和授权是使用 JWT 不记名令牌实现的。现在的问题是计划任务的集成,它没有获取令牌的逻辑。

如何在身份验证方面将这些控制台应用程序与 .net core Web API 集成?最简单的选择(我认为)是使用用户名“servicetask”创建一个用户登录,并根据该用户名获取令牌并执行 API 操作(但这需要更多的努力,因为控制台应用程序的数量越来越多,并且有一些也来自其他项目的应用程序)。

在这种情况下有什么办法可以处理身份验证吗?

  1. 从控制台应用程序传递一些 API 密钥并绕过 Web API 中的身份验证是一种好的做法吗?那可能吗 ?那么.net core web api中如何处理请求呢?

  2. 是否可以为这些服务帐户创建任何 JWT 角色或声明并验证它们?

请帮忙。

nig*_*awk 5

最好的方法是允许不记名令牌和 API 密钥授权,特别是因为您允许用户和(内部)服务访问。

添加 API 密钥中间件(我个人使用这个,使用起来很简单 - 包名称是AspNetCore.Authentication.ApiKey)和自定义验证(将 API 密钥与常规用户数据一起存储在数据库中或在配置中,无论您喜欢什么)。修改[Authorize]控制器上的属性,以便可以使用 Bearer 和 ApiKey 授权。Angular 应用程序继续使用 Bearer 身份验证,并且任何服务/控制台应用程序(或任何其他客户端,包括 Angular 客户端(如果在某些情况下需要的话))发送包含分配给该应用程序的 API 密钥的 X-Api-Key 标头。

中间件配置应该如下所示:

services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddApiKeyInHeader(ApiKeyDefaults.AuthenticationScheme, options =>
{
    options.KeyName = "X-API-Key";
    options.SuppressWWWAuthenticateHeader = true;
    options.Events = new ApiKeyEvents
    {
        // A delegate assigned to this property will be invoked just before validating the api key. 
        OnValidateKey = async (context) =>
        {
            var apiKey = context.ApiKey.ToLower();
            // custom code to handle the api key, create principal and call Success method on context. apiUserService should look up the API key and determine is it valid and which user/service is using it
            var apiUser = apiUserService.Validate(apiKey);
            if (apiUser != null)
            {
                ... fill out the claims just as you would for user which authenticated using Bearer token...
                var claims = GenerateClaims();
                context.Principal = new ClaimsPrincipal(new ClaimsIdentity(claims, context.Scheme.Name));
                context.Success();
             }
             else
             {
                 // supplied API key is invalid, this authentication cannot proceed
                 context.NoResult();
             }
         }
        };
})
// continue with JwtBearer code you have
.AddJwtBearer(JwtBearerDefaults.AuthenticationScheme, x => ...
Run Code Online (Sandbox Code Playgroud)

这整理了Startup.cs一部分。

现在,在控制器中,您想要启用 Bearer 和 ApiKey 身份验证,修改属性,使其如下所示:

[Route("api/[controller]")]
[ApiController]
[Authorize(AuthenticationSchemes = "ApiKey, Bearer")]
public class SomeController : ControllerBase
Run Code Online (Sandbox Code Playgroud)

现在,Angular 客户端仍将以相同的方式工作,但控制台应用程序可能会像这样调用 API:

using (HttpClient client = new HttpClient())
{
    // header must match definition in middleware
    client.DefaultRequestHeaders.Add("X-API-Key", "someapikey");
    client.BaseAddress = new Uri(url);
    using (HttpResponseMessage response = await client.PostAsync(url, q))
    {
        using (HttpContent content =response.Content)
        {
            string mycontent = await content.ReadAsStringAsync();              
        }        
    }
}
Run Code Online (Sandbox Code Playgroud)

在我看来,这种方法充分利用了AuthenticationHandler,并提供了最干净的方法来处理使用 JWT 的“常规”客户端和使用固定 API 密钥的服务,密切关注OAuth 中间件之类的东西。如果有人想从头开始构建类似的东西,基本上实现任何类型的身份验证,则有关构建自定义身份验证处理程序的更多详细信息。

当然,缺点是这些 API 密钥的安全性,即使您仅将它们用于内部服务。这个问题可以通过使用声明限制这些 API 密钥的访问范围、不对多个服务使用相同的 API 密钥并定期更改它们来解决。此外,如果不使用 SSL,API 密钥很容易被拦截 (MITM),因此请注意这一点。