how to generate a unique token which expires after 24 hours?

前端 未结 5 424
既然无缘
既然无缘 2020-11-28 17:30

I have a WCF Webservice which checks if the user is valid.

If the user is valid I want to generate a token which expires after 24 hours.

public bool         


        
相关标签:
5条回答
  • 2020-11-28 18:07

    I like Guffa's answer and since I can't comment I will provide the answer Udil's question here.

    I needed something similar but I wanted certein logic in my token, I wanted to:

    1. See the expiration of a token
    2. Use a guid to mask validate (global application guid or user guid)
    3. See if the token was provided for the purpose I created it (no reuse..)
    4. See if the user I send the token to is the user that I am validating it for

    Now points 1-3 are fixed length so it was easy, here is my code:

    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(2).ToArray();
        byte[] _Id       = data.Skip(26).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;
    }
    
    private static string GetString(byte[] reason) => Encoding.ASCII.GetString(reason);
    
    private static byte[] GetBytes(string reason) => Encoding.ASCII.GetBytes(reason);
    

    The TokenValidation class looks like this:

    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:

    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

    0 讨论(0)
  • 2020-11-28 18:12

    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.

    0 讨论(0)
  • 2020-11-28 18:13

    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.

    To generate key refer here

    0 讨论(0)
  • 2020-11-28 18:14

    This way a token will exist up-to 24 hours. here is the code to generate token which will valid up-to 24 Hours. this code we use but i did not compose it.

    public static string GenerateToken()
    {
        int month = DateTime.Now.Month;
        int day = DateTime.Now.Day;
        string token = ((day * 100 + month) * 700 + day * 13).ToString();
        return token;
    }
    
    0 讨论(0)
  • 2020-11-28 18:20

    Use Dictionary<string, DateTime> to store token with timestamp:

    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:

     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.

    0 讨论(0)
提交回复
热议问题