Как реализовать аутентификацию JWT в ASP.NET Core

автор vadim


ASP.NET Core предлагает упрощенную модель хостинга, называемую минимальными API, которая позволяет нам создавать облегченные API с минимальными зависимостями. Минимальные API идеально подходят для создания микросервисов и быстрых HTTP API. Естественно, вам часто придется защищать конечные точки таких API в ваших приложениях. Цель этого поста — дать вам фору в этом.

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

Чтобы защитить минимальный API с использованием аутентификации JWT, мы выполним следующие шаги:

  1. Создайте минимальный проект API в Visual Studio 2022.
  2. Создайте конечную точку API в файле Program.cs.
  3. Добавьте пакет NuGet Microsoft.AspNetCore.Authentication.JwtBearer в наш проект.
  4. Реализуйте аутентификацию JWT в файле Program.cs.
  5. Создайте класс модели пользователя с именем User для хранения учетных данных пользователя.
  6. Укажите секретный ключ в файле appsettings.json.
  7. Укажите параметры аутентификации JWT в файле Program.cs.
  8. Добавьте промежуточное программное обеспечение служб авторизации в наше приложение в файле Program.cs.
  9. Создайте и проверьте веб-токен JSON в файле Program.cs.

Обратите внимание, что все примеры кода, показанные в этом посте, за исключением класса модели User, должны быть частью Program.cs. Класс модели User должен быть частью файла User.cs.

Для работы с примерами кода, представленными в этой статье, в вашей системе должна быть установлена ​​Visual Studio 2022. Если у вас еще нет копии, вы можете скачать Visual Studio 2022 здесь.

Создайте минимальный проект веб-API ASP.NET Core в Visual Studio 2022.

Прежде всего давайте создадим проект ASP.NET Core в Visual Studio. Выполнение этих шагов приведет к созданию нового проекта веб-API ASP.NET Core в Visual Studio 2022:

  1. Запустите интегрированную среду разработки Visual Studio 2022.
  2. Нажмите «Создать новый проект».
  3. В окне «Создать новый проект» выберите «ASP.NET Core Web API» из списка отображаемых шаблонов.
  4. Нажмите “Далее.
  5. В окне «Настроить новый проект» укажите имя и местоположение нового проекта.
  6. При желании установите флажок «Поместить решение и проект в один каталог», в зависимости от ваших предпочтений.
  7. Нажмите “Далее.
  8. В следующем окне «Дополнительная информация» снимите флажок «Использовать контроллеры…», поскольку в этом примере мы будем использовать минимальные API. В поле «Тип аутентификации» оставьте значение «Нет» (по умолчанию).
  9. Убедитесь, что флажки «Включить Docker», «Настроить HTTPS» и «Включить поддержку Open API» сняты, поскольку мы не будем здесь использовать ни одну из этих функций.
  10. Нажмите Создать.

Мы будем использовать этот проект веб-API ASP.NET Core для создания минимальной конечной точки API и реализации для нее аутентификации JWT в последующих разделах этой статьи.

Создайте конечную точку HTTP Get в ASP.NET Core.

Когда вы создаете новый минимальный проект веб-API в Visual Studio 2022, будет создан файл Program.cs с несколькими строками кода по умолчанию. Вы можете заменить код по умолчанию следующим фрагментом кода, чтобы упростить задачу и при этом предоставить возможность протестировать свой API.

var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.MapGet("/security/getMessage",
() => "Hello World!").RequireAuthorization();
app.Run();

Обратите внимание на использование здесь метода расширения RequireAuthorization. Это поможет вам защитить ваши маршруты с помощью политик авторизации и вынудит вас предоставить информацию для аутентификации при вызове этой конечной точки. Промежуточное программное обеспечение авторизации будет использовать эту информацию для проверки запроса для текущего контекста выполнения.

Если вы запустите эту конечную точку без этой информации, вы столкнетесь с ошибкой HTTP 401 Unauthorized, как показано на рисунке 1.

безопасный минимальный API 01 ИДГ

Рисунок 1. Ошибка HTTP 401 Unauthorized будет сгенерирована, если требуется авторизация и не предоставлена ​​информация об авторизации.

Установите пакет NuGet JwtBearer.

Теперь добавьте пакет NuGet Microsoft.AspNetCore.Authentication.JwtBearer в свой проект. Для этого выберите проект в окне обозревателя решений, затем щелкните правой кнопкой мыши и выберите «Управление пакетами NuGet». В окне диспетчера пакетов NuGet найдите пакет Microsoft.AspNetCore.Authentication.JwtBearer и установите его.

Кроме того, вы можете установить пакет через консоль диспетчера пакетов NuGet, введя команду, показанную ниже.

PM> Install-Package Microsoft.AspNetCore.Authentication.JwtBearer

Укажите секретный ключ в файле appsettings.json.

Затем создайте раздел в файле appsettings.json для информации об эмитенте, аудитории и ключе. Эта информация будет использоваться позже для создания веб-токена JSON. Обратите внимание, что вы можете дать этому разделу любое имя; Для удобства я буду использовать имя «Jwt».

Добавьте следующую информацию в файл appsettings.json.

  "Jwt": {
    "Issuer": "https://joydipkanjilal.com/",
    "Audience": "https://joydipkanjilal.com/",
    "Key": "This is a sample secret key - please don't use in production environment.'"
  }

Укажите параметры аутентификации в файле Program.cs.

Метод AddAuthenication в файле Program.cs используется для настройки аутентификации JWT во время запуска приложения. Он определяет схему аутентификации как JwtBearer. Кроме того, вызов метода AddJwtBearer помогает настроить параметры токена.

Значения «Издатель», «Аудитория» и «Ключ» считываются из файла конфигурации appsettings.json. Экземпляр TokenValidationParameters используется, чтобы указать, следует ли проверять информацию об эмитенте, аудитории, ключе и сроке действия.

builder.Services.AddAuthentication(options =>
    {
    options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
    options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
    options.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
}).AddJwtBearer(o =>
{
    o.TokenValidationParameters = new TokenValidationParameters
    {
        ValidIssuer = builder.Configuration["Jwt:Issuer"],
        ValidAudience = builder.Configuration["Jwt:Audience"],
        IssuerSigningKey = new SymmetricSecurityKey
        (Encoding.UTF8.GetBytes(builder.Configuration["Jwt:Key"])),
        ValidateIssuer = true,
        ValidateAudience = true,
        ValidateLifetime = false,
        ValidateIssuerSigningKey = true
    };
});

Чтобы добавить службы авторизации в ваше приложение, ваш файл Program.cs также должен включать следующий фрагмент кода.

builder.Services.AddAuthorization();

Ваш Program.cs также должен включать следующие методы для включения возможностей аутентификации и авторизации.

app.UseAuthentication();
app.UseAuthorization();

Создайте модель пользователя в ASP.NET Core.

Нам понадобится класс для хранения учетных данных пользователя или пользователей. Создайте класс с именем User в файле с тем же именем и расширением .cs. Затем вставьте следующий код.

public class User
{
    public string UserName { get; set; }
    public string Password { get; set; }
}

Этот класс будет использоваться для приема учетных данных пользователя в качестве входных данных.

Создайте конечную точку для генерации веб-токенов JSON.

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

Теперь напишите следующий код в файле Program.cs, чтобы создать новую конечную точку HTTP Post, которая создаст JWT для аутентифицированного пользователя.

app.MapPost("/security/createToken",
[AllowAnonymous] (User user) =>
{
    if (user.UserName == "joydip" && user.Password == "joydip123")
    {
        var issuer = builder.Configuration["Jwt:Issuer"];
        var audience = builder.Configuration["Jwt:Audience"];
        var key = Encoding.ASCII.GetBytes
        (builder.Configuration["Jwt:Key"]);
        var tokenDescriptor = new SecurityTokenDescriptor
        {
            Subject = new ClaimsIdentity(new[]
            {
                new Claim("Id", Guid.NewGuid().ToString()),
                new Claim(JwtRegisteredClaimNames.Sub, user.UserName),
                new Claim(JwtRegisteredClaimNames.Email, user.UserName),
                new Claim(JwtRegisteredClaimNames.Jti,
                Guid.NewGuid().ToString())
            }),
            Expires = DateTime.UtcNow.AddMinutes(5),
            Issuer = issuer,
            Audience = audience,
            SigningCredentials = new SigningCredentials
            (new SymmetricSecurityKey(key),
            SecurityAlgorithms.HmacSha512Signature)
        };
        var tokenHandler = new JwtSecurityTokenHandler();         var token = tokenHandler.CreateToken(tokenDescriptor);         var jwtToken = tokenHandler.WriteToken(token);         var stringToken = tokenHandler.WriteToken(token);         return Results.Ok(stringToken);     }     return Results.Unauthorized(); });

Экземпляр класса User используется для принятия имени пользователя и пароля, передаваемых в эту конечную точку. Обратите внимание на атрибут AllowAnonymous. Это используется, чтобы указать, что нам не нужна проверка авторизации в этой конечной точке. Эмитент, Аудитория и Ключ считываются из файла конфигурации. Каждый из них используется для создания токена, срок действия которого, как мы указали, истекает через пять минут.

Полный исходный код Program.cs

Вот полный исходный код файла Program.cs для справки.

using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Authorization;
using Microsoft.IdentityModel.Tokens;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Text;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddAuthentication(options =>
{
    options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
    options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
    options.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
}).AddJwtBearer(o =>
{
    o.TokenValidationParameters = new TokenValidationParameters
    {
        ValidIssuer = builder.Configuration["Jwt:Issuer"],
        ValidAudience = builder.Configuration["Jwt:Audience"],
        IssuerSigningKey = new SymmetricSecurityKey
            (Encoding.UTF8.GetBytes(builder.Configuration["Jwt:Key"])),
        ValidateIssuer = true,
        ValidateAudience = true,
        ValidateLifetime = false,
        ValidateIssuerSigningKey = true
    };
});
builder.Services.AddAuthorization();
var app = builder.Build();
app.UseHttpsRedirection();
app.MapGet("/security/getMessage", () => "Hello World!").RequireAuthorization();
app.MapPost("/security/createToken",
[AllowAnonymous] (User user) =>
{
    if (user.UserName == "joydip" && user.Password == "joydip123")
    {
        var issuer = builder.Configuration["Jwt:Issuer"];
        var audience = builder.Configuration["Jwt:Audience"];
        var key = Encoding.ASCII.GetBytes
        (builder.Configuration["Jwt:Key"]);
        var tokenDescriptor = new SecurityTokenDescriptor
        {
            Subject = new ClaimsIdentity(new[]
            {
                new Claim("Id", Guid.NewGuid().ToString()),
                new Claim(JwtRegisteredClaimNames.Sub, user.UserName),
                new Claim(JwtRegisteredClaimNames.Email, user.UserName),
                new Claim(JwtRegisteredClaimNames.Jti,
                Guid.NewGuid().ToString())
             }),
            Expires = DateTime.UtcNow.AddMinutes(5),
            Issuer = issuer,
            Audience = audience,
            SigningCredentials = new SigningCredentials
            (new SymmetricSecurityKey(key),
            SecurityAlgorithms.HmacSha512Signature)
        };
        var tokenHandler = new JwtSecurityTokenHandler();
        var token = tokenHandler.CreateToken(tokenDescriptor);
        var jwtToken = tokenHandler.WriteToken(token);
        var stringToken = tokenHandler.WriteToken(token);
        return Results.Ok(stringToken);
    }
    return Results.Unauthorized();
});
app.UseAuthentication();
app.UseAuthorization();
app.Run();

JWT-аутентификация в действии

Когда вы отправите учетные данные пользователя в конечную точку createToken с помощью Postman, вы сможете увидеть сгенерированный токен.

безопасный минимальный API 02 ИДГ

Рисунок 2. JWT успешно сгенерирован.

Обратите внимание, что мы передали учетные данные пользователя, т. е. имя пользователя и пароль, в теле запроса.

Теперь вызовите конечную точку HTTP Get, которую мы создали ранее, и передайте сгенерированный токен в качестве токена-носителя в заголовке запроса. Если сгенерированный токен действителен, вы увидите сообщение, показанное на рисунке 3.

безопасный минимальный API 03 ИДГ

Рисунок 3. Конечная точка HTTP Get возвращает текстовое сообщение в ответе.

Как вы можете видеть на рисунке 3, текстовое сообщение «Hello World!» отображается, поскольку переданный нами токен действителен. Обратите также внимание на ответ HTTP 200 OK (выделен зеленым прямоугольником).

В этом примере мы жестко запрограммировали имя пользователя и пароль, чтобы упростить задачу. Конечно, никогда не следует жестко запрограммировать учетные данные пользователя в производственной среде. Хорошим выбором является использование ASP.NET Core Identity для управления учетными записями пользователей.

Чтобы протестировать нашу минимальную реализацию API, мы использовали Postman, один из самых популярных инструментов, доступных сегодня для тестирования API. Чтобы протестировать минимальные конечные точки API, вы также можете использовать Swagger, набор инструментов, который упрощает предоставление графического представления вашего API.

Дальше читайте это:

  • Лучшее программное обеспечение с открытым исходным кодом 2023 года
  • Сертификаты программирования все еще имеют значение?
  • Облачные вычисления больше не являются пустяком
  • Что такое генеративный ИИ? Искусственный интеллект, который создает
  • Программирование с помощью ИИ: советы и лучшие практики от разработчиков
  • Почему Wasm — это будущее облачных вычислений

Related Posts

Оставить комментарий