web-dev-qa-db-ja.com

パスワードが変更されたときにすべてのブラウザからユーザーをログアウトする

パスワードのリセットページがあります: enter image description here

ユーザーが詳細を入力してReset Passwordボタンをクリックしたとき。次のコントローラーが呼び出されます。

public ActionResult ResetPassword(ResetPassword model)
{
    ...
    return RedirectToAction("Logout");
}

ユーザーがパスワードを変更すると、ブラウザからLogged Outを取得します。ただし、同時に別のブラウザにログインしている場合は、他のブラウザにログインしたままになります。

ユーザーがパスワードを変更したときに、ログインしているすべてのブラウザーからユーザーをログアウトしたいと思います。

16
anand

それで私は家に帰って、いくつかのコードをまとめることにしました。コードを見せてください!!!

ハンドラーを使用するので、ユーザーが最初にアプリケーションにアクセスしたときに常に検証が行われ、アクションメソッドにアクセスするたびに1か所で検証が行われます。

ユーザーがパスワードをリセットすると、アプリケーションは、ユーザーがパスワードをリセットし、初めてログインしていないことを記録し、ユーザーをサインアウトするという考え方です。

user.HasResetPassword = true;
user.IsFirstLoginAfterPasswordReset = false;

ユーザーがサインインすると、アプリケーションは、ユーザーが以前にパスワードをリセットし、現在初めてサインインしているかどうかを確認します。これらのステートメントが有効な場合、アプリケーションはレコードを更新して、パスワードをリセットしておらず、初めてサインインしていないことを通知します。

ステップ1

ApplicationUserモデルに2つのプロパティを追加します

enter image description here

ステップ2

以下の実装を使用して、ModelsフォルダーにクラスAuthHandler.csを追加します。この段階で、ユーザーがパスワードをリセットし、パスワードがリセットされてから初めてログインしていないかどうかを確認します。これが当てはまる場合は、ユーザーをログインにリダイレクトします。

enter image description here

ステップ3

RouteConfig.csでAuthHandlerを呼び出して、アプリケーションへの着信httpリクエストごとに呼び出されるようにします。 enter image description here

ステップ4

ResetPasswordメソッドで、以下のように実装を追加します。このステップで、ユーザーがパスワードをリセットしたら、プロパティを更新して、「パスワードをリセットし、初めてログインしていない」と言います。ユーザーがパスワードをリセットすると、ユーザーも明示的にサインアウトすることに注意してください。

enter image description here

ステップ5

Loginメソッドで、以下の実装を追加します。このステップで、ユーザーが正常にログインした場合は、パスワードがリセットされ、初めてログインしたことがfalseであることを確認します。すべての条件が当てはまる場合は、データベースのプロパティを更新して、ユーザーが将来パスワードをリセットしたときにプロパティが準備できる状態になるようにします。したがって、パスワードのリセットと、パスワードをリセットした後の最初のログインの状態を判別して更新する一種のループです。

enter image description here

最後に

AspnetUsersテーブルは次のようになります。

enter image description here

コメント

これが私がそれにアプローチする方法です。私はそれをテストしていませんので、例外が発生した場合は変更する必要があります。また、問題を解決するためのアプローチを示すためにすべてハードコーディングされています。

6
Julius Depulla

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

再現する手順:

  1. VS2015で新しいAsp.NetWebアプリを作成しました。
  2. MVCテンプレートを選択します。
  3. 編集App_Stat/Startup.Auth.cs、34行目:validateInterval: TimeSpan.FromMinutes(30)validateInterval: TimeSpan.FromSeconds(1)に変更
  4. 編集Controllers/ManageController.cs、236行目:_UserManager.UpdateSecurityStampAsync_メソッド呼び出しを追加します。
  5. プロジェクトを実行し、ユーザーを作成し、ログインし、別のブラウザーを開いて、ログインします。
  6. パスワードを変更し、他のブラウザでページを更新します。ログアウトする必要があります。
15
Chris

ASP.NET ID認証は、ユーザーのブラウザーのCookieに依存しています。 2つの異なるブラウザを使用してテストしているためです。 2つの異なる認証Cookieがあります。Cookieの有効期限が切れるまで、ユーザーは引き続き認証されます。そのため、その結果が得られます。

したがって、カスタム実装を用意する必要があります。

たとえば、ユーザーがパスワードをリセットし、新しいパスワードで初めてログインしていないかどうかを常に確認してください。まだ行っていない場合は、ログアウトして、ログインにリダイレクトします。ログインすると、新しい認証Cookieが作成されます。

0
Julius Depulla

ASP.NET認証でさえ、ユーザーがまだアクティブなログインユーザーであるかどうかを確認するために二次チェックが必要であると明確に述べています(たとえば、ユーザーをブロックしたり、ユーザーがパスワードを変更した可能性があります)、フォーム認証チケットは提供しませんこれらに対するセキュリティ。

serSessionはASP.NET MVCセッションとは関係ありません。ここでは単なる名前です

私が実装したソリューションは、

  1. UserSessionID (PK, Identity) UserID (FK) DateCreated, DateUpdatedを使用してデータベースにUserSessionsテーブルを作成します
  2. 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);

    // 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));
}
0
Akash Kava

Githubのブログからこの記事を中心にアプローチをモデル化しました

アプリのユーザーセッションのモデリング

彼らはRubyを使用してHybrid Cookie Store / DB approachを使用しますが、私はそれをMy ASP .Net MVCプロジェクトに移植し、正常に動作します。

ユーザーは他のすべてのセッションを表示し、必要に応じてそれらを取り消すことができます。ユーザーがパスワードをリセットすると、アクティブなセッションはすべて取り消されます。

ベースコントローラーでActionFilterAttributeを使用して、アクティブなセッションのCookieを確認します。セッションCookieが古くなっていることが判明した場合、ユーザーはログアウトされ、サインインするようにリダイレクトされます。

0
Nkosi

CodeRealmの回答に基づく...

ブラウザでアプリケーションへのhttpsアクセスがnullポインタ例外をスローする状況(つまり、オブジェクト参照がオブジェクトのインスタンスに設定されていない)を経験した人にとっては、HasResetPassWordおよび/がデータベースに存在する可能性があるためです。またはIsFirstLoginAfterPasswordResetがnullです。 Httpリクエストは機能しますが、httpsリクエストは失敗します。理由はわかりません。

解決策:データベースを手動で更新し、両方のフィールドに値を指定するだけです。できれば、両方の列でfalseを指定します。

0
illusionistXV