如何确保用户仅登录一次?

时间:2020-03-06 14:45:25  来源:igfitidea点击:

几年前,我开发了一个Web应用程序,我们想要确保该用户不共享凭据。

我们决定要做的一件事就是只允许用户一次从一台计算机上登录。我这样做的方法是每N秒对服务器进行一点iframe ping操作;只要服务器具有特定用户的心跳(来自特定IP),就不允许该用户从任何其他IP登录。

该解决方案尽管得到了我的经理的认可,但对我而言似乎总是很棘手。另外,似乎很容易规避。

有没有一种好的方法来确保Web应用程序用户仅登录一次?老实说,我从来不明白为什么管理层甚至想要此功能。在分布式应用程序上强制执行此操作是否有意义?

解决方案

在高度安全的应用程序中,可能会要求我们这样做。我们可以做的是保持登录计数递增,以增加登录用户和IP地址的数量。计数永远不能为2. 如果是,则我们注销另一个IP,并且无论该IP登录到谁,该IP都会被丢弃。这不会阻止用户1将其凭据提供给用户2,如果用户2同时登录其他地方,只会使用户1难以完成其工作。

我从未找到解决此问题的标准解决方案。在我的一个应用程序中,我结合使用Javascript和Java来确保只能从指定IP(实际上是会话ID)记录一次用户,但在最坏的情况下会超时(设置为2分钟)无法使用该帐户。
我不为什么没有通用的方法来做到这一点。

我通过维护当前登录用户的哈希表来实现这一点,键是用户名,值是他们的上次活动时间。

登录时,我们只需要检查此哈希表中的密钥即可,如果该密钥存在,则拒绝登录。

当用户执行任何操作时,我们将随时间更新哈希表(如果使其成为核心页面框架的一部分,这将很容易)。

如果哈希表中的时间大于20分钟的闲置时间,则将其删除。我们可以在每次检查哈希表时执行此操作,因此,即使我们只有一个用户,并且尝试在几小时后登录,在该初始检查期间,也会将其从哈希表中删除,以保持空闲状态。

C(未经测试)中的一些示例:

public Dictionary<String,DateTime> UserDictionary
{
    get
    {
        if (HttpContext.Current.Cache["UserDictionary"] != null)
        {
            return HttpContext.Current.Cache["UserDictionary"] as Dictionary<String,DateTime>;
        }
        return new Dictionary<String,DateTime>();
    }
    set
    {
        HttpContext.Current.Cache["UserDictionary"] = value;
    }
}

public bool IsUserAlreadyLoggedIn(string userName)
{
    removeIdleUsers();
    return UserDictionary.ContainsKey(userName);
}

public void UpdateUser(string userName)
{
    UserDictionary[userName] = DateTime.Now;

    removeIdleUsers();
}

private void removeIdleUsers()
{
   for (int i = 0; i < UserDictionary.Length; i++)
        {
            if (user[i].Value < DateTime.Now.AddMinutes(-20))
                user.RemoveAt(i);
        }
}

只看IP是不可靠的。 IIRC有一些代理样式,可以通过多个IP地址随机处理传出请求。根据应用范围,这可能会或者可能不会影响我们。其他代理将显示来自单个IP的大量流量。

上次登录时间也可能是一个问题。考虑基于cookie的身份验证,其中身份验证cookie并不是持久的(一件好事)。如果浏览器崩溃或者关闭,则用户必须重新登录,但直到超时到期后才能登录。如果该应用程序用于交易股票,则20分钟的不工作时间会花费金钱,这可能是不可接受的。

通常,可以购买比我们或者我做得更好的智能防火墙/路由器做得更好。它们还有助于防止重放攻击,cookie窃取等,并且可以配置为与所选Web平台中的标准机制一起运行。

我只是有这个问题。

我们正在构建一个包含Flex应用程序(由客户端构建)的Drupal网站,他想要以下内容:

  • 从Drupal <-> Flex透明登录(真好!)
  • 没有并发登录!

他测试了每种解决方案中的废话,最后,这就是我们所做的:

  • 我们通过每个URL传递了会话ID。
  • 当用户登录时,我们建立了一个时间戳-IP-SessionID-用户名足迹
  • 每个页面都对数据库执行了ping操作,并且如果在同一IP上找到同一用户且具有不同的SessionID,则将引导他们的较早用户

该解决方案满足了客户的严格测试要求(他家中有2台计算机。在我们提出此解决方案之前,他让我们工作了几个小时才发现代码中的小问题和缝隙)

我会解决这个问题,并允许以任何较早的登录为代价来允许最后一次登录,因此,每当用户登录时,都要终止他可能拥有的任何其他登录会话。

它实施起来非常容易,我们最终会知道自己在哪里。

被警告过类似的"特征",这是一个边缘案例的陷阱,我们最终以为自己被钉上了,然后我们或者其他人说"但是如果有人做了X会怎样?"并且我们意识到我们必须增加另一层复杂性。

例如:

  • 如果用户打开带有会话副本的新选项卡怎么办?
  • 如果用户打开一个新的浏览器窗口怎么办?
  • 如果用户登录并且浏览器崩溃怎么办?

等等...

基本上,有一系列或者多或者少的hacky解决方案,它们都不是万无一失的,所有这些都将很难维护。通常,客户的真正目标是其他一些合法的安全目标,例如"停止用户共享帐户"。

最好的主意是找出根本目标是什么,并找到实现该目标的方法。而且我担心这涉及谈判外交和其他"软技能",而不是进行技术性的追赶。