当同一个用户ID尝试登录多台设备上,我怎么杀死其他设备上的会话?(When the same use

2019-08-20 23:05发布

我想要做的是一个用户ID限制为仅能够在同一时间登录到一台设备。 例如,用户ID“ABC”登录到他们的计算机上。 用户ID“ABC”现在试图从自己的手机登录。 我希望发生是杀死他们的计算机上的会话。

Spotify应用不正是这个 - Spotify的只允许一次要记录在一个用户ID在一个设备上。

我使用ASP.NET成员资格(SqlMembershipProvider的)和窗体身份验证。

我尝试用Session变量,但我不知道究竟在何处,从这里走。

Answer 1:

我想出了一个相当真棒解决这个。 我已经实现了当用户“鲍勃”日志从他们的PC,然后同一个用户从其他位置“鲍勃”的日志,日志,从第一位置(个人电脑)将同时允许秒杀登录去住。 一旦用户登录,这将记录插入自定义表我创建一个名为“登录”。 一旦成功登录,一个记录将被插入到这个表格,表格中“用户ID,会话ID和的loggedIn”的价值观。 用户ID是不言自明,会话ID是当前会话ID(下如何得到解释),和的loggedIn仅仅是最初真实在成功的用户登录设置为True布尔。 我把这个“插入”我在成功验证我的AccountController用户看到下面的登录方法里面的逻辑:

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

对于我的情况,我想放在我的每一个控制器的检查,以查看当前登录的用户登录其他地方,如果是这样,杀死其他会话(S)。 然后,当杀死会议试图随时随地浏览我放在这些检查,它会记录出来,将其重定向到登录画面。

我有一个做这些检查方法主要有三种:

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

保存会话ID会话[“...”]

所有这一切,虽然之前,我SessionID的保存到里面的AccountController的会话集合,里面Login[HttpPost]方法:

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

控制器代码

然后我把逻辑我的控制器内部,以控制这三种方法的执行的流程。 注意低于如果由于某种原因Session["sessionid"]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的登录表。

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

编辑我也只是想补充一点,这个代码忽略“记住我”功能的能力。 我的要求并不涉及此功能(其实,我的客户不想使用它,出于安全原因),所以我刚刚离开它。 随着尽管一些额外的编码,我敢肯定,这可以被考虑在内。



Answer 2:

你将不得不存储有人已经登录到数据库中的信息。 这将让你验证,如果用户已经有现有会话。 开箱即用的ASP.NET窗体身份验证模块与饼干,有没有办法让你知道当然,除非你在服务器上存储这些信息的用户是否有其他设备上的cookie在服务器上。



Answer 3:

你可能想要做的是,当用户登录时,你挽救他们的会话ID在数据库中的某个地方。 然后你访问的每个页面上,你必须检查当前会话ID是一样的什么存储在数据库中,如果不是你签出来。

你可能会想创建一个基本的控制器,这是否在OnAuthorization或OnActionExecuting方法。 另一种选择是创建自己的授权过滤器(我更喜欢我自己,其实,我不喜欢这样的公共基类)。

在这种方法中,你会访问数据库并检查会话ID。

要知道,他并非万无一失。 这可能是有人复制会话cookie并围绕这个搞定了,虽然这已经足够晦涩,大多数人可能不知道该怎么做,够烦人的,那些做不会理会。

你也可以使用IP地址,但是这是同样的协议。 后面的代理服务器或防火墙的NAT两个人似乎是相同的用户。



Answer 4:

我想指出的是,设置会话[“的SessionID”] =“什么”的一个关键原因是,直到你真正分配到的东西Session对象的会话ID似乎不断变化的每一个要求。

我就遇上了这样一些分割测试软件,我写的。



Answer 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; }
    }

有了这个,你在验证了用户后,你的登录过程,您可以创建用户类的实例,并为其分配的用户名和会话ID,将其保存为一个Session对象,然后调用RegisterLogin功能吧。

然后,在每个页面加载,你得到的会话对象,并把它传递给ValidateCurrentLogin功能。

该DeregisterLogin功能并非绝对必要,但保持_sessions对象尽可能小。



文章来源: When the same user ID is trying to log in on multiple devices, how do I kill the session on the other device?