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