我想出了一个非常棒的解决方案。我所实现的是,当用户“Bob”从他们的 PC 登录,然后同一用户“Bob”从另一个位置登录时,第一个位置(他们的 PC)的登录将被终止,同时允许第二个位置登录即可生活。用户登录后,它会将记录插入到我创建的名为“登录”的自定义表中。成功登录后,将在该表中插入一条记录,其值为“UserId、SessionId 和 LoggedIn”。 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();
对于我的情况,我想对每个控制器进行检查,以查看当前登录的用户是否在其他地方登录,如果是,则终止其他会话。然后,当被终止的会话尝试导航到我放置这些检查的任何位置时,它会将它们注销并将它们重定向到登录屏幕。
我有三种主要方法来执行这些检查:
IsYourLoginStillTrue(UserId, SessionId);
IsUserLoggedOnElsewhere(UserId, SessionId);
LogEveryoneElseOut(UserId, SessionId);
将会话 ID 保存到会话["..."]
不过,在这一切之前,我将 SessionID 保存到 AccountController 内的 Session 集合中,在Login
([HttpPost]
) 方法:
if (Membership.ValidateUser(model.UserName, model.Password))
{
Session["sessionid"] = System.Web.HttpContext.Current.Session.SessionID;
...
控制器代码
然后,我将逻辑放入控制器中来控制这三个方法的执行流程。请注意下面,如果由于某种原因Session["sessionid"]
is null
,它只会简单地为其分配一个值“empty”。这是为了防止由于某种原因它返回为空:
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();
}
EDIT我还想补充一点,此代码忽略了“记住我”功能的功能。我的要求不涉及此功能(事实上,出于安全原因,我的客户不想使用它),所以我就把它排除在外。不过,通过一些额外的编码,我非常确定可以考虑这一点。