web-dev-qa-db-fra.com

Comment générer un jeton unique qui expire après 24 heures?

J'ai un service Web WCF qui vérifie si l'utilisateur est valide.

Si l'utilisateur est valide, je souhaite générer un jeton qui expire après 24 heures.

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

        return true;
    }
    else 
    {
        return false;
    }
}   
43
Eray Geveci

Il y a deux approches possibles; soit vous créez une valeur unique et enregistrez quelque part avec l'heure de création, par exemple dans une base de données, ou vous insérez l'heure de création dans le jeton afin de pouvoir le décoder ultérieurement et voir à quel moment il a été créé.

Pour créer un jeton unique:

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

Exemple de base pour créer un jeton unique contenant un horodatage:

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

Pour décoder le jeton pour obtenir l'heure de création:

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

Remarque: Si vous souhaitez que le jeton avec l'horodatage soit sécurisé, vous devez le chiffrer. Sinon, un utilisateur pourrait comprendre ce qu'il contient et créer un faux jeton.

137
Guffa

J'aime la réponse de Guffa et comme je ne peux pas me prononcer, je vais répondre ici à la question d'Udil.

J'avais besoin de quelque chose de similaire mais je voulais une logique logique dans mon jeton, je voulais:

  1. Voir l'expiration d'un jeton
  2. Utiliser un guid pour masquer valider (global application application ou user guid)
  3. Voir si le jeton a été fourni dans le but que je l'ai créé (pas de réutilisation ..)
  4. Voir si l'utilisateur auquel je envoie le jeton est l'utilisateur pour lequel je le valide

Maintenant, les points 1 à 3 sont de longueur fixe, donc c'était facile, voici mon code:

Voici mon code pour générer le jeton:

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

Voici mon code pour prendre la chaîne de jeton générée et la valider:

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

La classe TokenValidation ressemble à ceci:

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
}

Maintenant, j'ai un moyen facile de valider un jeton, pas besoin de le conserver dans une liste pendant environ 24 heures. Voici mon test d'unité de cas:

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

On peut facilement adapter le code aux autres cas.

Codage heureux

Walter

28
Walter Verhoeven

Utilisation Dictionary<string, DateTime> pour stocker le jeton avec l’horodatage:

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

Ajouter un jeton avec horodatage chaque fois que vous créez un nouveau jeton:

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

Une minuterie est en cours d'exécution pour supprimer tous les jetons expirés de 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);
    }

Ainsi, lorsque vous authentifiez un jeton, vérifiez simplement si le jeton existe ou non dans le code.

3
cuongle

vous devez stocker le jeton lors de la création pour la première inscription. Lorsque vous récupérez des données de la table de connexion, vous devez différencier la date saisie de la date du jour si celle-ci est supérieure à 1 jour (24 heures). Vous devez afficher un message indiquant que votre jeton a expiré.

Pour générer la clé, référez-vous ici

0
Lingasamy Sakthivel