Mik*_*rks 33 c# asp.net-membership session-state sqlmembershipprovider asp.net-mvc-4
我想要做的是将用户ID限制为只能一次登录到一个设备.例如,用户ID"abc"登录到他们的计算机.用户ID"abc"现在尝试从手机登录.我想要发生的是在他们的计算机上杀死会话.
Spotify应用就是这样做的 - Spotify只允许一个用户ID一次登录到一个设备上.
我正在使用ASP.NET成员资格(SqlMembershipProvider)和表单身份验证.
我已尝试使用Session变量,但我不确定从何处开始.
Mik*_*rks 36
我想出了一个非常棒的解决方案.我实现的是当用户"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();
Run Code Online (Sandbox Code Playgroud)
对于我的情况,我想检查我的每个控制器,看看当前登录的用户是否在其他地方登录,如果是,则杀死其他会话.然后,当被杀死的会话尝试在我放置这些检查的任何地方导航时,它会将它们记录下来并将它们重定向到登录屏幕.
我有三种主要方法来执行这些检查:
IsYourLoginStillTrue(UserId, SessionId);
IsUserLoggedOnElsewhere(UserId, SessionId);
LogEveryoneElseOut(UserId, SessionId);
Run Code Online (Sandbox Code Playgroud)
将会话ID保存到会话["..."]
在此之前,我将SessionID保存到AccountController内的Session集合中,在Login([HttpPost])方法内:
if (Membership.ValidateUser(model.UserName, model.Password))
{
Session["sessionid"] = System.Web.HttpContext.Current.Session.SessionID;
...
Run Code Online (Sandbox Code Playgroud)
控制器代码
然后我将逻辑放在我的控制器中以控制这三种方法的执行流程.注意低于如果由于某种原因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");
}
}
Run Code Online (Sandbox Code Playgroud)
三种方法
这些是我用来检查您是否仍然登录的方法(即确保您没有被其他登录尝试启动),如果是,请检查您的用户ID是否已记录在其他位置如果是这样,只需false在Logins表中设置LoggedIn状态即可启动它们.
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();
}
Run Code Online (Sandbox Code Playgroud)
编辑我还想补充一点,这段代码忽略了"记住我"功能的功能.我的要求没有涉及这个功能(事实上,出于安全原因,我的客户不想使用它)所以我就把它留了出来.但是通过一些额外的编码,我很确定这可以考虑到.
您必须将某人登录的信息存储到数据库中.这将允许您验证用户是否已有现有会话.开箱即用的ASP.NET中的表单身份验证模块使用cookie,您无法在服务器上知道用户是否在其他设备上有cookie,除非您将此信息存储在服务器上.
您可能想要做的是当用户登录时,您将会话ID保存在某个地方的数据库中.然后在您访问的每个页面上,您必须检查当前会话ID是否与存储在数据库中的内容相同,如果不是,则将其签名.
您可能希望创建一个在OnAuthorization或OnActionExecuting方法中执行此操作的基本控制器.另一个选择是创建自己的授权过滤器(我更喜欢自己,实际上,因为我不喜欢像这样的常见基类).
在该方法中,您将访问数据库并检查会话ID.
请注意,他并非万无一失.有人可能会复制会话cookie并解决这个问题,尽管这个模糊不清以至于大多数人可能不会知道如何做到这一点,而且非常烦人,那些做不会打扰的人.
你也可以使用IP地址,但这是同样的交易.代理或nat防火墙后面的两个人似乎是同一个用户.