如何生成一个24小时后到期的唯一令牌?

Era*_*eci 46 c# asp.net authentication wcf token

我有一个WCF Web服务,它检查用户是否有效.

如果用户有效,我想生成一个在24小时后过期的令牌.

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

        return true;
    }
    else 
    {
        return false;
    }
}   
Run Code Online (Sandbox Code Playgroud)

Guf*_*ffa 139

有两种可能的方法; 您可以创建一个唯一值并将其与创建时间一起存储在某个位置,例如在数据库中,或者将创建时间放在令牌中,以便以后可以对其进行解码并查看它何时创建.

要创建唯一标记:

string token = Convert.ToBase64String(Guid.NewGuid().ToByteArray());
Run Code Online (Sandbox Code Playgroud)

创建包含时间戳的唯一标记的基本示例:

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

要解码令牌以获取创建时间:

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

注意:如果您需要带有时间戳的令牌是安全的,则需要对其进行加密.否则,用户可以找出它包含的内容并创建一个假令牌.

  • @UdiI:是的,令牌包含时间戳.你的意思是你想获得GUID密钥吗?然后你将使用`new GUID(data.Skip(8).ToArray())`. (6认同)
  • 使用此时间解决方案和此加密http://msdn.microsoft.com/en-us/library/system.security.cryptography.rijndael.aspx,您可以创建一个不错的安全令牌. (2认同)
  • @Guffa在令牌中使用guid是否安全 (2认同)

Wal*_*ven 29

我喜欢Guffa的回答,因为我不能发表评论,我会在这里提供答案Udil的问题.

我需要类似的东西,但我想在我的令牌中使用certein逻辑,我想:

  1. 查看令牌的到期日期
  2. 使用guid来屏蔽验证(全局应用程序guid或用户指南)
  3. 查看令牌是否是为了我创建的目的而提供的(不重用..)
  4. 查看我发送令牌的用户是否是我正在验证它的用户

现在点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());
}
Run Code Online (Sandbox Code Playgroud)

这是我的代码,用于获取生成的令牌字符串并对其进行验证:

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;
}
Run Code Online (Sandbox Code Playgroud)

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
}
Run Code Online (Sandbox Code Playgroud)

现在我有一种简单的方法来验证令牌,无需将其保留在列表中24小时左右.这是我的Good-Case Unit测试:

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");
}
Run Code Online (Sandbox Code Playgroud)

可以轻松地为其他业务案例调整代码.

快乐的编码

沃尔特

  • 我有一种感觉,这个例子完全省略了任何安全层。似乎没有加密签名允许令牌通过不安全的通道传输并确保不被篡改。就我现在看来,任何看到这个令牌的人都可以创建它的有效变体,这些变体将通过验证。**注意,这个答案似乎不安全!** (3认同)