ASP.NET 認証でさえ、ユーザーがまだアクティブなログイン ユーザーであるかどうかを確認するために二次的なチェックが必要であると明確に述べています (たとえば、ユーザーをブロックすることができ、ユーザーがパスワードを変更した可能性があります)。フォーム認証チケットは提供しません。これらに対するセキュリティ。
UserSession は ASP.NET MVC セッションとは関係ありません。ここでは単なる名前です。
私が実装した解決策は、
UserSessions
データベースにテーブルを作成しますUserSessionID (PK, Identity) UserID (FK) DateCreated, DateUpdated
- FormsAuthenticationTicket には UserData というフィールドがあり、そこに UserSessionID を保存できます。
ユーザーのログイン時
public void DoLogin(){
// do not call this ...
// FormsAuthentication.SetAuthCookie(....
DateTime dateIssued = DateTime.UtcNow;
var sessionID = db.CreateSession(UserID);
var ticket = new FormsAuthenticationTicket(
userName,
dateIssued,
dateIssued.Add(FormsAuthentication.Timeout),
iSpersistent,
// userData
sessionID.ToString());
HttpCookie cookie = new HttpCookie(
FormsAuthentication.CookieName,
FormsAuthentication.Encrypt(ticket));
cookie.Expires = ticket.Expires;
if(FormsAuthentication.CookieDomain!=null)
cookie.Domain = FormsAuthentication.CookieDomain;
cookie.Path = FormsAuthentication.CookiePath;
Response.Cookies.Add(cookie);
}
ユーザーを承認するには
Global.asax クラスにより、Authorize にフックできます
public void Application_Authorize(object sender, EventArgs e){
var user = Context.User;
if(user == null)
return;
FormsIdentity formsIdentity = user.Identity as FormsIdentity;
long userSessionID = long.Parse(formsIdentity.UserData);
string cacheKey = "US-" + userSessionID;
// caching to improve performance
object result = HttpRuntime.Cache[cacheKey];
if(result!=null){
// if we had cached that user is alright, we return..
return;
}
// hit the database and check if session is alright
// If user has logged out, then all UserSessions should have been
// deleted for this user
UserSession session = db.UserSessions
.FirstOrDefault(x=>x.UserSessionID == userSessionID);
if(session != null){
// update session and mark last date
// this helps you in tracking and you
// can also delete sessions which were not
// updated since long time...
session.DateUpdated = DateTime.UtcNow;
db.SaveChanges();
// ok user is good to login
HttpRuntime.Cache.Add(cacheKey, "OK",
// set expiration for 5 mins
DateTime.UtcNow.AddMinutes(5)..)
// I am setting cache for 5 mins to avoid
// hitting database for all session validation
return;
}
// ok validation is wrong....
throw new UnauthorizedException("Access denied");
}
ユーザーがログアウトするとき
public void Logout(){
// get the ticket..
FormsIdentity f = Context.User.Identity as FormsIdentity;
long sessionID = long.Parse(f.UserData);
var session = db.UserSessions.First(x=>x.UserSessionID = sessionID);
db.UserSession.Remove(session);
db.SaveChanges();
FormsAuthentication.Signout();
}
** ユーザーがパスワードを変更したり、ユーザーがブロックされたり、ユーザーが削除されたりした場合... **
public void ChangePassword(){
// get the ticket..
FormsIdentity f = Context.User.Identity as FormsIdentity;
long sessionID = long.Parse(f.UserData);
var session = db.UserSessions.First(x=>x.UserSessionID = sessionID);
// delete all sessions for the same user id
// this will force user to relogin on all other
// devices...
db.Database.ExecuteSql(
"DELETE FROM UserSessions WHERE UserID=@UserID",
new SqlParameter("@UserID", session.UserID));
}