ユーザーが有効かどうかを確認する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;
}
}
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
}
注:安全にするためにタイムスタンプ付きのトークンが必要な場合は、暗号化する必要があります。そうしないと、ユーザーは何が含まれているかを把握し、偽のトークンを作成できます。
Guffaの回答が好きです。コメントできないので、ここでUdilの質問に対する回答を提供します。
同様の何かが必要でしたが、トークンに証明書のロジックが必要でした。
ポイント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(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;
}
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時間)を超える場合は、トークンの有効期限が切れたようなメッセージを表示する必要があります。
キーを生成するには、 here を参照してください