C# 如何生成一个在 24 小时后过期的唯一令牌?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/14643735/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
how to generate a unique token which expires after 24 hours?
提问by Eray Geveci
I have a WCF Webservice which checks if the user is valid.
我有一个 WCF Web 服务,用于检查用户是否有效。
If the user is valid I want to generate a token which expires after 24 hours.
如果用户有效,我想生成一个 24 小时后过期的令牌。
public bool authenticateUserManual(string userName, string password,string language,string token)
{
if (Membership.ValidateUser(userName,password))
{
//////////
string token = ????
//////////
return true;
}
else
{
return false;
}
}
采纳答案by Guffa
There are two possible approaches; either you create a unique value and store somewhere along with the creation time, for example in a database, or you put the creation time inside the token so that you can decode it later and see when it was created.
有两种可能的方法;您可以创建一个唯一值并与创建时间一起存储在某个地方,例如在数据库中,或者将创建时间放在令牌中,以便稍后对其进行解码并查看它的创建时间。
To create a unique token:
要创建唯一令牌:
string token = Convert.ToBase64String(Guid.NewGuid().ToByteArray());
Basic example of creating a unique token containing a time stamp:
创建包含时间戳的唯一令牌的基本示例:
byte[] time = BitConverter.GetBytes(DateTime.UtcNow.ToBinary());
byte[] key = Guid.NewGuid().ToByteArray();
string token = Convert.ToBase64String(time.Concat(key).ToArray());
To decode the token to get the creation time:
解码令牌以获取创建时间:
byte[] data = Convert.FromBase64String(token);
DateTime when = DateTime.FromBinary(BitConverter.ToInt64(data, 0));
if (when < DateTime.UtcNow.AddHours(-24)) {
// too old
}
Note: If you need the token with the time stamp to be secure, you need to encrypt it. Otherwise a user could figure out what it contains and create a false token.
注意:如果您需要带时间戳的令牌安全,则需要对其进行加密。否则,用户可以弄清楚它包含什么并创建一个错误的令牌。
回答by Lingasamy Sakthivel
you need to store the token while creating for 1st registration. When you retrieve data from login table you need to differentiate entered date with current date if it is more than 1 day (24 hours) you need to display message like your token is expired.
您需要在为第一次注册创建时存储令牌。当您从登录表中检索数据时,您需要将输入的日期与当前日期区分开来,如果它超过 1 天(24 小时),您需要显示消息,例如您的令牌已过期。
To generate key refer here
要生成密钥,请参阅此处
回答by cuongle
Use Dictionary<string, DateTime>
to store token with timestamp:
使用Dictionary<string, DateTime>
存储所令牌时间戳:
static Dictionary<string, DateTime> dic = new Dictionary<string, DateTime>();
Add token with timestamp whenever you create new token:
每当您创建新令牌时添加带有时间戳的令牌:
dic.Add("yourToken", DateTime.Now);
There is a timer running to remove any expired tokens out of dic:
有一个计时器正在运行以从 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);
}
So, when you authenticate token, just check whether token exists in dic or not.
因此,当您验证令牌时,只需检查 dic 中是否存在令牌。
回答by Walter Verhoeven
I like Guffa's answer and since I can't comment I will provide the answer Udil's question here.
我喜欢 Guffa 的回答,因为我无法发表评论,所以我将在这里提供 Udil 问题的答案。
I needed something similar but I wanted certein logic in my token, I wanted to:
我需要类似的东西,但我想在我的令牌中使用某些逻辑,我想:
- See the expiration of a token
- Use a guid to mask validate (global application guid or user guid)
- See if the token was provided for the purpose I created it (no reuse..)
- See if the user I send the token to is the user that I am validating it for
- 查看令牌的到期时间
- 使用 guid 屏蔽验证(全局应用程序 guid 或用户 guid)
- 查看令牌是否是为了我创建它的目的而提供的(不可重复使用..)
- 查看我向其发送令牌的用户是否是我为其验证的用户
Now points 1-3 are fixed length so it was easy, here is my code:
现在点 1-3 是固定长度所以很容易,这是我的代码:
Here is my code to generate the token:
这是我生成令牌的代码:
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());
}
Here is my Code to take the generated token string and validate it:
这是我的代码,用于获取生成的令牌字符串并对其进行验证:
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;
}
The TokenValidation class looks like this:
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
}
Now I have an easy way to validate a token, no Need to Keep it in a list for 24 hours or so. Here is my Good-Case Unit test:
现在我有一种简单的方法来验证令牌,无需将其保留在列表中 24 小时左右。这是我的 Good-Case 单元测试:
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");
}
One can adapt the code for other business cases easely.
可以轻松地为其他业务案例调整代码。
Happy Coding
快乐编码
Walter
沃尔特