49

ユーザーが有効かどうかを確認する 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;
    }
}   
4

6 に答える 6

156

考えられるアプローチは 2 つあります。一意の値を作成し、データベースなどの作成時間とともにどこかに保存するか、トークン内に作成時間を入れて、後でデコードしていつ作成されたかを確認できるようにします。

一意のトークンを作成するには:

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
}

注: タイム スタンプ付きのトークンを安全にする必要がある場合は、トークンを暗号化する必要があります。そうしないと、ユーザーが内容を把握し、偽のトークンを作成する可能性があります。

于 2013-02-01T10:34:11.267 に答える
35

私はグッファの答えが好きです。コメントできないので、ここでウディルの質問に答えます。

同様のものが必要でしたが、トークンに特定のロジックが必要でした。

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

コードを他のビジネス ケースに簡単に適応させることができます。

ハッピーコーディング

ウォルター

于 2015-11-23T14:53:35.583 に答える
3

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 に存在するかどうかを確認するだけです。

于 2013-02-01T10:20:02.927 に答える