【问题标题】:Prevent calling a web service too many times防止多次调用 Web 服务
【发布时间】:2012-07-16 07:48:06
【问题描述】:

我为我的客户提供了一个 Web 服务,允许他将记录添加到生产数据库中。

我最近发生了一个事件,我的客户的程序员循环调用服务,迭代调用我的服务数千​​次。

我的问题是防止这种事情发生的最好方法是什么。

我想到了一些方法: 1.在服务入口处,我可以为每个调用服务的客户端更新计数器,但这看起来太笨拙了。 2.检查调用该服务的客户端的IP,每次调用该服务的时候都会升一个flag,然后每隔一小时重置一次flag。

我很肯定有更好的方法并且会采纳任何建议。

谢谢,大卫

【问题讨论】:

  • 另一个想法,更新这个用户的会话:
  • 我想你有某种“批量”添加方法?

标签: c# asp.net web-services


【解决方案1】:

方法是在会话中存储一个计数器,并使用该计数器来防止每次调用过多。

但是,如果您的用户可能会尝试避免这种情况并每次发送不同的 cookie*,那么您需要创建一个自定义表,其行为类似于会话,但将用户与 ip 连接,而不是与 cookie。

这里还有一个问题是,如果您在 ip 上阻止 basic,您可能会阻止整个公司从代理中出来。所以最后一个正确但更复杂的方法是让ip和cookie都与用户连接并知道浏览器是否允许cookie。如果没有,那么你用 ip 阻止。这里的困难部分是了解 cookie。 您可以在每次通话时强制他发送与现有会话相关的有效 cookie。如果没有,则浏览器没有 cookie。

[ * ] cookie 与会话连接。
[ * ] 通过创建新表来保持计数器并与会话断开连接,您还可以避免会话锁定。

过去我使用了用于 DosAttack 的代码,但是当您有很多池和困难的应用程序时,它们都不能正常工作,所以我现在使用我描述的自定义表。这是我测试和使用的两个代码

Dos attacks in your web app

Block Dos attacks easily on asp.net

如何查找保存在表格中的每秒点击次数。这是我的 SQL 计算每秒点击次数的部分。诀窍之一是,如果距离最后一次检查还有 6 秒或更多秒,我会继续添加点击次数并计算平均值。这是从计算中截取的代码作为一个想法

set @cDos_TotalCalls = @cDos_TotalCalls + @NewCallsCounter

SET @cMilSecDif = ABS(DATEDIFF(millisecond, @FirstDate, @UtpNow))

-- I left 6sec diferent to make the calculation
IF @cMilSecDif > 6000
    SET @cClickPerSeconds = (@cDos_TotalCalls * 1000 / @cMilSecDif)
else
    SET @cClickPerSeconds = 0

IF @cMilSecDif > 30000
    UPDATE ATMP_LiveUserInfo SET cDos_TotalCalls = @NewCallsCounter, cDos_TotalCallsChecksOn = @UtpNow WHERE cLiveUsersID=@cLiveUsersID         
ELSE IF @cMilSecDif > 16000
    UPDATE ATMP_LiveUserInfo SET cDos_TotalCalls = (cDos_TotalCalls / 2), 
    cDos_TotalCallsChecksOn = DATEADD(millisecond, @cMilSecDif / 2, cDos_TotalCallsChecksOn)
        WHERE cLiveUsersID=@cLiveUsersID

【讨论】:

  • 感谢您的回答,可以说我更新了会话: Session["ServiceInvoked"]=true;并询问我们是否在会话开始时询问: if((bool)Session["ServiceInvoked"]) ... 我会阻止通话。但是,我将如何在一个小时后重置它?
  • 如果我要插入日期时间,我可以工作,因为我可以检查会话日期时间 订阅 DateTime.Now,并允许调用服务......
  • @DavidRasuli 您将上次更新的 DateTime 字段与计数器一起使用并进行平均计算 - 例如,您在最后一个 DateTime 上看到最后一次询问是在 1 秒之前并且一切准备就绪10 个电话。换句话说,你为最后一次通话剪掉了它,而不是现在。
【解决方案2】:

首先,您需要了解您的情况的法律方面:与客户的合同是否允许您限制客户的访问?

这个问题超出了 SO 的范围,但你必须想办法回答它。因为如果您在法律上有义务处理所有请求,那么就没有办法绕过它。此外,对您的情况的法律分析可能已经包含一些限制,您可以通过这种方式限制访问。这反过来又会对您的解决方案产生影响。

除了所有这些问题,只关注技术方面,您是否使用某种用户身份验证? (如果不是,为什么不呢?)如果你这样做,你可以在每个用户群上实施你决定使用的任何方案,我认为这将是最干净的解决方案(你不需要依赖 IP 地址,这是一个某种丑陋的解决方法)。

一旦您有了识别单个用户的方法,您就可以实施多个限制。我首先想到的是这些:

  1. 同步处理
    只有在处理完所有先前的请求后才开始处理请求。这甚至可以在您的主要处理方法中仅使用lock 语句来实现。如果你选择这种方法,
  2. 处理请求之间的时间延迟
    要求在一个处理调用之后必须经过特定时间才能允许下一个调用。最简单的解决方案是在用户会话中存储LastProcessed 时间戳。如果您采用这种方法,您需要开始考虑在允许处理新请求之前如何响应 - 您是否向调用者发送错误消息?我认为你应该...

编辑
lock 声明,简要说明:

它旨在用于线程安全操作。语法如下:

lock(lockObject)
{
    // do stuff
}

lockObject 需要是一个对象,通常是当前类的私有成员。效果是,如果您有 2 个线程都想执行此代码,则第一个到达 lock 语句的线程将锁定 lockObject。虽然它确实是,但第二个线程无法获取锁,因为对象已经被锁定。所以它只是坐在那里等待,直到第一个线程在} 退出块时释放锁。只有这样,第二个线程才能锁定 lockObject 并执行此操作,阻止 lockObject 以阻止任何第三个线程出现,直到它也退出该块。

小心,线程安全的整个问题绝非微不足道。 (可以说,唯一无关紧要的是程序员可能犯的许多琐碎错误;-) See here 介绍 C# 中的线程

【讨论】:

【解决方案3】:

使用web服务后获取用户ip并将其插入缓存一小时,缓存在服务器上:

HttpContext.Current.Cache.Insert("UserIp", true,  null,DateTime.Now.AddHours(1),System.Web.Caching.Cache.NoSlidingExpiration);

当您需要检查用户是否在过去一小时内输入时:

if(HttpContext.Current.Cache["UserIp"] != null)
{
 //means user entered in last hour
}

【讨论】:

    猜你喜欢
    • 2020-02-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-10-14
    • 2016-03-08
    • 2014-05-21
    • 1970-01-01
    • 2015-03-04
    相关资源
    最近更新 更多