当同一个用户 ID 尝试登录多个设备时,如何终止其他设备上的会话? [英] When the same user ID is trying to log in on multiple devices, how do I kill the session on the other device?

查看:27
本文介绍了当同一个用户 ID 尝试登录多个设备时,如何终止其他设备上的会话?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想要做的是将用户 ID 限制为一次只能登录到一台设备.例如,用户 IDabc"登录到他们的计算机.用户 IDabc"现在尝试从他们的手机登录.我想要发生的是终止他们计算机上的会话.

What I want to do is to limit a user ID to only being able to log in to one device at a time. For example, user ID "abc" logs in to their computer. User ID "abc" now tries to log in from their phone. What I want to happen is to kill the session on their computer.

Spotify 应用正是这样做的 - Spotify 一次只允许一个用户 ID 登录到一台设备上.

The Spotify app does exactly this- Spotify only allows one User ID to be logged in on one device at a time.

我正在使用 ASP.NET 成员身份 (SqlMembershipProvider) 和表单身份验证.

I'm using ASP.NET membership (SqlMembershipProvider) and Forms Authentication.

我已经尝试过 Session 变量,但我不确定从这里开始.

I've experimented with Session variables but I'm not sure exactly where to go from here.

推荐答案

我想出了一个非常棒的解决方案.我实施的是当用户Bob"从他们的 PC 登录,然后同一个用户Bob"从另一个位置登录时,第一个位置(他们的 PC)的登录将被终止,同时允许第二个位置登录即可生活.用户登录后,它会将一条记录插入到我创建的名为登录"的自定义表中.成功登录后,将在此表中插入一条记录,其中包含UserId、SessionId 和 LoggedIn"的值.UserId 是不言自明的,SessionId 是当前的会话 ID(在下面解释如何获取),而 LoggedIn 只是一个布尔值,在用户成功登录时最初设置为 True.成功验证用户后,我将此插入"逻辑放置在我的 AccountController 的 Login 方法中 - 见下文:

I came up with a pretty awesome solution to this. What I've implemented was when user "Bob" logs in from their PC, and then the same user "Bob" logs in from another location, the log-in from the first location (their PC) will be killed while allowing the second log-in to live. Once a user logs in, it inserts a record into a custom table I created called "Logins". Upon a successful log-in, one record will be inserted into this table with values for "UserId, SessionId, and LoggedIn". UserId is pretty self-explanatory, SessionId is the current Session ID (explained below how to get), and LoggedIn is simply a Boolean that's initially set to True upon a successful user log-in. I place this "insert" logic inside my Login method of my AccountController upon successful validation of the user- see below:

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();

对于我的情况,我想对我的每个控制器进行检查,以查看当前登录的用户是否在其他地方登录,如果是,则终止其他会话.然后,当被终止的会话尝试导航到我放置这些检查的任何位置时,它会将它们注销并将它们重定向到登录屏幕.

For my situation, I want to place the check on each of my controllers to see if the currently logged in user is logged in elsewhere, and if so, kill the other session(s). Then, when the killed session tries to navigate anywhere I placed these checks on, it'll log them out and redirect them to the Log-in screen.

我有三个主要方法来执行这些检查:

I have three main methods that does these checks:

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

将会话 ID 保存到会话["..."]

在这一切之前,我将 SessionID 保存到 AccountController 内的 Session 集合中,在 Login ([HttpPost]) 方法内:

Before all of this though, I save the SessionID to the Session collection inside the AccountController, inside the Login ([HttpPost]) method:

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

控制器代码

然后我在我的控制器中放置逻辑来控制这三个方法的执行流程.请注意,如果由于某种原因 Session["sessionid"]null,它只会简单地为其分配一个空"值.这是以防万一它由于某种原因返回为空:

I then place logic inside my controllers to control the flow of the execution of these three methods. Notice below that if for some reason Session["sessionid"] is null, it'll just simply assign it a value of "empty". This is just in case for some reason it comes back as 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 是否已登录在其他地方,如果是这样,只需在登录表中将他们的 LoggedIn 状态设置为 false 即可将其关闭.

These are the methods I use to check to see if YOU are still logged in (i.e. make sure you weren't kicked off by another log-in attempt), and if so, check to see if your User ID is logged in somewhere else, and if so, kick them off by simply setting their LoggedIn status to false in the Logins table.

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 I just also want to add that this code ignores the capability of the "Remember Me" feature. My requirement didn't involve this feature (in fact, my customer didn't want to use it, for security reasons) so I just left it out. With some additional coding though, I'm pretty certain that this could be taken into consideration.

这篇关于当同一个用户 ID 尝试登录多个设备时,如何终止其他设备上的会话?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆