programing

24시간 후에 만료되는 고유 토큰을 생성하는 방법은 무엇입니까?

muds 2023. 8. 24. 22:32
반응형

24시간 후에 만료되는 고유 토큰을 생성하는 방법은 무엇입니까?

사용자가 유효한지 확인하는 WCF 웹 서비스가 있습니다.

사용자가 유효한 경우 24시간 후에 만료되는 토큰을 생성합니다.

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

        return true;
    }
    else 
    {
        return false;
    }
}   

고유한 값을 작성하고 작성 시간과 함께 데이터베이스에 저장하거나 나중에 디코딩하여 작성 시간을 확인할 수 있도록 토큰 내부에 작성 시간을 저장하는 두 가지 방법이 있습니다.

고유 토큰을 만드는 방법

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

타임스탬프가 포함된 고유 토큰을 만드는 기본 예:

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

토큰을 디코딩하여 생성 시간을 얻는 방법

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

참고: 보안을 위해 타임스탬프가 있는 토큰이 필요한 경우 암호화해야 합니다.그렇지 않으면 사용자가 포함된 내용을 파악하여 잘못된 토큰을 만들 수 있습니다.

저는 구파의 답변이 마음에 들고 코멘트할 수 없기 때문에 우딜의 질문에 대한 답변을 여기서 제공하겠습니다.

나는 비슷한 것이 필요했지만, 나는 내 토큰에 특정한 논리를 원했고, 나는 다음을 원했습니다.

  1. 토큰 만료 보기
  2. 마스크 검증을 위해 GUID 사용(글로벌 응용 프로그램 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());
}

생성된 토큰 문자열을 가져와서 유효성을 검사하는 내 코드는 다음과 같습니다.

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

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
}

이제 토큰을 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");
}

다른 비즈니스 사례에 맞게 코드를 쉽게 조정할 수 있습니다.

해피 코딩

월터.

사용하다Dictionary<string, DateTime>토큰을 타임스탬프와 함께 저장하는 방법:

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

새 토큰을 만들 때마다 타임스탬프가 있는 토큰 추가:

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

만료된 토큰을 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);
    }

따라서 토큰을 인증할 때는 토큰이 dic에 존재하는지 여부만 확인하면 됩니다.

첫 번째 등록을 위해 작성하는 동안 토큰을 저장해야 합니다.로그인 테이블에서 데이터를 검색할 때 입력한 날짜가 1일(24시간) 이상이면 현재 날짜로 구분해야 합니다. 토큰이 만료된 것처럼 메시지를 표시해야 합니다.

키를 생성하려면 여기를 참조하십시오.

저도 비슷하지만 약간 다른 문제가 있었습니다.저는 다른 서버에서 2분 이내에 사용할 수 있는 토큰이 필요했습니다.서버 A와 서버 B는 개인 키를 공유합니다.물론 사용자 브라우저에서는 토큰이 공개되어 있습니다.

enter image description here

A 웹 서버는 날짜가 포함된 문자열을 만듭니다.그런 다음 끈을 해싱합니다.브라우저의 사용자가 토큰을 사용합니다.서버 B가 토큰을 수신하면 동일한 해시를 수행하고 결과를 비교합니다.서버 A와 서버 B는 c# 코드를 실행합니다.

문제는 비슷하지만 같지는 않지만, 어쨌든 코드는 누군가를 도울 수 있습니다.

public class TokenHelper
{

    private static byte[] GetHash(string inputString)
    {
        using (HashAlgorithm algorithm = MD5.Create())
            return algorithm.ComputeHash(Encoding.UTF8.GetBytes(inputString));
    }

    private static string GetHashString(string inputString)
    {
        StringBuilder sb = new StringBuilder();
        foreach (byte b in GetHash(inputString))
            sb.Append(b.ToString("X2"));

        return sb.ToString();
    }

    public static Guid GetTokenForGuest(EnumWebsiteName website, string privateKey)
    {            
        string datetime = DateTime.Now.ToString("dd/MM/yyyy hh:mm");
        var hashString = GetHashString($"{datetime}|{website}|{privateKey}");            
        return new Guid(hashString);
    }

    public static bool  CheckTokenForGuest(Guid token, EnumWebsiteName website, string privateKey)
    {
        string datetime = DateTime.Now.ToString("dd/MM/yyyy hh:mm");
        var hashString = GetHashString($"{datetime}|{website}|{privateKey}");
        var test1 = new Guid(hashString);
        if (test1.CompareTo(token)==0) {
            return true;
        }

        string datetime2 = DateTime.Now.AddMinutes(-1).ToString("dd/MM/yyyy hh:mm");
        var hashString2 = GetHashString($"{datetime2}|{website}|{privateKey}");
        var test2 = new Guid(hashString2);
        if (test2.CompareTo(token) == 0)
        {
            return true;
        }

        return false;
    }

}

이렇게 하면 토큰은 최대 24시간까지 존재합니다.여기 24시간까지 유효한 토큰을 생성하는 코드가 있습니다. 이 코드는 우리가 사용하지만 나는 그것을 구성하지 않았습니다.

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

언급URL : https://stackoverflow.com/questions/14643735/how-to-generate-a-unique-token-which-expires-after-24-hours

반응형