Как создать уникальный токен, срок действия которого истекает через 24 часа?
У меня есть веб-служба WCF, которая проверяет, действителен ли пользователь.
Если пользователь действителен, я хочу сгенерировать токен, срок действия которого истекает через 24 часа.
public bool authenticateUserManual(string userName, string password,string language,string token)
{
if (Membership.ValidateUser(userName,password))
{
//////////
string token = ????
//////////
return true;
}
else
{
return false;
}
}
Ответы
Ответ 1
Существует два возможных подхода; либо вы создаете уникальное значение и храните где-то вместе со временем создания, например, в базе данных, или вы помещаете время создания внутри токена, чтобы впоследствии его расшифровать и посмотреть, когда он был создан.
Чтобы создать уникальный токен:
string token = Convert.ToBase64String(Guid.NewGuid().ToByteArray());
Основной пример создания уникального токена, содержащего отметку времени:
byte[] time = BitConverter.GetBytes(DateTime.UtcNow.ToBinary());
byte[] key = Guid.NewGuid().ToByteArray();
string token = Convert.ToBase64String(time.Concat(key).ToArray());
Чтобы декодировать токен, чтобы получить время создания:
byte[] data = Convert.FromBase64String(token);
DateTime when = DateTime.FromBinary(BitConverter.ToInt64(data, 0));
if (when < DateTime.UtcNow.AddHours(-24)) {
// too old
}
Примечание. Если вам нужен токен с меткой времени для обеспечения безопасности, вам необходимо зашифровать его. В противном случае пользователь может выяснить, что он содержит, и создать ложный токен.
Ответ 2
Мне нравится ответ Гуффа, и, поскольку я не могу комментировать, я предоставлю ответ на вопрос Udil.
Мне нужно что-то подобное, но я хотел, чтобы логика certein в моем токене, я хотел:
- См. окончание токена
- Используйте руководство для маскировки validate (глобальное руководство для приложений или пользовательское руководство).
- Посмотрите, был ли предоставлен токен для цели, которую я создал (без повторного использования..)
- Посмотрите, является ли пользователь, которому я посылаю токен, пользователю, которого я проверяю для
Теперь точки 1-3 фиксированной длины, так что это было легко, вот мой код:
Вот мой код для генерации токена:
public string GenerateToken(string reason, MyUser user)
{
byte[] _time = BitConverter.GetBytes(DateTime.UtcNow.ToBinary());
byte[] _key = Guid.Parse(user.SecurityStamp).ToByteArray();
byte[] _Id = GetBytes(user.Id.ToString());
byte[] _reason = GetBytes(reason);
byte[] data = new byte[_time.Length + _key.Length + _reason.Length+_Id.Length];
System.Buffer.BlockCopy(_time, 0, data, 0, _time.Length);
System.Buffer.BlockCopy(_key , 0, data, _time.Length, _key.Length);
System.Buffer.BlockCopy(_reason, 0, data, _time.Length + _key.Length, _reason.Length);
System.Buffer.BlockCopy(_Id, 0, data, _time.Length + _key.Length+ _reason.Length, _Id.Length);
return Convert.ToBase64String(data.ToArray());
}
Вот мой код, чтобы взять сгенерированную строку токена и проверить его:
public TokenValidation ValidateToken(string reason, MyUser user, string token)
{
var result = new TokenValidation();
byte[] data = Convert.FromBase64String(token);
byte[] _time = data.Take(8).ToArray();
byte[] _key = data.Skip(8).Take(16).ToArray();
byte[] _reason = data.Skip(24).Take(4).ToArray();
byte[] _Id = data.Skip(28).ToArray();
DateTime when = DateTime.FromBinary(BitConverter.ToInt64(_time, 0));
if (when < DateTime.UtcNow.AddHours(-24))
{
result.Errors.Add( TokenValidationStatus.Expired);
}
Guid gKey = new Guid(_key);
if (gKey.ToString() != user.SecurityStamp)
{
result.Errors.Add(TokenValidationStatus.WrongGuid);
}
if (reason != GetString(_reason))
{
result.Errors.Add(TokenValidationStatus.WrongPurpose);
}
if (user.Id.ToString() != GetString(_Id))
{
result.Errors.Add(TokenValidationStatus.WrongUser);
}
return result;
}
Класс TokenValidation выглядит следующим образом:
public class TokenValidation
{
public bool Validated { get { return Errors.Count == 0; } }
public readonly List<TokenValidationStatus> Errors = new List<TokenValidationStatus>();
}
public enum TokenValidationStatus
{
Expired,
WrongUser,
WrongPurpose,
WrongGuid
}
Теперь у меня есть простой способ проверить токен, нет необходимости хранить его в списке в течение 24 часов или около того.
Вот мой пример хорошего Unit test:
private const string ResetPasswordTokenPurpose = "RP";
private const string ConfirmEmailTokenPurpose = "EC";//change here change bit length for reason section (2 per char)
[TestMethod]
public void GenerateTokenTest()
{
MyUser user = CreateTestUser("name");
user.Id = 123;
user.SecurityStamp = Guid.NewGuid().ToString();
var token = sit.GenerateToken(ConfirmEmailTokenPurpose, user);
var validation = sit.ValidateToken(ConfirmEmailTokenPurpose, user, token);
Assert.IsTrue(validation.Validated,"Token validated for user 123");
}
Можно легко адаптировать код для других бизнес-кейсов.
Счастливое кодирование
Вальтер
Ответ 3
Используйте Dictionary<string, DateTime>
для хранения токена с меткой времени:
static Dictionary<string, DateTime> dic = new Dictionary<string, DateTime>();
Добавить маркер с меткой времени при создании нового токена:
dic.Add("yourToken", DateTime.Now);
Существует таймер для удаления любых истекших токенов из dic:
timer = new Timer(1000*60); //assume run in 1 minute
timer.Elapsed += timer_Elapsed;
static void timer_Elapsed(object sender, ElapsedEventArgs e)
{
var expiredTokens = dic.Where(p => p.Value.AddDays(1) <= DateTime.Now)
.Select(p => p.Key);
foreach (var key in expiredTokens)
dic.Remove(key);
}
Итак, когда вы аутентифицируете токен, просто проверьте, существует ли токен в dic или нет.
Ответ 4
вам нужно сохранить токен при создании для первой регистрации. Когда вы извлекаете данные из таблицы входа в систему, вам нужно различать введенную дату с текущей датой, если вам больше 1 дня (24 часа), вам нужно отобразить сообщение, так как ваш токен истек.
Чтобы сгенерировать ключ, обратитесь здесь