Как стать автором
Обновить

Используем API Key и JWT Bearer аутентификацию вместе в ASP.NET Core Web API

Уровень сложностиСредний
Время на прочтение4 мин
Количество просмотров3.3K

Я расскажу, как реализовать аутентификацию с использованием как JWT, так и API-ключа на одном и том же endpoint в ASP.NET Core Web API. Совмещение этих схем аутентификации полезно, если вы хотите использовать токен JWT Bearer для аутентификации пользователей и API-ключ для аутентификации между сервисами.

Эта статья основана на моем предыдущей статье, в которой я уже рассмотрел аутентификацию с использованием API-ключа в ASP.NET Core. Если вам нужно подробное объяснение этой реализации, обратитесь к ней. Эта реализация была протестирована как в .NET 8, так и в .NET 9.

ASP.NET Core позволяет комбинировать обработчики аутентификации для поддержки нескольких схем, и я продемонстрирую, как это реализовать.

Реализация составного обработчика аутентификации

Для того чтобы включить аутентификацию как с использованием JWT, так и с использованием API-ключа на одном endpoint, нам нужен составной обработчик аутентификации. Этот обработчик определяет, какую схему аутентификации применить в зависимости от запроса. Сначала мы проверим, содержит ли запрос API-ключ; если нет, мы применим схему аутентификации с использованием JWT. Реализация показана ниже:

internal static class CompositeAuthenticationDefaults
{
    public const string AuthenticationScheme =
        $"Composite-{JwtBearerDefaults.AuthenticationScheme}-{ApiKeyAuthenticationDefaults.ApiKeyHeaderName}";
}

internal sealed class CompositeAuthenticationHandler(
    IOptionsMonitor<AuthenticationSchemeOptions> options, ILoggerFactory logger, UrlEncoder encoder)
    : AuthenticationHandler<AuthenticationSchemeOptions>(options, logger, encoder)
{
    protected override async Task<AuthenticateResult> HandleAuthenticateAsync()
    {
        var scheme = GetAuthunticationScheme();
        return await Context.AuthenticateAsync(scheme);
    }

    protected override async Task HandleChallengeAsync(AuthenticationProperties properties)
    {
        var scheme = GetAuthunticationScheme();
        await Context.ChallengeAsync(scheme);
    }

    protected override async Task HandleForbiddenAsync(AuthenticationProperties properties)
    {
        var scheme = GetAuthunticationScheme();
        await Context.ForbidAsync(scheme);
    }

    private bool IsApiKeyAuthScheme()
        => Request.Headers.ContainsKey(ApiKeyAuthenticationDefaults.ApiKeyHeaderName);

    private string GetAuthunticationScheme()
        => IsApiKeyAuthScheme()
            ? ApiKeyAuthenticationDefaults.AuthenticationScheme
            : JwtBearerDefaults.AuthenticationScheme;
}

Настройка составной схемы аутентификации

Далее нам нужно настроить наши службы аутентификации для использования как JWT, так и API-ключа в файле Program.cs:

builder.Services.AddAuthentication(options =>
    {
        options.DefaultScheme = CompositeAuthenticationDefaults.AuthenticationScheme;
    })
    .AddScheme<AuthenticationSchemeOptions, CompositeAuthenticationHandler>(CompositeAuthenticationDefaults.AuthenticationScheme, _ => { })
    .AddScheme<AuthenticationSchemeOptions, ApiKeyAuthenticationHandler>(ApiKeyAuthenticationDefaults.AuthenticationScheme, _ => { })
    .AddJwtBearer(JwtBearerDefaults.AuthenticationScheme, options =>
    {
        options.Authority = "authority";
        options.MetadataAddress = "metadata-address"; // e.g. https://login.microsoftonline.com/your-tenant-id/v2.0/.well-known/openid-configuration
        options.RequireHttpsMetadata = !builder.Environment.IsDevelopment();

        options.TokenValidationParameters = new TokenValidationParameters
        {
            ValidateIssuer = true,
            ValidateAudience = false,
            ValidateLifetime = true,
            ValidateIssuerSigningKey = true
        };
    });

builder.Services.AddAuthorization();

В этом примере конфигурация JWT гарантирует, что токены выданы доверенным провайдером и проверяет ключевые аспекты безопасности, такие как срок действия и ключ подписи.

Чтобы добавить аутентификацию JWT в ваш проект, установите пакет Microsoft.AspNetCore.Authentication.JwtBearer из NuGet.

Применение схем аутентификации в контроллерах

Для защиты API с использованием обеих схем аутентификации примените атрибут [Authorize] следующим образом:

[Authorize]
[ApiController]
[Route("api/[controller]")]
public class SecureController : ControllerBase
{
    [HttpGet]
    public IActionResult GetSecureData()
    {
        return Ok(new { Message = "This is a secure endpoint." });
    }
}

Если вы хотите ограничить endpoint использованием только JWT схемы, укажите:

[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]

Аналогично для API Key:

[Authorize(AuthenticationSchemes = ApiKeyAuthenticationDefaults.AuthenticationScheme)]

Ниже пример использования [Authorize] атрибута в minimal API:

app.MapGet("/api/secure", [Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)] () =>
{
    return new { Message = "This is a secure endpoint." };
}).RequireAuthorization();

Заключение

Реализовав свой составной обработчик аутентификации, мы можем без проблем поддерживать как JWT, так и аутентификацию с использованием API-ключа на одном и том же API endpoint. Этот подход предоставляет гибкость для сценариев, когда различные пользователи вашего API требуют разных методов аутентификации.

Если вы нашли это руководство полезным или у вас есть вопросы, не стесняйтесь оставить комментарий!

Дополнение в ответ на комментарий: Использование AuthorizationPolicyBuilder для установки политики по умолчанию, как в коде ниже:

builder.Services.AddAuthorizationBuilder()
    .SetDefaultPolicy(new AuthorizationPolicyBuilder(ApiKeyAuthenticationDefaults.AuthenticationScheme, JwtBearerDefaults.AuthenticationScheme)
        .RequireAuthenticatedUser()
        .Build());

не подходит, если вы хотите применять только одну схему аутентификации для конкретного API-эндпоинта, поскольку устанавливает политику авторизации по умолчанию, которая допускает аутентификацию либо через API-ключ, либо через JWT для всех защищенных эндпоинтов. Это делает невозможным ограничение эндпоинта только одной схемой (например, только JWT или только API-ключ). Это может непреднамеренно привести к несанкционированному доступу, если эндпоинт должен поддерживать только один метод аутентификации. Например, если эндпоинт предназначен для аутентификации через JWT, но по умолчанию также разрешает API-ключ, это может создать угрозу безопасности, позволяя API-ключу обходить более строгие проверки аутентификации.

Теги:
Хабы:
Всего голосов 2: ↑2 и ↓0+2
Комментарии11

Публикации

Истории

Работа

Ближайшие события

19 марта – 28 апреля
Экспедиция «Рэйдикс»
Нижний НовгородЕкатеринбургНовосибирскВладивостокИжевскКазаньТюменьУфаИркутскЧелябинскСамараХабаровскКрасноярскОмск
22 апреля
VK Видео Meetup 2025
МоскваОнлайн
23 апреля
Meetup DevOps 43Tech
Санкт-ПетербургОнлайн
24 апреля
VK Go Meetup 2025
Санкт-ПетербургОнлайн
25 – 26 апреля
IT-конференция Merge Tatarstan 2025
Казань
14 мая
LinkMeetup
Москва
5 июня
Конференция TechRec AI&HR 2025
МоскваОнлайн
20 – 22 июня
Летняя айти-тусовка Summer Merge
Ульяновская область