反爬虫:利用ASP.NET MVC的Filter和缓存(入坑出坑)

 背景介绍

 

为了平衡社区成员的贡献和索取,

但这样就产生了一个问题,因为这个“帮帮币”是可以买卖有价值的,所以难免会有恶意用户用爬虫不断的扫描,导致这样的情况出现:

注:经核实,

 

细节设计

 

一个最核心的问题是:用什么来记录用户的访问频次

数据库?感觉没必要,这个数据又不需要长期保留,访问一次就做一次I/O操作在性能上接受不了,所以我们决定使用内存。

但是,具体需要记录那些数据,又用什么样的数据结构呢?

最后我们选择使用缓存,记录最简单的“用户ID -> 访问次数”键值对,来解决这个问题,因为:

当然,这里其实还是有那么点问题的。比如,假设缓存时间是5分钟,最多访问次数是10次。0:10,开始缓存访问次数,一直累加,到0:14,共记录访问次数7次,没有问题;然而,一过0:15,缓存被清空,0:16的时候,缓存里只有0:15到0:16这一分钟的数据,没有过去5分钟(从0:11到0:16)的数据。所以用户可以控制一直爬虫,访问9次,然后就歇着,5分钟过后,再继续访问9次,然后再歇5分钟……

唉~~真这么拼,我还真没什么办法?但如果这么一个频次他能接受的话,我其实也无所谓,你就慢慢爬呗。或者,我们后台做更大的监控,把每个用户的每次访问都记录下来,进行统计,找出异常。那时候可能就真的需要数据库了(为了提高性能可以内存里放一个DataTable,定时同步到Database)。但暂时来说,没有这个必要。

 

此外,还有一个问题,是不是只需要记录用户访问频次?

如果按上述方案,在缓存里记录访问频次,通过缓存数据来判断是否允许继续访问,会有一个问题:缓存到期失效之后,这个用户就又可以自由访问目标页面了!相当于到期自动解锁。

我觉得这还是不科学,如果认定是爬虫,只能是人工解锁(识别码验证)。所以在数据库用户表里添加一个“已锁定”(Locked)字段,如果用户被锁定,Update其为当前时间;未锁定时(解锁后)为NULL。

 

具体实现

 

为了重用,我们需要利用 Authorize Fitler,在它的OnAuthorization()方法里面进行检查和记录。

代码本身应该比较简单,if...else...的逻辑:

复制代码
            ///1. 先根据数据库捡查当前用户是否被锁定             ///2. 如果被锁定,直接拦截。否则:             ///3. 在缓存中检查有无当前用户的访问次数记录             ///     3.1 没有,新建一条他的缓存。否则:             ///     3.2 检查该用户已访问次数             ///         3.2.1 如果已到达访问次数限制,拦截并在数据库中锁定该用户。否则             ///         3.2.2 累加用户的访问次数
复制代码

 

精简注释代码如下:
复制代码
    public class NeedLogOn : AuthorizeAttribute     {         public override void OnAuthorization(AuthorizationContext filterContext)         {             HttpContextBase context = filterContext.HttpContext;              ///Autofac相关操作,获取正取的ISharedService实例            ISharedService service = AutofacConfig.Container.Resolve<ISharedService>();             _NavigatorModel model = service.Get();  //从数据库获取当前User的信息            ///截断式编程,减少if...else的{}嵌套            if (model.Locked.HasValue)             {                 ///model.Locked 来自数据库,用户已经被锁定,拦截                visitTooMuch(filterContext);                 return;             }              string cacheKey = CacheKey.MAX_VISIT + model.Id;              ///非常有意思,不能直接使用int值类型,必须使用引用类型的            VisitCounter amount;             if (context.Cache[cacheKey] == null)             {                 amount = new VisitCounter { Value = 1 };                 ///新建立一条Cache                context.Cache.Add(cacheKey, amount, null,                     DateTime.Now.AddSeconds(Config.Seconds),                     Cache.NoSlidingExpiration, CacheItemPriority.Normal, null
                        
关键字:
50000+
5万行代码练就真实本领
17年
创办于2008年老牌培训机构
1000+
合作企业
98%
就业率

联系我们

电话咨询

0532-85025005

扫码添加微信