jak wygenerować unikalny token, który wygasa po 24 godzinach?

Mam WCF Webservice, który sprawdza, czy użytkownik jest poprawny.

Jeśli użytkownik jest ważny chcę wygenerować token, który wygasa po 24 godzinach.

public bool authenticateUserManual(string userName, string password,string language,string token)
{
    if (Membership.ValidateUser(userName,password))
    {
        //////////
        string token = ???? 
        //////////

        return true;
    }
    else 
    {
        return false;
    }
}   
Author: Tom, 2013-02-01

4 answers

Istnieją dwa możliwe podejścia: albo tworzysz unikalną wartość i przechowujesz gdzieś razem z czasem tworzenia, na przykład w bazie danych, albo umieszczasz czas tworzenia wewnątrz tokenu, aby móc go później odkodować i zobaczyć, kiedy został utworzony.

Aby utworzyć unikalny token:

string token = Convert.ToBase64String(Guid.NewGuid().ToByteArray());

Podstawowy przykład tworzenia unikalnego tokena zawierającego znacznik czasu:

byte[] time = BitConverter.GetBytes(DateTime.UtcNow.ToBinary());
byte[] key = Guid.NewGuid().ToByteArray();
string token = Convert.ToBase64String(time.Concat(key).ToArray());

Aby odszyfrować token, aby uzyskać czas utworzenia:

byte[] data = Convert.FromBase64String(token);
DateTime when = DateTime.FromBinary(BitConverter.ToInt64(data, 0));
if (when < DateTime.UtcNow.AddHours(-24)) {
  // too old
}

Uwaga: jeśli potrzebujesz tokena z znacznik czasu aby być bezpiecznym, musisz go zaszyfrować. W przeciwnym razie użytkownik może dowiedzieć się, co zawiera i utworzyć fałszywy token.

 127
Author: Guffa,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-05-21 18:20:20

Podoba mi się odpowiedź Guffa, a ponieważ nie mogę skomentować, podam tutaj odpowiedź.

Potrzebowałem czegoś podobnego, ale chciałem mieć w tokenie certein logic, chciałem:

  1. Zobacz wygaśnięcie tokena
  2. W tym celu należy użyć guid do zamaskowania walidacji (global application guid lub user guid)
  3. sprawdź, czy token został dostarczony w celu, w którym go stworzyłem (nie ma ponownego użycia..)
  4. sprawdź, czy użytkownik, do którego wysyłam token, jest użytkownikiem, któremu go sprawdzam dla

Teraz punkty 1-3 są stałą długością, więc było łatwo, Oto Mój kod:

Oto Mój kod do wygenerowania tokena:

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());
}

Oto Mój kod do pobrania wygenerowanego ciągu tokena i jego walidacji:

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;
}

Klasa TokenValidation wygląda tak:

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
}

Teraz mam łatwy sposób, aby zweryfikować token, nie trzeba trzymać go na liście przez 24 godziny lub więcej. Oto mój dobry test jednostkowy:

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");
}

Można dostosować kod do innych przypadków biznesowych spokojnie.

Happy Coding

Walter

 25
Author: Walter Verhoeven,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-09-06 08:41:29

Użyj Dictionary<string, DateTime> do przechowywania tokena ze znacznikiem czasu:

static Dictionary<string, DateTime> dic = new Dictionary<string, DateTime>();

Dodaj token ze znacznikiem czasu za każdym razem, gdy tworzysz nowy token:

dic.Add("yourToken", DateTime.Now);

Jest uruchomiony timer, aby usunąć wszelkie przeterminowane tokeny z 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);
    }

Więc, kiedy uwierzytelniasz token, po prostu sprawdź, czy token istnieje w dic, czy nie.

 2
Author: Cuong Le,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-02-01 10:20:02

Musisz zapisać token podczas tworzenia dla pierwszej rejestracji. Podczas pobierania danych z tabeli logowania musisz odróżnić wprowadzoną datę od bieżącej, jeśli jest ona dłuższa niż 1 dzień (24 godziny), musisz wyświetlić komunikat, jakby twój token wygasł.

Aby wygenerować klucz zapoznaj się z tutaj

 0
Author: Lingasamy Sakthivel,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-02-01 10:11:37