web-dev-qa-db-ja.com

同じユーザーIDが複数のデバイスにログインしようとしている場合、他のデバイスでセッションを強制終了するにはどうすればよいですか?

私がしたいのは、ユーザーIDを一度に1つのデバイスにしかログインできないように制限することです。たとえば、ユーザーID「abc」は自分のコンピューターにログインします。ユーザーID「abc」は、電話からログインしようとします。私がやりたいのは、彼らのコンピューターでセッションを終了することです。

Spotifyアプリはこれを正確に実行します。Spotifyは、一度に1つのデバイスにログインできるユーザーIDは1つだけです。

ASP.NETメンバーシップ(SqlMembershipProvider)とフォーム認証を使用しています。

セッション変数を試しましたが、ここからどこに進むべきか正確にはわかりません。

33
Mike Marks

これに対する非常に素晴らしい解決策を思いつきました。私が実装したのは、ユーザー "Bob"がPCからログインし、同じユーザー "Bob"が別の場所からログインすると、最初の場所(PC)からのログインが殺され、2番目のログインしてライブ。ユーザーがログインすると、作成した「Logins」というカスタムテーブルにレコードが挿入されます。ログインに成功すると、「UserId、SessionId、およびLoggedIn」の値を持つ1つのレコードがこのテーブルに挿入されます。 UserIdは一目瞭然、SessionIdは現在のセッションID(取得方法については以下で説明)、LoggedInはユーザーのログインが成功すると最初にTrueに設定されるブール値です。ユーザーの検証が成功したら、AccountControllerのLoginメソッド内にこの「挿入」ロジックを配置します。以下を参照してください。

Logins login = new Logins();
login.UserId = model.UserName;
login.SessionId = System.Web.HttpContext.Current.Session.SessionID;;
login.LoggedIn = true;

LoginsRepository repo = new LoginsRepository();
repo.InsertOrUpdate(login);
repo.Save();

私の状況では、現在ログインしているユーザーが別の場所にログインしているかどうかを確認するために各コントローラーをチェックし、そうであれば他のセッションを強制終了します。その後、強制終了されたセッションがこれらのチェックを配置した任意の場所に移動しようとすると、ログアウトされてログイン画面にリダイレクトされます。

これらのチェックを行う3つの主な方法があります。

IsYourLoginStillTrue(UserId, SessionId);
IsUserLoggedOnElsewhere(UserId, SessionId);
LogEveryoneElseOut(UserId, SessionId);

セッションIDをセッションに保存["..."]

ただし、このすべての前に、SessionController内のLogin[HttpPost]) 方法:

if (Membership.ValidateUser(model.UserName, model.Password))
{
     Session["sessionid"] = System.Web.HttpContext.Current.Session.SessionID;
...

コントローラーコード

その後、コントローラー内にロジックを配置して、これら3つのメソッドの実行フローを制御します。何らかの理由でSession["sessionid"]nullで、単に「空」の値を割り当てるだけです。これは、何らかの理由でnullとして返される場合に備えています。

public ActionResult Index()
{
    if (Session["sessionid"] == null)
        Session["sessionid"] = "empty";

    // check to see if your ID in the Logins table has LoggedIn = true - if so, continue, otherwise, redirect to Login page.
    if (OperationContext.IsYourLoginStillTrue(System.Web.HttpContext.Current.User.Identity.Name, Session["sessionid"].ToString()))
    {
        // check to see if your user ID is being used elsewhere under a different session ID
        if (!OperationContext.IsUserLoggedOnElsewhere(System.Web.HttpContext.Current.User.Identity.Name, Session["sessionid"].ToString()))
        {
            return View();
        }
        else
        {
            // if it is being used elsewhere, update all their Logins records to LoggedIn = false, except for your session ID
            OperationContext.LogEveryoneElseOut(System.Web.HttpContext.Current.User.Identity.Name, Session["sessionid"].ToString());
            return View();
        }
    }
    else
    {
        FormsAuthentication.SignOut();
        return RedirectToAction("Login", "Account");
    }
}

つの方法

これらは、あなたがまだログインしているかどうかを確認するために使用する方法です(つまり、別のログイン試行によってキックオフされていないことを確認します)。そうであれば、ユーザーIDが他の場所にログインしているかどうかを確認します、もしそうなら、LoginsInステータスをLoginsテーブルでfalseに設定するだけでキックオフします。

public static bool IsYourLoginStillTrue(string userId, string sid)
{
    CapWorxQuikCapContext context = new CapWorxQuikCapContext();

    IEnumerable<Logins> logins = (from i in context.Logins
                                  where i.LoggedIn == true && i.UserId == userId && i.SessionId == sid
                                  select i).AsEnumerable();
    return logins.Any();
}

public static bool IsUserLoggedOnElsewhere(string userId, string sid)
{
    CapWorxQuikCapContext context = new CapWorxQuikCapContext();

    IEnumerable<Logins> logins = (from i in context.Logins
                                  where i.LoggedIn == true && i.UserId == userId && i.SessionId != sid
                                  select i).AsEnumerable();
    return logins.Any();
}

public static void LogEveryoneElseOut(string userId, string sid)
{
    CapWorxQuikCapContext context = new CapWorxQuikCapContext();

    IEnumerable<Logins> logins = (from i in context.Logins 
                                  where i.LoggedIn == true && i.UserId == userId && i.SessionId != sid // need to filter by user ID
                                  select i).AsEnumerable();

    foreach (Logins item in logins)
    {
        item.LoggedIn = false;
    }

    context.SaveChanges();
}

[〜#〜] edit [〜#〜]また、このコードが「Remember Me」機能の機能を無視することも追加したいだけです。私の要件にはこの機能が関係していなかったので(実際、セキュリティ上の理由から、顧客はこの機能を使いたくありませんでした)、そのまま残しました。ただし、追加のコーディングを行うことで、これを考慮に入れることができると確信しています。

36
Mike Marks

誰かがデータベースにログインしたという情報を保存する必要があります。これにより、ユーザーが既存のセッションを既に持っているかどうかを確認できます。 ASP.NETのフォーム認証モジュールはそのまま使用でき、Cookieを使用できます。もちろん、サーバーにこの情報を保存しない限り、ユーザーが他のデバイスにCookieを持っているかどうかをサーバーで知る方法はありません。

7
Darin Dimitrov

おそらくやりたいことは、ユーザーがログインしたときに、セッションIDをデータベースのどこかに保存することです。次に、アクセスするすべてのページで、現在のセッションIDがデータベースに保存されているものと同じであるかどうかを確認し、そうでない場合はログアウトします。

OnAuthorizationメソッドまたはOnActionExecutingメソッドでこれを行うベースコントローラーを作成することをお勧めします。もう1つのオプションは、独自の承認フィルターを作成することです(実際、私はそのような一般的な基本クラスが好きではないので、自分自身を好むでしょう)。

その方法では、データベースにアクセスし、セッションIDを確認します。

彼は絶対確実ではないことに注意してください。だれかがセッションCookieをコピーしてこれを回避することは可能ですが、ほとんどの人はおそらくそれを行う方法を知らないので、面倒なことは気にしません。

IPアドレスを使用することもできますが、それは同じことです。プロキシまたはNATファイアウォールの背後にいる2人のユーザーは、同じユーザーのように見えます。

5

以下は、受け入れられている答えよりも少し簡単な方法です。

public static class SessionManager
    {
        private static List<User> _sessions = new List<User>();

        public static void RegisterLogin(User user)
        {
            if (user != null)
            {
                _sessions.RemoveAll(u => u.UserName == user.UserName);
                _sessions.Add(user);
            }
        }

        public static void DeregisterLogin(User user)
        {
            if (user != null)
                _sessions.RemoveAll(u => u.UserName == user.UserName && u.SessionId == user.SessionId);
        }

        public static bool ValidateCurrentLogin(User user)
        {
            return user != null && _sessions.Any(u => u.UserName == user.UserName && u.SessionId == user.SessionId);
        }
    }

    public class User {
        public string UserName { get; set; }
        public string SessionId { get; set; }
    }

これにより、ユーザーを検証した後のログインプロセス中に、Userクラスのインスタンスを作成し、ユーザー名とセッションIDを割り当て、それをSessionオブジェクトとして保存してから、RegisterLogin関数を呼び出します。

次に、ページを読み込むたびに、セッションオブジェクトを取得し、ValidateCurrentLogin関数に渡します。

DeregisterLogin関数は必ずしも必要ではありませんが、_sessionsオブジェクトを可能な限り小さくします。

3
Kevin

Session ["SessionID"] = "anything"を設定する主な理由は、実際にセッションオブジェクトに何かを割り当てるまで、セッションIDはリクエストごとに変化し続けるためです。

私が書いたいくつかの分割テストソフトウェアでこれに遭遇しました。

1
Roger Willcocks