Я использую в своем проекте jwt токены. Код для создания ClaimsIdentity
public async Task
var claims = new List
Код для создания токена:
public async Task
var now = DateTime.UtcNow;
var jwt = new JwtSecurityToken(
issuer: AuthOptions.ISSUER,
audience: AuthOptions.AUDIENCE,
notBefore: now,
claims: identity.Claims,
expires: now.Add(TimeSpan.FromMinutes(AuthOptions.LIFETIME)),
signingCredentials: new SigningCredentials(AuthOptions.GetSymmetricSecurityKey(), SecurityAlgorithms.HmacSha256));
return new JwtSecurityTokenHandler().WriteToken(jwt);
}
Код в классе Startup
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options =>
{
options.RequireHttpsMetadata = false;
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = true,
ValidIssuer = AuthOptions.ISSUER,
ValidateAudience = true,
ValidAudience = AuthOptions.AUDIENCE,
ValidateLifetime = true,
IssuerSigningKey = AuthOptions.GetSymmetricSecurityKey(),
ValidateIssuerSigningKey = true,
};
});
Код был взят из этой статьи: https://metanit.com/sharp/aspnet5/23.7.php
Получив строку токена можно авторизоваться с любого браузера или устройства.
Необходимо сделать так, чтобы токеном мог воспользоваться только тот, кому он был предназначен.
Думаю сделать это таким образом: на серверной стороне генерируется уникальный ключ. Этот ключ потом отправляется вместе с jwt токеном в каждом запросе, что позволяет определить какое устройство получает доступ. Но этот ключ тоже могут украсть, как и сам токен :(
Также необходимо сделать так, чтобы если параметры пользователя изменились, ранее выданные токены стали невалидными. Ничего лучше, чем черный список токенов в бд я для этого не придумал. Должны же быть какие-то стандартные методы для сброса токенов.
Ответ
Получив строку токена, можно авторизоваться с любого браузера или устройства.
Да, эта проблема, которую токены аутентификации не решают. Поэтому важно реализовать следующие возможности:
Использовать HTTPS вместо HTTP, это защитит от перехвата токенов.
При сохранении токенов, использовать средства ОС, которые прячут их от других программ/пользователей. Например, в Андроид-приложениях задействуйте Internal Storage с флагом MODE_PRIVATE
Делайте время жизни токена небольшим, например, 20 или 30 минут. Плюсом токена, относительно логина и пароля является время жизни: даже если его удалось перехватить, обычно это случается слишком поздно.
Ничего лучше, чем черный список токенов в бд я для этого не придумал. Должны же быть какие-то стандартные методы для сброса токенов.
Для того, чтобы токены можно было отключать, например, реализовывать логаут, их действительно придётся хранить. Мы для этого используем не БД, а кэш Redis, где указываем время хранения. И работает быстрее, и старые записи чистить не надо.
Порядок действий:
При создании токена генерируете уникальный идентификатор токена "jti" (JWT ID). Создаёте токен доступа (access token).
В Redis (ну или в БД) заносите access_token с ключом, основанном на "jti", указав время жизни токена доступа (30 минут).
При аутентификации проверяете подпись JWT и время жизни, хранящееся в самом JWT. Если всё нормально, извлекаете значение "jti" и проверяете, есть ли такой ключ в Redis. Если есть, всё нормально, если нет, значит, был логаут, или пользователя выкинул администратор. В любом случае считаем, что пользователь аутентификацию не прошёл.
Такое решение позволяет реализовать логаут и бан и является достаточно безопасным.
Из-за короткого времени жизни токена доступа, необходимо реализовать перелогин через токен обновления. Этот токен тоже нужно хранить в Redis, только время жизни задавать больше (вполне нормальным считается даже месяц, но вы смотрите, что подходит к вашей предметной области).
При логауте/бане токен обновления также нужно удалять, как и токен доступа.
Дальше идут детали, которые вам придётся учитывать. Если нужен бан, значит, администратору каким-то образом нужно уметь узнавать "jti" по идентификатору пользователя. Можно например "jti" делать равным "sub" (то есть идентификатору пользователя). В этом случае пользователь не сможет одновременно работать с разных устройств, потому что у него может быть только один токен доступа. Если нужно, чтобы мог работать, можно например "jti" формировать в виде $"{sub}:{random}". Тут возможны разные решения в зависимости от предметной области, но в целом ничего сложного нет.
Комментариев нет:
Отправить комментарий