JWT‐токены - gro1vy/DeliverServiceAPI GitHub Wiki

В целях безопасности для защиты пользовательских данных в аккаунте в проекте используется механизм аутентификации на основе JWT токена, благодаря которому можно легко и просто организовать проверку на права доступа к методам контроллеров приложения asp.net. Именно из-за простоты использования был выбран такой механизм.

Что такое JWT-токен?

JWT (JSON Web Token) - это структурированный формат для представления информации между двумя сторонами в виде объекта JSON. JWT часто используется для аутентификации и авторизации в веб-приложениях и API. Он представляет собой компактный и самодостаточный способ передачи информации между сторонами в зашифрованной форме.

JWT состоит из трех частей, разделенных точками:

  1. Заголовок (Header): В заголовке указывается тип токена и алгоритм, используемый для подписи токена. Заголовок обычно выглядит как:
json
{ 
   "alg": "HS256", 
   "typ": "JWT" 
}
  1. Полезная нагрузка (Payload): Полезная нагрузка содержит информацию, которую вы хотите закодировать в токене. Это могут быть утверждения (claims) о пользователе, правах доступа и другие данные. Пример полезной нагрузки:
json
{ 
   "name": "John Doe", // имя пользователя
   "exp": 1516239022 // время истечения токена (в формате UNIX-времени)
}
  1. Подпись (Signature): Подпись создается путем объединения заголовка и полезной нагрузки и последующим хешированием с использованием секретного ключа. Это обеспечивает проверку подлинности токена. Пример подписи:
HMACSHA256(base64UrlEncode(header) + "." + base64UrlEncode(payload), secret)

Подробнее познакомится со структурой JWT токена поможет JWT Decoder.

Как подключить JWT-токены к проекту ASP.NET?

Первым делом необходимо установить необходимой пакет, который показан на картинке, в Nuget.

image

Далее в appsettings.json добавить секретный ключ:

"ApiSettings": {
  "Secret": "Any Secret Key"
}

После чего в Program.cs необходимо настроить аутентификации, в примере ниже сначал происходит настройка схемы аутентификации, после чего настройка самого JWT-токена. Поле RequireHttpsMetadata указывает должен ли использоваться HTTPS или нет, поле TokenValidationParameters имеет несколько внутренних полей для настройки валидации, некторые из них представлены в примере. ValidateIssuerSigningKey говорит, нужно ли проверять ключ подписи токена или нет, если нужно, то в IssuerSigningKey указывает секретный ключ, созданный ранее в appsettings.json, причем сначала он декодируется в битовое представление и передается в SymmetricSecurityKey. Соответственно ValidateIssuer говорит, стоит ли проверять издателя токена, и если да, то в ValidIssuer указываем издателя, с остальными параметрами TokenValidationParameters все работает так же.

var key = builder.Configuration.GetValue<string>("ApiSettings:Secret");
builder.Services.AddAuthentication(authOptions =>
{
    authOptions.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
    authOptions.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
}).AddJwtBearer(jwtOptions =>
    {
        jwtOptions.RequireHttpsMetadata = false; // Develop 
        jwtOptions.TokenValidationParameters = new TokenValidationParameters
        {
            ValidateIssuerSigningKey = true,
            IssuerSigningKey = new SymmetricSecurityKey(Encoding.ASCII.GetBytes(key)),
            ValidateIssuer = true,
            ValidIssuer = "HITs",
            ValidateAudience = false
        };
    });

Далее в Program.cs необходимо также добавить middleware для аутентификации:

app.UseAuthentication();

Для того, чтобы добавить в Swagger возможность работать с аутентификацией, необходимо добавить следующий код:

builder.Services.AddSwaggerGen(options =>
{
    options.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme
    {
        Description = "",
        Name = "Authorization",
        In = ParameterLocation.Header,
        Scheme = "Bearer"
    });
    options.AddSecurityRequirement(new OpenApiSecurityRequirement()
    {
        {
            new OpenApiSecurityScheme
            {
                Reference = new OpenApiReference
                {
                    Type = ReferenceType.SecurityScheme,
                    Id = "Bearer"
                },
                Scheme = "oauth2",
                Name = "Bearer",
                In = ParameterLocation.Header
            },
            new List<string> ()
        }
    });
});

После настройки проекта для JWT аутентификации, научимся создавать токены. Для этого нужно создать JwtSecurityTokenHandler, благодаря которому будет сгенерирован токен, и SecurityTokenDescriptor, в котором будет проходить описание токена. В Subject, который является объектом типа ClaimsIdentity, происходит перечисление утверждений (полезной информации), в примере созданы Claim для имя пользователя и его роли. В Expires указывается, сколько будет действителен токен, в SigningCredentials указаны секретный ключ из appsettings.json и алгоритм подписи в данном примере это SHA256, а в Issuer указывает издатель. Это лишь некоторые настройки SecurityTokenDescriptor. И в конце происходит генерация токен с помощью ранее созданного JwtSecurityTokenHandler.

 var tokenHandler = new JwtSecurityTokenHandler();
 var key = Encoding.ASCII.GetBytes(key);
 
 var tokenDescriptor = new SecurityTokenDescriptor()
 {
     Subject = new ClaimsIdentity(new Claim[]
     {
         new Claim(ClaimTypes.Name, user.UserName.ToString()),
         new Claim(ClaimTypes.Role, user.Role),
     }),
     Expires = DateTime.UtcNow.AddDays(7),
     SigningCredentials = new (new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha256Signature),
     Issuer = "HITs"
 };

 var token = tokenHandler.CreateToken(tokenDescriptor);

Теперь, если у нас есть контроллер и мы хотим защитить его методы авторизацией, то рядом с методом прописываем атрибут [Authorize], тогда если кто-то захочет обратиться к данному методу, то он должен отправить JWT-токен вместе с запросом, если токен действителен, то пользователь попадет в метод, иначе выдаст код 401. Также если мы хотим проверить роль пользователя, то в том же атрибуте должны указать проверку [Authorize(Roles = "AnyRoles")], если роль пользователя не та, то выдаст код 403.

[ApiController]
[Route("[controller]")]
public class WeatherForecastController : ControllerBase
{
    private WeatherForecastService _weatherForecastService;
    
    public WeatherForecastController(WeatherForecastService weather)
    {
        _weatherForecastService = weather;
    }

    [HttpGet]
    [Authorize]
    public ActionResult<IEnumerable<WeatherForecastDTO>> Get()
    {
        return _weatherForecastService.GenerateWeatherForecasts();
    }

    [HttpPost]
    [Authorize(Roles = "admin")]
    public async Task Post(WeatherForecastDTO model)
    {
        await _weatherForecastService.Add(model);
    }
}
⚠️ **GitHub.com Fallback** ⚠️