ユーザーが詳細を入力してReset Password
ボタンをクリックしたとき。次のコントローラーが呼び出されます。
public ActionResult ResetPassword(ResetPassword model)
{
...
return RedirectToAction("Logout");
}
ユーザーがパスワードを変更すると、ブラウザからLogged Out
を取得します。ただし、同時に別のブラウザにログインしている場合は、他のブラウザにログインしたままになります。
ユーザーがパスワードを変更したときに、ログインしているすべてのブラウザーからユーザーをログアウトしたいと思います。
それで私は家に帰って、いくつかのコードをまとめることにしました。コードを見せてください!!!
ハンドラーを使用するので、ユーザーが最初にアプリケーションにアクセスしたときに常に検証が行われ、アクションメソッドにアクセスするたびに1か所で検証が行われます。
ユーザーがパスワードをリセットすると、アプリケーションは、ユーザーがパスワードをリセットし、初めてログインしていないことを記録し、ユーザーをサインアウトするという考え方です。
user.HasResetPassword = true;
user.IsFirstLoginAfterPasswordReset = false;
ユーザーがサインインすると、アプリケーションは、ユーザーが以前にパスワードをリセットし、現在初めてサインインしているかどうかを確認します。これらのステートメントが有効な場合、アプリケーションはレコードを更新して、パスワードをリセットしておらず、初めてサインインしていないことを通知します。
ステップ1
ApplicationUserモデルに2つのプロパティを追加します
ステップ2
以下の実装を使用して、ModelsフォルダーにクラスAuthHandler.csを追加します。この段階で、ユーザーがパスワードをリセットし、パスワードがリセットされてから初めてログインしていないかどうかを確認します。これが当てはまる場合は、ユーザーをログインにリダイレクトします。
ステップ3
RouteConfig.csでAuthHandlerを呼び出して、アプリケーションへの着信httpリクエストごとに呼び出されるようにします。
ステップ4
ResetPasswordメソッドで、以下のように実装を追加します。このステップで、ユーザーがパスワードをリセットしたら、プロパティを更新して、「パスワードをリセットし、初めてログインしていない」と言います。ユーザーがパスワードをリセットすると、ユーザーも明示的にサインアウトすることに注意してください。
ステップ5
Loginメソッドで、以下の実装を追加します。このステップで、ユーザーが正常にログインした場合は、パスワードがリセットされ、初めてログインしたことがfalseであることを確認します。すべての条件が当てはまる場合は、データベースのプロパティを更新して、ユーザーが将来パスワードをリセットしたときにプロパティが準備できる状態になるようにします。したがって、パスワードのリセットと、パスワードをリセットした後の最初のログインの状態を判別して更新する一種のループです。
最後に
AspnetUsersテーブルは次のようになります。
コメント
これが私がそれにアプローチする方法です。私はそれをテストしていませんので、例外が発生した場合は変更する必要があります。また、問題を解決するためのアプローチを示すためにすべてハードコーディングされています。
ASP.NET Identity 2を使用しているのを見ました。実行しようとしていることはすでに組み込まれています。必要なのはSecurityStampを変更することだけで、以前の認証Cookieはすべて無効になります。
パスワードを変更した後、SecurityStamp:も変更する必要があります。
_await UserManager.ChangePasswordAsync(User.Identity.GetUserId(), model.OldPassword, model.NewPassword);
await UserManager.UpdateSecurityStampAsync(User.Identity.GetUserId());
_
ユーザーがログインしたままにする場合は、新しい認証Cookie(サインイン)を再発行する必要があります。
_ await SignInManager.SignInAsync(user, isPersistent: false, rememberBrowser: false);
_
そうしないと、パスワードの変更を開始したユーザー/セッションもログアウトされます。
また、他のすべてのセッションをすぐにログアウトするには、構成のチェック間隔を短くする必要があります。
_app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
LoginPath = new PathString("/Account/Login"),
Provider = new CookieAuthenticationProvider
{
// Enables the application to validate the security stamp when the user logs in.
// This is a security feature which is used when you change a password or add an external login to your account.
OnValidateIdentity = SecurityStampValidator.OnValidateIdentity<ApplicationUserManager, ApplicationUser>(
validateInterval: TimeSpan.FromSeconds(1),
regenerateIdentity: (manager, user) => user.GenerateUserIdentityAsync(manager))
}
});
_
再現する手順:
validateInterval: TimeSpan.FromMinutes(30)
をvalidateInterval: TimeSpan.FromSeconds(1)
に変更UserManager.UpdateSecurityStampAsync
_メソッド呼び出しを追加します。ASP.NET ID認証は、ユーザーのブラウザーのCookieに依存しています。 2つの異なるブラウザを使用してテストしているためです。 2つの異なる認証Cookieがあります。Cookieの有効期限が切れるまで、ユーザーは引き続き認証されます。そのため、その結果が得られます。
したがって、カスタム実装を用意する必要があります。
たとえば、ユーザーがパスワードをリセットし、新しいパスワードで初めてログインしていないかどうかを常に確認してください。まだ行っていない場合は、ログアウトして、ログインにリダイレクトします。ログインすると、新しい認証Cookieが作成されます。
ASP.NET認証でさえ、ユーザーがまだアクティブなログインユーザーであるかどうかを確認するために二次チェックが必要であると明確に述べています(たとえば、ユーザーをブロックしたり、ユーザーがパスワードを変更した可能性があります)、フォーム認証チケットは提供しませんこれらに対するセキュリティ。
serSessionはASP.NET MVCセッションとは関係ありません。ここでは単なる名前です
私が実装したソリューションは、
UserSessionID (PK, Identity) UserID (FK) DateCreated, DateUpdated
を使用してデータベースにUserSessions
テーブルを作成しますユーザーがログインしたとき
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);
// this will prevent cookie hijacking
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);
// deleting Session will prevent all saved tickets from
// logging in
db.Database.ExecuteSql(
"DELETE FROM UerSessions WHERE UserSessionID=@SID",
new SqlParameter("@SID", sessionID));
}
Githubのブログからこの記事を中心にアプローチをモデル化しました
彼らはRubyを使用してHybrid Cookie Store / DB approach
を使用しますが、私はそれをMy ASP .Net MVCプロジェクトに移植し、正常に動作します。
ユーザーは他のすべてのセッションを表示し、必要に応じてそれらを取り消すことができます。ユーザーがパスワードをリセットすると、アクティブなセッションはすべて取り消されます。
ベースコントローラーでActionFilterAttribute
を使用して、アクティブなセッションのCookieを確認します。セッションCookieが古くなっていることが判明した場合、ユーザーはログアウトされ、サインインするようにリダイレクトされます。
CodeRealmの回答に基づく...
ブラウザでアプリケーションへのhttpsアクセスがnullポインタ例外をスローする状況(つまり、オブジェクト参照がオブジェクトのインスタンスに設定されていない)を経験した人にとっては、HasResetPassWordおよび/がデータベースに存在する可能性があるためです。またはIsFirstLoginAfterPasswordResetがnullです。 Httpリクエストは機能しますが、httpsリクエストは失敗します。理由はわかりません。
解決策:データベースを手動で更新し、両方のフィールドに値を指定するだけです。できれば、両方の列でfalseを指定します。