4. JWT, роли и доступ¶
JWT-токен подтверждает, что пользователь прошёл вход. Роль внутри токена позволяет защищать эндпоинты.
Без авторизации API доверяет любому клиенту. Это опасно: любой человек мог бы удалить события, выгрузить отчёт или создать фальшивые данные. Поэтому сначала пользователь проходит вход, а затем доказывает свою личность с помощью токена.
JWT удобно использовать в Web API, потому что серверу не нужно хранить сессию в памяти. Клиент отправляет токен в каждом запросе, а сервер проверяет подпись и срок действия.
Типовой поток такой:
- Пользователь отправляет логин и пароль на
/api/auth/login. - Сервер проверяет данные в базе.
- Сервер создаёт JWT с идентификатором пользователя и ролью.
- Клиент сохраняет токен.
- В следующих запросах клиент отправляет
Authorization: Bearer <token>. - ASP.NET Core проверяет токен до входа в контроллер.
Пакет¶
JWT-проверка в ASP.NET Core подключается отдельным пакетом. Он добавляет middleware и настройки, которые позволяют серверу читать заголовок Authorization: Bearer ... и проверять подпись токена.
Настройки¶
Параметры JWT выносятся в конфигурацию, чтобы не зашивать их прямо в код. Issuer и Audience помогают проверить, кто выпустил токен и для какого клиента он предназначен, а Key используется для подписи.
appsettings.json:
{
"Jwt": {
"Issuer": "ActivityMonitoring",
"Audience": "ActivityMonitoringClient",
"Key": "change-this-key-to-long-production-secret"
}
}
Генерация токена¶
Токен содержит claims — утверждения о пользователе. В практике используются три claim: идентификатор, имя пользователя и роль. Пароль, секретные данные и персональные данные в JWT хранить нельзя: токен может быть прочитан клиентом.
public class JwtTokenFactory
{
private readonly IConfiguration _configuration;
public JwtTokenFactory(IConfiguration configuration)
{
_configuration = configuration;
}
public string Create(AppUser user)
{
var claims = new[]
{
new Claim(ClaimTypes.NameIdentifier, user.Id.ToString()),
new Claim(ClaimTypes.Name, user.UserName),
new Claim(ClaimTypes.Role, user.Role.Name)
};
var key = new SymmetricSecurityKey(
Encoding.UTF8.GetBytes(_configuration["Jwt:Key"]!));
var credentials = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
var token = new JwtSecurityToken(
issuer: _configuration["Jwt:Issuer"],
audience: _configuration["Jwt:Audience"],
claims: claims,
expires: DateTime.UtcNow.AddHours(4),
signingCredentials: credentials);
return new JwtSecurityTokenHandler().WriteToken(token);
}
}
Подключение JWT¶
После генерации токенов нужно научить API их проверять. Этот блок регистрирует схему Bearer-аутентификации и задаёт правила валидации для каждого входящего токена.
builder.Services
.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options =>
{
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = true,
ValidateAudience = true,
ValidateLifetime = true,
ValidateIssuerSigningKey = true,
ValidIssuer = builder.Configuration["Jwt:Issuer"],
ValidAudience = builder.Configuration["Jwt:Audience"],
IssuerSigningKey = new SymmetricSecurityKey(
Encoding.UTF8.GetBytes(builder.Configuration["Jwt:Key"]!))
};
});
builder.Services.AddAuthorization();
В конвейере эти middleware должны выполняться до маршрутов контроллеров. Сначала приложение устанавливает личность пользователя по токену, затем проверяет, имеет ли он право вызвать конкретный endpoint.
Защита эндпоинтов¶
Атрибут [Authorize] говорит: “сюда можно только авторизованным пользователям”. Если указать роли, ASP.NET Core дополнительно проверит claim ClaimTypes.Role.
[Authorize]
[HttpGet]
public async Task<ActionResult<IReadOnlyList<ActivityEventDto>>> Get(...)
{
...
}
[Authorize(Roles = "Admin")]
[HttpDelete("{id:int}")]
public async Task<IActionResult> Delete(int id, CancellationToken ct)
{
...
}
[Authorize(Roles = "Admin,Analyst")]
[HttpGet("/api/analytics/summary")]
public async Task<ActionResult<AnalyticsSummaryDto>> Summary(CancellationToken ct)
{
...
}
Роли практики¶
| Роль | Возможности |
|---|---|
Admin |
все операции, удаление событий, отчёты, управление данными |
Analyst |
просмотр событий, аналитика, отчёты |
Operator |
создание и просмотр событий без удаления и отчётов |
Частые ошибки авторизации¶
| Ошибка | Причина | Что проверить |
|---|---|---|
401 Unauthorized |
Токен отсутствует, истёк или неверно подписан | Заголовок Authorization, Jwt:Key, срок действия |
403 Forbidden |
Пользователь вошёл, но роль не подходит | Claim роли и атрибут [Authorize(Roles = "...")] |
| Swagger не отправляет токен | Не настроена схема Bearer в Swagger | Добавить настройку безопасности Swagger |
| Все токены стали недействительными | Изменился Jwt:Key |
Войти заново и получить новый токен |