#c_sharp #net #aspnet #aspnet_core #jwt
Я использую в своем проекте jwt токены. Код для создания ClaimsIdentity: public async TaskGetClaimsIdentityAsync(User user) { var roles = await _roleService.GetUserRolesAsync(user); var claims = new List { new Claim(ClaimsIdentity.DefaultNameClaimType, user.Username), }; foreach (var role in roles) { claims.Add(new Claim(ClaimsIdentity.DefaultRoleClaimType, role.Name)); } ClaimsIdentity claimsIdentity = new ClaimsIdentity(claims, "Token", ClaimsIdentity.DefaultNameClaimType, ClaimsIdentity.DefaultRoleClaimType); return claimsIdentity; } Код для создания токена: public async Task GetJwtToken(User user) { var identity = await _sinInService.GetClaimsIdentityAsync(user); 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 токеном в каждом запросе, что позволяет определить какое устройство получает доступ. Но этот ключ тоже могут украсть, как и сам токен :( Также необходимо сделать так, чтобы если параметры пользователя изменились, ранее выданные токены стали невалидными. Ничего лучше, чем черный список токенов в бд я для этого не придумал. Должны же быть какие-то стандартные методы для сброса токенов.
Ответы
Ответ 1
Получив строку токена, можно авторизоваться с любого браузера или устройства. Да, эта проблема, которую токены аутентификации не решают. Поэтому важно реализовать следующие возможности: Использовать 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}". Тут возможны разные решения в зависимости от предметной области, но в целом ничего сложного нет.Ответ 2
Сервер знает о клиенте только то, что клиент прислал ему в http-запросе. По сути, это только: Client IP, который может меняться. UserAgent, который неуникален. Так что ответ - провалидировать привязку токена к девайсу не получится.
Комментариев нет:
Отправить комментарий