我想要做的是一个用户ID限制为仅能够在同一时间登录到一台设备。 例如,用户ID“ABC”登录到他们的计算机上。 用户ID“ABC”现在试图从自己的手机登录。 我希望发生是杀死他们的计算机上的会话。
Spotify应用不正是这个 - Spotify的只允许一次要记录在一个用户ID在一个设备上。
我使用ASP.NET成员资格(SqlMembershipProvider的)和窗体身份验证。
我尝试用Session变量,但我不知道究竟在何处,从这里走。
我想要做的是一个用户ID限制为仅能够在同一时间登录到一台设备。 例如,用户ID“ABC”登录到他们的计算机上。 用户ID“ABC”现在试图从自己的手机登录。 我希望发生是杀死他们的计算机上的会话。
Spotify应用不正是这个 - Spotify的只允许一次要记录在一个用户ID在一个设备上。
我使用ASP.NET成员资格(SqlMembershipProvider的)和窗体身份验证。
我尝试用Session变量,但我不知道究竟在何处,从这里走。
我想出了一个相当真棒解决这个。 我已经实现了当用户“鲍勃”日志从他们的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();
}
编辑我也只是想补充一点,这个代码忽略“记住我”功能的能力。 我的要求并不涉及此功能(其实,我的客户不想使用它,出于安全原因),所以我刚刚离开它。 随着尽管一些额外的编码,我敢肯定,这可以被考虑在内。
你将不得不存储有人已经登录到数据库中的信息。 这将让你验证,如果用户已经有现有会话。 开箱即用的ASP.NET窗体身份验证模块与饼干,有没有办法让你知道当然,除非你在服务器上存储这些信息的用户是否有其他设备上的cookie在服务器上。
你可能想要做的是,当用户登录时,你挽救他们的会话ID在数据库中的某个地方。 然后你访问的每个页面上,你必须检查当前会话ID是一样的什么存储在数据库中,如果不是你签出来。
你可能会想创建一个基本的控制器,这是否在OnAuthorization或OnActionExecuting方法。 另一种选择是创建自己的授权过滤器(我更喜欢我自己,其实,我不喜欢这样的公共基类)。
在这种方法中,你会访问数据库并检查会话ID。
要知道,他并非万无一失。 这可能是有人复制会话cookie并围绕这个搞定了,虽然这已经足够晦涩,大多数人可能不知道该怎么做,够烦人的,那些做不会理会。
你也可以使用IP地址,但是这是同样的协议。 后面的代理服务器或防火墙的NAT两个人似乎是相同的用户。
我想指出的是,设置会话[“的SessionID”] =“什么”的一个关键原因是,直到你真正分配到的东西Session对象的会话ID似乎不断变化的每一个要求。
我就遇上了这样一些分割测试软件,我写的。
下面是比接受的答案稍微简单的方法。
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对象尽可能小。