如何在C#中解决这个时间段问题

时间:2012-11-02 16:53:32

标签: c# asp.net

我正在尝试解决此业务问题:

  • 用户每5分钟登录10次
  • 如果用户超过10次尝试,我会显示“您需要等待 登录“消息
  • 一旦经过5分钟,我需要重置尝试次数 让用户多尝试10次。

我想在不使用Timer的情况下执行此操作,并且我将大部分信息存储在Session

public class LoginExp
{
  public DateTime FirstAttempt;
  public int NumOfAttempts;
}

我将FirstAttempt日期时间存储在页面加载上。

在“登录”按钮上单击:

  • 我增加NumOfAttempts
  • 我需要检查NumOfAttempts< 10 相同的5分钟时段。我可以获得FirstAttemptDateTime.Now之间经过的分钟数,并执行5的模式,但这不会告诉我这是哪个时间段(换句话说,用户可能已尝试3从第2分钟开始,然后在第7分钟回来再次尝试。mod会给我相同的价值。

关于我如何做到这一点的任何想法?

5 个答案:

答案 0 :(得分:6)

请不要使用计时器。为这些数据和用户数据腾出空间,保存,存储某种形式的凭据。只需保留上次登录尝试时间和尝试次数 - 如果时间大于 n ,则下次尝试将清除之前的计数并从0开始,依此类推。

答案 1 :(得分:2)

您可以存储尝试时间戳列表,并使用它来确定用户是否被锁定。这不是完整的代码,但这是基本的想法:

// Store a list of attempts
var attempts = new List<DateTime>();

// Determine if 10 or more attempts have been made in the last 5 minutes
bool lockout = attempts.Where(a => (DateTime.Now - a).TotalMinutes <= 5).Count() >= 10;

// Register an attempt
attempts.Add(DateTime.Now);

// Remove attempts older than 5 minutes
attempts.Where(a => (DateTime.Now - a).TotalMinutes > 5).ToList()
    .ForEach(a => attempts.Remove(a));

您还可以将尝试存储在数据库中,这样可以为您提供安全性的papertrail。同样的方法适用 - 计算不到5分钟的记录。

答案 2 :(得分:1)

答案取决于您如何解释要求。也就是说,5分钟的时间框架是否为滑动尺度?

严格应用该要求将要求您为登录尝试创建数据库表,每条记录包括用户记录的密钥和登录尝试的时间戳。然后,对于每次尝试,执行类似

的查询
delete from login_attempts where user=[user] and attempt_time<[now - 5 minutes]
select count(*) from login_attempts where user=[user]

(括号中的值被替换为合适的值,无论您是使用预备陈述还是其他任何内容)

然后,如果返回的计数&gt; 10,你阻止了这次尝试。

(我建议每次都删除旧的尝试以保持桌子清洁。当然,替代方法是可能的。)

如果不需要严格执行要求并且您不想创建新表,则可以通过例如将最后尝试时间和计数添加到用户记录来简化它。然后在每次尝试(伪代码在这里......):

read last_attempt and attempt_count
if last_attempt < now - 5 minutes then
  attempt_count=0
endif
if attempt_count>10 then
  display error
else if attempt_fails then
  last_attempt=now
  attempt_count=attempt_count+1
endif

这不会严格符合规定的要求:如果用户每4分钟尝试一次,那么在40分钟后我们会说他有10次不良尝试,而实际上只有一次超过5分钟。但它会更简单,然后创建一个新的表,并实现我认为的目标,阻止某人通过蛮力破解密码。

答案 3 :(得分:0)

要实现滑动的5分钟窗口,您需要知道每次尝试的时间,而不仅仅是第一次或最后一次。

每次登录尝试都存储在数据库中。正如评论中提到的那样,只是简单地使用会话缓存。

username     | time
mr.mindor    | 00:00:00
mr.mindor    | 00:01:00

等。 然后对于每次登录尝试检查有10次或更少的尝试...

Select count(*) 
from LOGIN_ATTEMPTS 
  where username = @current_user 
  AND time > DATEADD(m,-5,GetDate())

您可以无限期地保留LOGIN_ATTEMPTS用于安全/审核目的,或定期清除,或成功登录。

仅存储第一次或最后一次和计数的问题。
没有每次,您无法确定尝试的分布。

基于第一次尝试的解决方案:

只需第一次尝试时间和计数,您可以在第一次尝试后的5分钟内防止超过10次尝试,但在任何给定的5分钟间隔内不会阻止超过10次尝试。
例如:你可以在0:00和9在4:59获得1,并在几秒钟内在5:01获得10以上19。

attempt_time | record
0:00         | {0:00, 1}
4:59         | {0:00, 2}
4:59         | {0:00, 3}
4:59         | {0:00, 4}
4:59         | {0:00, 5}
4:59         | {0:00, 6}
4:59         | {0:00, 7}
4:59         | {0:00, 8}
4:59         | {0:00, 9}
4:59         | {0:00, 10}  
5:01         | {5:00, 1}  // 10 since 4:59
5:02         | {5:00, 2}  // 11 since 4:59

基于最后尝试的解决方案:
只保存最后一次尝试时间和计数,您可以在任何5分钟间隔内防止超过10次尝试,但实际上它更具限制性。在前一次尝试的5分钟内,每次尝试都会超过10次 示例:如果每4分钟尝试一次,您的计数器将永远不会重置。

attempt_time | record
0:00         | {0:00, 1}
4:00         | {4:00, 2}  // 2 in last 5 minutes
8:00         | {8:00, 3}  // 2 in last 5 minutes
...
40:00        | {40:00,11} // blocked but only 2 in last 5 minutes.

答案 4 :(得分:0)

这里的计时器太丑了。 只需保留某种Attempts表。 存储UserId,尝试时间和成功/失败标志。

从那里逻辑是自我解释的。