Аутентифікація за API ключем у .NET 6+: Власні заголовки, фільтри дій та Swagger.

API (Application Programming Interface) - це як посланець у світі програмного забезпечення. Вони забезпечують можливість спілкування між різними додатками. Але так само, як ви не хочете, щоб випадкові незнайомці читали ваші особисті повідомлення, ви також не хочете, щоб просто будь-хто отримував доступ до вашого API. І для цього існує аутентифікація за допомогою API ключів.

У цій статті ми розглянемо, що таке API ключі, як вони працюють для захисту вашого API, і як ви можете налаштувати аутентифікацію за допомогою API ключа в .NET 6+ API з використанням Swagger. Уявіть, що це схоже на надання вашому API свого власного VIP списку гостей, і тільки ті, хто має правильний ключ, можуть потрапити.

Що таке аутентифікація API ключем?

Аутентифікація за допомогою API ключа - це ефективний спосіб захисту ваших API шляхом вимоги унікального ключа (API ключа) для кожного запиту. Це унікальний ідентифікатор, зазвичай довга алфавітно-цифрова стрічка, надана клієнту для аутентифікації запитів, що забезпечує доступ лише для авторизованих користувачів або додатків. Ключ може передаватися в заголовку запиту, рядку запиту URL або в тілі запиту. У цій статті ми розглянемо реалізацію аутентифікації API ключем, використовуючи власний заголовок запиту.

Коли використовувати аутентифікацію API ключем?

Аутентифікація API ключем ідеально підходить для:

  • Публічних API, де ви хочете контролювати доступ.
  • Внутрішніх API, які використовуються в межах довіреної команди або організації.
  • Ситуацій, коли аутентифікація на рівні користувача (наприклад, OAuth або JWT) не є необхідною, наприклад, для інтеграції з корпоративною послугою.

Припустимо, ви створили сервіс для гаманців, який дозволяє людям створювати гаманці і керувати всіма видами транзакцій для користувачів. Це чудово, і я хочу використовувати це, щоб спростити життя моїм користувачам замість того, щоб будувати те ж саме з нуля. Тепер, ви ж не хочете, щоб просто будь-хто отримував доступ до вашого API, правда? Ви хочете переконатися, що доступ має лише авторизовані люди (як я). Як це зробити?

Ви генеруєте API ключ спеціально для мене і говорите, щоб я включав його в кожен запит, який відправляю на ваш API. Таким чином, ваша система знає, що це я роблю запит, а не хтось інший. Ви також можете дати мені AppId або AppName, щоб ви могли відслідковувати, який додаток використовує ваш API.

Такий підхід спрощує все, робить безпечним і легким для управління. Це як надати мені унікальний пароль для доступу до вашої послуги, але без складності керування обліковими записами користувачів.

Добре, досить слів! Перейдімо до реалізації.

  1. ПЕРЕДУМОВИ

Перед тим, як почати, переконайтеся, що у вас є .NET 6+ Web API Project. Цей посібник припускає, що ви вже створили проект .NET Web API. Я також використовую Visual Studio як свою основну IDE.

  1. ІНСТАЛЮЙТЕ НЕОБХІДНИЙ ПАКЕТ NUGET

Для початку роботи з Swagger для вашого API потрібно встановити NuGet пакет Swashbuckle.AspNetCore. Swagger спрощує розробникам тестування та інтеграцію API.

При створенні веб API проекту в Visual Studio шаблон .NET Web API за замовчуванням включає підтримку OpenAPI, яку можна вимкнути, якщо це не потрібно. Якщо підтримка OpenAPI увімкнена, ваш проект автоматично налаштується з необхідною залежністю NuGet, зазначеною вище.

Інакше ви можете використовувати NuGet Package Manager в Visual Studio або виконати ці кроки:

  • Відкрийте термінал або командний рядок.
  • Перейдіть до каталогу вашого проекту.
  • Виконайте наступну команду для додавання пакету:
dotnet add package Swashbuckle.AspNetCore --version 6.5.0
  1. ДОДАТИ ВЛАСНИЙ ЗАГОЛОВОК ДЛЯ API КЛЮЧА У SWAGGER

Щоб ефективно використовувати аутентифікацію за допомогою API ключа у вашому API, Swagger повинен попросити клієнтів або користувачів вказати API ключ у заголовку запиту. Для цього потрібно додати власний заголовок у ваш інтерфейс користувача (UI), що вимагатиме введення API ключа перед тим, як запит буде надісланий.
Інтерфейс IOperationFilter в просторі імен ‘Swashbuckle.AspNetCore.SwaggerGen’ має метод Apply, який допомагає нам це реалізувати.

У наведеному коді ми налаштовуємо характеристики нашого власного заголовка — тип аутентифікації, де він має бути розташований, опис та інші параметри. Я також навів приклад, коли вам може знадобитися додати більше ніж один заголовок для аутентифікації, в даному випадку AppId разом з ApiKey.

using Microsoft.OpenApi.Models;  
using Swashbuckle.AspNetCore.SwaggerGen;  

namespace ApiKeyImplementation.Helpers  
{  
 public class CustomHeaderSwaggerAttribute : IOperationFilter  
 {  
 public void Apply(OpenApiOperation operation, OperationFilterContext context)  
 {  
 if (operation.Parameters == null)  
 operation.Parameters = new List();  
 operation.Parameters.Add(new OpenApiParameter  
 {  
 Name = VarHelper.KeyTypes.ApiKey.ToString(), //Додаємо ім’я ключа  
 In = ParameterLocation.Header, //Встановлюємо місце розташування  
 Required = true,  
 Description = "Додайте опис за потреби",  
 Schema = new OpenApiSchema  
 {  
 Type = "string"  
 }  
 });  
 //Якщо ваша програма вимагає AppId як частину аутентифікації  
 operation.Parameters.Add(new OpenApiParameter  
 {  
 Name = VarHelper.KeyTypes.AppId.ToString(),  
 In = ParameterLocation.Header,  
 Required = true,  
 Schema = new OpenApiSchema  
 {  
 Type = "string"  
 }  
 });  
 }  
 }  
}

Після реалізації вище зазначеного, ваш фільтр операцій (власний заголовок) повинен бути зареєстрований в пайплайні, щоб він відображався в UI Swagger вашого API. Для цього додайте код нижче в секцію конфігурації Swagger у program.cs.

var builder = WebApplication.CreateBuilder(args);  
builder.Services.AddControllers();  
builder.Services.AddEndpointsApiExplorer();  
builder.Services.AddSwaggerGen(config =>  
{  
 // Додаємо фільтр операцій для власного заголовка  
 config.OperationFilter();  
 // Встановлюємо шлях до коментарів для Swagger JSON та UI.  
 var xmlFile = $"{Assembly.GetExecutingAssembly().GetName().Name}.xml";  
 var xmlPath = Path.Combine(AppContext.BaseDirectory, xmlFile);  
 config.IncludeXmlComments(xmlPath);  
 config.SwaggerDoc("v1", new OpenApiInfo()  
 {  
 Version = "v1",  
 Title = "API для Wallet Service"  
 });  
});

Після того як ви інструктуєте користувачів додавати API ключі та AppId до заголовків запиту, як ми це зробили вище, важливо переконатися, що ми перевіряємо значення, передані перед тим, як дозволити доступ до нашого API.

  1. ВАЛІДАЦІЯ API КЛЮЧА З ВИКОРИСТАННЯМ ACTION FILTER

Action Filter — це тип фільтра, який виконує логіку як до, так і після виконання методу контролера. Це ідеально підходить для повторюваних завдань, таких як валідація API ключа. Чому? Тому що він є першим, хто виконується до того, як ваші методи контролера навіть будуть оброблені. Якщо валідація проходить, доступ до вашого API надається. Якщо ж ні, запит зупиняється на самому початку.

Action Filters дозволяють обробляти це в одному централізованому місці, що означає, що вам не потрібно повторювати один і той самий код валідації в усіх ваших контролерах. Це зберігає ваш код чистим і дозволяє контролерам зосередитися на виконанні своїх основних функцій.

Щоб створити Action Filter для валідації API ключа, просто створіть новий клас, який успадковується як від Attribute, так і від IAsycnActionFilter в просторі імен ‘AspNetCore.Mvc.Filters’, і реалізуйте всю необхідну логіку в ньому.
Ось реалізація коду нижче.

using ApiKeyImplementation.Helpers;  
using Microsoft.AspNetCore.Mvc.Filters;  
using Microsoft.EntityFrameworkCore;  
using ApiKeyImplementation.Models;  
using ApiKeyImplementation.Responses;  
using Newtonsoft.Json;  
using System.Net;  

namespace ApiKeyImplementation.Middlewares  
{  
 [AttributeUsage(validOn: AttributeTargets.Class)]  
 public class ApiKeyAttribute : Attribute, IAsyncActionFilter  
 {  
 public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)  
 {  
 /* В реалізації CustomHeaderSwggerAttribute це значення було встановлено як обов'язкове, і вважається, що swagger  
 * має обробляти цю валідацію, але я надаю перевагу контролювати це і не покладатися на фронтенд або користувача.  
 * Ви можете ігнорувати мою параноїдальну травму, якщо хочете, і пропустити реалізацію  
 * перевірки, чи містить заголовок запиту apiKey, як вказано в UI  
 */  
 if (!context.HttpContext.Request.Headers.TryGetValue(VarHelper.KeyTypes.ApiKey.ToString(), out var retrievedApiKey))  
 {  
 await HandleResponseAsync(context.HttpContext, "ApiKey не був наданий");  
 return;  
 }  
 // Теж саме для AppId, якщо це необхідно  
 if (!context.HttpContext.Request.Headers.TryGetValue(VarHelper.KeyTypes.AppId.ToString(), out var retrievedAppId))  
 {  
 await HandleResponseAsync(context.HttpContext, "AppId не був наданий");  
 return;  
 }  
 // Перевіряємо отримані значення і перевіряємо, чи існує клієнт з наданими ApiKey та AppId  
 var _DbContext = context.HttpContext.RequestServices.GetRequiredService();  
 var record = await _DbContext.AppConfig.SingleOrDefaultAsync(x => x.AppKey == retrievedApiKey.ToString() && x.AppId == retrievedAppId.ToString());  
 if (record == null)  
 {  
 await HandleResponseAsync(context.HttpContext, "Неавторизований клієнт");  
 return;  
 }  
 if (record.AccessStatus == VarHelper.AccessStatus.DISABLED.ToString())  
 {  
 await HandleResponseAsync(context.HttpContext, "Клієнт заблокований. Ваш доступ до цього сервісу був припинений.");  
 return;  
 }  
 await next();  
 }  
 // Налаштування відповіді  
 private Task HandleResponseAsync(HttpContext context, string message)  
 {  
 context.Response.StatusCode = (int)HttpStatusCode.Forbidden;  
 context.Response.ContentType = "application/json";  
 return context.Response.WriteAsync(JsonConvert.SerializeObject(new ResponseBase(context.Response.StatusCode, message, VarHelper.ResponseStatus.ERROR.ToString())));  
 }  
 }  
}
  1. Застосування схеми аутентифікації API ключа

Атрибут [AttributeUsage(validOn: AttributeTargets.Class)] в коді ApiKeyAttribute визначає, де можна застосовувати ваш фільтр API ключа.

Як і будь-який Action Filter, він може бути застосований на трьох рівнях:

  1. Рівень дії (Action Level)
  2. Рівень контролера (Controller Level)
  3. Глобальний рівень (Global Level)

Розуміючи ці рівні застосування фільтрів дії, ви можете реалізувати аутентифікацію API ключа в спосіб, що найкраще відповідає структурі вашого проекту та потребам безпеки.

Тепер давайте розглянемо їх:

1. Застосування на рівні контролера:

Опція AttributeTargets.Class обмежує використання ApiKey до контролерів або класів. Це гарантує, що фільтр не буде випадково застосований до методів чи інших частин вашого коду.

Ось приклад того, як це працює на рівні контролера:

[ApiKey]  
 [ApiController]  
 [EnableCors("AllowAllHeaders")]  
 public class LookupController : ControllerBase  
 {  
 //Логіка контролера тут  
 }

Зверніть увагу, як використано [ApiKey] замість [ApiKeyAttribute]. C# дозволяє опускати суфікс "Attribute" для зручності та стилю, тому [ApiKey] є еквівалентом або тим самим, що й [ApiKeyAttribute].

2. Застосування на рівні дії:

Ви можете застосувати фільтр ApiKey до конкретних дій або методів, використовуючи AttributeTargets.Method. Це забезпечить, що фільтр буде застосований лише до тих методів, де він явно необхідний.

3.

Застосування на глобальному рівні:

Щоб застосувати фільтр ApiKey до всіх контролерів автоматично, без необхідності окремо додавати [ApiKey] на кожному контролері_, зареєструйте його як глобальний фільтр у program.cs:

builder.Services.AddMvc(options =>  
{  
 options.Filters.Add(typeof(ApiKeyAttribute));  
});

Цей підхід ідеально підходить для проектів, де кожен контролер вимагає перевірки API ключа. Однак, якщо деякі контролери чи дії повинні обходити цю перевірку, знадобиться додаткова логіка для їх виключення.

Наприклад:

  • Додати логіку у фільтр для перевірки конкретних умов (наприклад, шаблонів маршрутів або метаданих).
  • Застосувати атрибут вибірково на рівні контролера, а не глобально, видаливши його з program.cs.

У таких випадках опція AttributeTargets.Class добре працює для застосування фільтра лише до конкретних контролерів.

Нижче наведено приклад того, як ця реалізація виглядає у Swagger UI, використовуючи власний заголовок для аутентифікації за API ключем.

pic

Інтеграція аутентифікації API ключем зі Swagger

Щоб зробити вашу реалізацію API ключа безпечною та надійною, рекомендується:

1. Використовувати безпечне сховище

  • Уникайте хардкодингу ключів у вашому вихідному коді, оскільки це може призвести до вразливостей безпеки.

2. Заміна ключів

  • Регулярно генеруйте та розподіляйте нові API ключі клієнтам.
  • Встановлюйте терміни дії для ключів, щоб мінімізувати наслідки потенційних витоків ключів.

Я знаю, що ця стаття вийшла довшою, ніж планувалося. Можливо, я трохи захопився. Але я хотів переконатися, що охопив все необхідне для впевненого впровадження аутентифікації за API ключем. Сподіваюся, я це зробив.

З деталями, які ми пройшли, від того, що таке аутентифікація за API ключем до того, як реалізувати її у .NET 6+ API як власний заголовок з використанням Swagger, тепер ви повинні бути здатні налаштувати це ефективно самостійно.

Дякую, що залишились зі мною.

Перекладено з: API Key Authentication in .NET 6+: Custom Headers, Action Filters, and Swagger.

Leave a Reply

Your email address will not be published. Required fields are marked *