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

时间:2022-09-11 18:30:34

A few years ago I developed a web app for which we wanted to make sure the users weren't sharing credentials.

几年前,我开发了一个网络应用程序,我们希望确保用户不共享凭据。

One of the things we decided to to, was only allow the user to be logged in from one computer at a time. The way I did this, was to have a little iframe ping the server every N seconds; as long as the server had a heartbeat for a particular user (from a particular IP), that user was not allowed to log in from any other IP.

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

The solution, although approved by my manger, always seemed hacky to me. Also, it seems like it would be easy to circumvent.

解决方案虽然得到了我的经理的批准,但对我来说似乎总是很苛刻。而且,似乎很容易规避。

Is there a good way to make sure a web app user only logs in once? To be honest, I never understood why management even wanted this feature. Does it make sense to enforce this on distributed apps?

有没有一种方法可以确保Web应用程序用户只登录一次?说实话,我从不理解为什么管理层甚至想要这个功能。在分布式应用程序上强制执行此操作是否有意义?

7 个解决方案

#1


11  

I've implemented this by maintaining a hashtable of currently logged in users, the key was the username, the value was their last activity time.

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

When logging in, you just check this hashtable for the key, and if it exists, reject the login.

登录时,只需检查密钥的此哈希表,如果存在,则拒绝登录。

When the user does anything, you update the hashtable with the time (This is easy if you make it part of the core page framework).

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

If the time in the hashtable is greater than 20 minutes of inactivity, you remove them. You can do this every time the hashtable is checked, so even if you only had one user, and the tried to login several hours later, during that initial check, it would remove them from the hashtable for being idle.

如果散列表中的时间超过20分钟不活动,则将其删除。您可以在每次检查哈希表时执行此操作,因此即使您只有一个用户,并且尝试在几小时后登录,在初始检查期间,它也会将它们从哈希表中删除以便空闲。

Some examples in C# (Untested):

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);
        }
}

#2


4  

I would turn the problem around, and allow the last login at the expense of any earlier login, so whenever a user logs on, terminate any other login sessions he may have.

我会解决问题,并允许最后一次登录而不考虑任何早期登录,因此每当用户登录时,终止他可能拥有的任何其他登录会话。

This is much eaiser it implement, and you end up knowing where you are.

这实现了很多,你最终知道自己在哪里。

#3


4  

Having worked on a 'feature' like this be warned - this is an elephant-trap of edge cases where you end up thinking you have it nailed and then you or someone else says "but what if someone did X?" and you realise that you have to add another layer of complexity.

在这样的“特征”上工作时会被警告 - 这是一个大象陷阱的边缘情况,你最终认为你已经将它钉上了,然后你或其他人说“但如果有人做了X怎么办?”而且你意识到你必须增加另一层复杂性。

For example:

  • what if a user opens a new tab with a copy of the session?
  • 如果用户打开带有会话副本的新选项卡怎么办?

  • what if the user opens a new browser window?
  • 如果用户打开新的浏览器窗口怎么办?

  • what if the user logs in and their browser crashes?
  • 如果用户登录并且他们的浏览器崩溃了怎么办?

and so on...

等等...

Basically there are a range of more or less hacky solutions none of which are foolproof and all of which are going to be hard to maintain. Usually the real aim of the client is some other legitimate security goal such as 'stop users sharing accounts'.

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

The best idea is to find out what the underlying goal is and find a way of meeting that. And I'm afraid that involves negotiotion diplomacy and other such 'soft skills' rather than embarking on a technical wild goose chase..,

最好的想法是找出底层目标是什么,并找到满足这一目标的方法。而且我担心这涉及到谈判外交和其他类似的“软技能”,而不是开始进行技术性的疯狂追逐..,

#4


3  

Looking at just IP can be unreliable. IIRC there are some styles of proxy that farm outgoing requests randomly over multiple IP addresses. Depending on the scope of your application, this may or may not affect you. Other proxies will show heaps of traffic from a single IP.

仅仅考虑IP可能是不可靠的。 IIRC有一些代理样式可以通过多个IP地址随机区分传出请求。根据您的申请范围,这可能会或可能不会影响您。其他代理将显示来自单个IP的大量流量。

Last login time can also be an issue. Consider cookie based authentication where the authenticate cookies isn't persistent (a good thing). If the browser crashes or is closed, the user must log back in, but can't until the timeout expires. If the app is for trading stocks, 20 minutes of not working costs money and is probably unacceptable.

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

Usually smart firewalls / routers can be purchased that do a better job than either you or I can do as a one-off. They also help prevent replay attacks, cookie stealing, etc, and can be configured to run alongside standard mechanisms in your web platform of choice.

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

#5


1  

In a highly secure application, you may be required to do such. What you can do is keep a login count incrementing that for the user that logs in and the IP address. The count should never be 2. If it is then you log the other IP out and whomever it is logged into that IP gets thrown out. That wont prevent user-1 from giving his credentials to user-2, it will just make it frustrating for user-1 to do his work if user-2 logs in somewhere else at the same time.

在高度安全的应用程序中,您可能需要这样做。您可以做的是保持登录计数递增,登录用户和IP地址。计数永远不应该是2.如果是,则将其他IP记录下来,无论登录到哪个IP,IP都会被抛出。这不会阻止用户1将他的凭据提供给用户-2,如果用户2同时在其他地方登录,则只会让用户1做他的工作变得令人沮丧。

#6


1  

I've never found a standard solution to this problem. In one of my apps I used a combination of Javascript + Java to ensure that a user could be logged only once from a specified IP (actually it was a session ID), but in the worst case of functioning, for a timeout (set to 2 minutes) the account was not available. I don't why there is not a common way to do it.

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

#7


1  

I just had this problem.

我刚遇到这个问题。

We were building a Drupal site that contained a Flex app (built by the client), and he wanted the following:

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

  1. transparent login from Drupal<->Flex (bleh!)
  2. 从Drupal透明登录< - > Flex(bleh!)

  3. no concurrent logins!!
  4. 没有并发登录!!

He tested the crap out of every solution, and in the end, this is what we did:

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

  • We passed along the Session ID through every URL.
  • 我们通过每个URL传递会话ID。

  • When the user logged-in, we established an timestamp-IP-SessionID-Username footprint
  • 当用户登录时,我们建立了时间戳-IP-SessionID-Username足迹

  • Every page, the DB was pinged, and if the same user was found at the same IP w/a different SessionID, they older user was booted
  • 每个页面,数据库都被ping,如果在相同的IP上找到相同的用户,并且具有不同的SessionID,则他们的旧用户被启动

This solutions satisfied our client's rigorous testing (2 computers in his house..he kept us up for hours finding little nooks and crannies in the code before we came to this solution)

这个解决方案满足了我们客户的严格测试(他家里有两台电脑......在我们找到这个解决方案之前,他们让我们在代码中找到了一些小角落和缝隙)

#1


11  

I've implemented this by maintaining a hashtable of currently logged in users, the key was the username, the value was their last activity time.

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

When logging in, you just check this hashtable for the key, and if it exists, reject the login.

登录时,只需检查密钥的此哈希表,如果存在,则拒绝登录。

When the user does anything, you update the hashtable with the time (This is easy if you make it part of the core page framework).

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

If the time in the hashtable is greater than 20 minutes of inactivity, you remove them. You can do this every time the hashtable is checked, so even if you only had one user, and the tried to login several hours later, during that initial check, it would remove them from the hashtable for being idle.

如果散列表中的时间超过20分钟不活动,则将其删除。您可以在每次检查哈希表时执行此操作,因此即使您只有一个用户,并且尝试在几小时后登录,在初始检查期间,它也会将它们从哈希表中删除以便空闲。

Some examples in C# (Untested):

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);
        }
}

#2


4  

I would turn the problem around, and allow the last login at the expense of any earlier login, so whenever a user logs on, terminate any other login sessions he may have.

我会解决问题,并允许最后一次登录而不考虑任何早期登录,因此每当用户登录时,终止他可能拥有的任何其他登录会话。

This is much eaiser it implement, and you end up knowing where you are.

这实现了很多,你最终知道自己在哪里。

#3


4  

Having worked on a 'feature' like this be warned - this is an elephant-trap of edge cases where you end up thinking you have it nailed and then you or someone else says "but what if someone did X?" and you realise that you have to add another layer of complexity.

在这样的“特征”上工作时会被警告 - 这是一个大象陷阱的边缘情况,你最终认为你已经将它钉上了,然后你或其他人说“但如果有人做了X怎么办?”而且你意识到你必须增加另一层复杂性。

For example:

  • what if a user opens a new tab with a copy of the session?
  • 如果用户打开带有会话副本的新选项卡怎么办?

  • what if the user opens a new browser window?
  • 如果用户打开新的浏览器窗口怎么办?

  • what if the user logs in and their browser crashes?
  • 如果用户登录并且他们的浏览器崩溃了怎么办?

and so on...

等等...

Basically there are a range of more or less hacky solutions none of which are foolproof and all of which are going to be hard to maintain. Usually the real aim of the client is some other legitimate security goal such as 'stop users sharing accounts'.

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

The best idea is to find out what the underlying goal is and find a way of meeting that. And I'm afraid that involves negotiotion diplomacy and other such 'soft skills' rather than embarking on a technical wild goose chase..,

最好的想法是找出底层目标是什么,并找到满足这一目标的方法。而且我担心这涉及到谈判外交和其他类似的“软技能”,而不是开始进行技术性的疯狂追逐..,

#4


3  

Looking at just IP can be unreliable. IIRC there are some styles of proxy that farm outgoing requests randomly over multiple IP addresses. Depending on the scope of your application, this may or may not affect you. Other proxies will show heaps of traffic from a single IP.

仅仅考虑IP可能是不可靠的。 IIRC有一些代理样式可以通过多个IP地址随机区分传出请求。根据您的申请范围,这可能会或可能不会影响您。其他代理将显示来自单个IP的大量流量。

Last login time can also be an issue. Consider cookie based authentication where the authenticate cookies isn't persistent (a good thing). If the browser crashes or is closed, the user must log back in, but can't until the timeout expires. If the app is for trading stocks, 20 minutes of not working costs money and is probably unacceptable.

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

Usually smart firewalls / routers can be purchased that do a better job than either you or I can do as a one-off. They also help prevent replay attacks, cookie stealing, etc, and can be configured to run alongside standard mechanisms in your web platform of choice.

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

#5


1  

In a highly secure application, you may be required to do such. What you can do is keep a login count incrementing that for the user that logs in and the IP address. The count should never be 2. If it is then you log the other IP out and whomever it is logged into that IP gets thrown out. That wont prevent user-1 from giving his credentials to user-2, it will just make it frustrating for user-1 to do his work if user-2 logs in somewhere else at the same time.

在高度安全的应用程序中,您可能需要这样做。您可以做的是保持登录计数递增,登录用户和IP地址。计数永远不应该是2.如果是,则将其他IP记录下来,无论登录到哪个IP,IP都会被抛出。这不会阻止用户1将他的凭据提供给用户-2,如果用户2同时在其他地方登录,则只会让用户1做他的工作变得令人沮丧。

#6


1  

I've never found a standard solution to this problem. In one of my apps I used a combination of Javascript + Java to ensure that a user could be logged only once from a specified IP (actually it was a session ID), but in the worst case of functioning, for a timeout (set to 2 minutes) the account was not available. I don't why there is not a common way to do it.

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

#7


1  

I just had this problem.

我刚遇到这个问题。

We were building a Drupal site that contained a Flex app (built by the client), and he wanted the following:

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

  1. transparent login from Drupal<->Flex (bleh!)
  2. 从Drupal透明登录< - > Flex(bleh!)

  3. no concurrent logins!!
  4. 没有并发登录!!

He tested the crap out of every solution, and in the end, this is what we did:

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

  • We passed along the Session ID through every URL.
  • 我们通过每个URL传递会话ID。

  • When the user logged-in, we established an timestamp-IP-SessionID-Username footprint
  • 当用户登录时,我们建立了时间戳-IP-SessionID-Username足迹

  • Every page, the DB was pinged, and if the same user was found at the same IP w/a different SessionID, they older user was booted
  • 每个页面,数据库都被ping,如果在相同的IP上找到相同的用户,并且具有不同的SessionID,则他们的旧用户被启动

This solutions satisfied our client's rigorous testing (2 computers in his house..he kept us up for hours finding little nooks and crannies in the code before we came to this solution)

这个解决方案满足了我们客户的严格测试(他家里有两台电脑......在我们找到这个解决方案之前,他们让我们在代码中找到了一些小角落和缝隙)