C# 如何在MVC4应用程序中实现单用户登录

C# 如何在MVC4应用程序中实现单用户登录,c#,asp.net,asp.net-mvc-4,session,simplemembership,C#,Asp.net,Asp.net Mvc 4,Session,Simplemembership,我正在实现一个应用程序,其中使用该应用程序的公司中的每台机器都有一名技工。我正在尝试实现一个用户策略,根据该策略,如果用户的角色为“机械师”——用户名为Machine1,并且已登录该机器,则公司只能同时有一个用户使用Machine1用户名登录。 如果其他人尝试使用相同的用户名登录,则应阻止该用户并通知其已登录用户。当会话超时到期时,我应该注销已登录的用户并释放要使用的登录名。当用户自己注销时也会发生同样的情况。我正在尝试在asp.NETMVC4应用程序上构建此应用程序 我考虑过在数据库中使用Se

我正在实现一个应用程序,其中使用该应用程序的公司中的每台机器都有一名技工。我正在尝试实现一个用户策略,根据该策略,如果用户的角色为“机械师”——用户名为Machine1,并且已登录该机器,则公司只能同时有一个用户使用Machine1用户名登录。 如果其他人尝试使用相同的用户名登录,则应阻止该用户并通知其已登录用户。当会话超时到期时,我应该注销已登录的用户并释放要使用的登录名。当用户自己注销时也会发生同样的情况。我正在尝试在asp.NETMVC4应用程序上构建此应用程序

我考虑过在数据库中使用SessionId、UserId和IsLoggedIn布尔值。但在这种情况下,我需要更改MVC应用程序中会话超时时的登录标志以写入数据库,如果有许多用户登录,这似乎有些过分

实施情况如何?我应该使用什么方法或属性来处理数据库中的会话管理

供参考

我制定了自己的方法,检查用户是否登录,如下所示:

public static bool ValidateUser(string username, string password, string companyName)
{
    int? companyId = myRepository.GetCompanyId(companyName);

    int? userId = companyId == 0 ? null : myRepository.GetUserId(username, companyId);

    if (userId.HasValue && userId.Value != 0)
    {
        var userKey = Security.GenerateUserKey(username, companyName);
        return WebSecurity.Login(userKey, password);
    }
    else
    {
        return false;
    }
}

在这个方法中,我可以以某种方式检查会话id是否与数据库中的相同。

我做过类似的事情,它们总是涉及其中。你需要某种锁或信号灯。 这最好在数据库端通过repo操作完成

我不确定这是否应该算作一个实际的答案,因为我给你的不是一个实现,而是一种方法

方法1:

通过身份验证后,使用独占锁保持用户行锁定。一旦用户注销,repo(EF)连接将被回收,独占锁将被释放

您需要在登录过程中使用
try-catch
(并可能使用超时),因为这将是登录尝试的结果

方法2:

登录后,通过取消登录权限锁定用户:

MembershipUser muUser = Membership.GetUser(strUsernameToActOn);
muUser.IsApproved = false;
Membership.UpdateUser(muUser);
注意:此方法需要维护,以确保用户在注销后解锁。这可能很棘手。您可以选择使用
析构函数
操作或后台守护进程来检查该用户上次实际活动的时间

方法3:

在用户记录上有登录时间戳。每当用户点击数据库执行任何操作时,更新此时间戳

在登录期间,检查带有合理偏移量的时间戳,以查看是否允许登录

注销时,清空时间戳

方法4

使用
singleton
类:登录时,使用构造函数创建一个实例,将用户ID(或名称)添加到不允许登录的用户的静态列表中

使用
析构函数
操作删除用户(或检查是否存在单例实例)


不允许静态-单例驱动列表中的用户进行任何登录。

此问题的关键在于知道用户何时注销以允许下一个同名用户登录。我不知道在web应用程序中有什么确切的方法可以做到这一点,但这里有一种方法可以通过控制用户的登录持续时间来大致了解用户何时注销。为了测试这一点,我将超时设置为1分钟,这样我就可以快速测试使用相同用户名的不同用户之间的通信。您可以通过web.config进行控制

<forms loginUrl="~/Account/Login" timeout="1" slidingExpiration="false" />
请注意,我使用了AbsoluteExpiration并将超时设置为与我为表单身份验证设置的长度相同的长度。这将在写入对象后1分钟内从缓存中清除该对象。这样,我就可以检查缓存中是否存在该对象,以确定为用户登录分配的时间是否已过。我使用用户名作为缓存对象的密钥,因为我们在任何时候都要检查系统中是否有一个使用该用户名的用户

现在我们只需将登录操作更改为如下所示

    [HttpPost]
    [AllowAnonymous]
    [ValidateAntiForgeryToken]
    public ActionResult Login(LoginModel model, string returnUrl)
    {
        if (ModelState.IsValid )
        {
            if (UserManager.IsLoggedIn(model.UserName))
            {
                ModelState.AddModelError("", "A user with that user name is already logged in.");
                return View(model);
            }
            if (WebSecurity.Login(model.UserName, model.Password, persistCookie: model.RememberMe))
            {
                UserManager.SetToLoggedIn(model.UserName);
                return RedirectToLocal(returnUrl);
            }
        }

        // If we got this far, something failed, redisplay form
        ModelState.AddModelError("", "The user name or password provided is incorrect.");
        return View(model);
    }

我使用SimpleMembership在MVC4应用程序上测试了这一点,它可以正常工作。这种方法的唯一缺点是需要绝对超时,而不是滑动。获取正确的超时设置对于减少用户在特定时间间隔内注销的挫折感以及最大限度地减少其他用户必须等待登录的时间至关重要。

我看到有人提到两个用户都在用户组名“Mechanical”中,而用户以相同的帐户名登录。你的一人政策基于哪一个?另外,我想知道一个只能由一个人同时使用的web应用程序的好处是什么。我编辑了一个问题“但是在这种情况下,我需要在MVC应用程序中更改会话超时时的登录标志以写入数据库,如果有多个用户登录,这似乎是过度杀伤力。”-你说的“过度杀伤力”是什么意思?你想在用户忘记时注销吗?它不起作用吗?你担心它效率不高吗?请尝试这样实现它,请参阅,如果遇到实际问题,请返回。如果您不想对会话结束做出反应,则登录后必须检查具有给定名称的用户在过去的
N
分钟内是否有任何活动,如果没有,注销旧会话。您可能会使用什么类型的身份验证windows/forms?因此,如果用户在一分钟后仍然登录,则缓存项将被删除,并且登录后返回空值?我遗漏了什么?IsLoggedIn返回一个布尔值。它使用string.IsNullOrEmpty检查缓存对象是否存在,因为缓存中存储的对象只是一个字符串。如果缓存对象不存在,则为该用户分配的时间(在缓存策略中设置)已过期,否则未过期。缓存中设置的分配时间与
    [HttpPost]
    [AllowAnonymous]
    [ValidateAntiForgeryToken]
    public ActionResult Login(LoginModel model, string returnUrl)
    {
        if (ModelState.IsValid )
        {
            if (UserManager.IsLoggedIn(model.UserName))
            {
                ModelState.AddModelError("", "A user with that user name is already logged in.");
                return View(model);
            }
            if (WebSecurity.Login(model.UserName, model.Password, persistCookie: model.RememberMe))
            {
                UserManager.SetToLoggedIn(model.UserName);
                return RedirectToLocal(returnUrl);
            }
        }

        // If we got this far, something failed, redisplay form
        ModelState.AddModelError("", "The user name or password provided is incorrect.");
        return View(model);
    }