اندازهی قلم متن
تخمین مدت زمان مطالعهی مطلب:
سه دقیقه
سناریویی را در نظر بگیرید که در آن یک برنامهی چت را با استفاده از SignalR نوشتهاید و قصد دارید از آن در یک سایت شلوغ استفاده کنید. در حالت عادی برنامه به خوبی کار میکند؛ تا زمانیکه کسی شروع به ارسال Spam در سیستم چت شما نکرده باشد. برای کنترل ارسال spam، اولین راهی که به ذهن میرسد این است که سمت کلاینت کلید ارسال پیغام را چند ثانیه غیر فعال کنیم تا کاربران نتوانند در عرض به طور مثال 3 ثانیه، تعداد زیادی پیام را ارسال کنند. برای کاربران معمولی وب سایت ما این راه حل جواب میدهد و مشکلی نیست. ولی اگر کاربر وبسایت آشنایی مختصری با JavaScript و نحوهی اتصال به signalR داشته باشد چطور؟ خیلی ساده میتواند در کنسول browser خود یک لوپ نوشته و شروع به ارسال spam به برنامهی چت ما کند. اینجاست که مجبوریم به علاوهی کنترل سمت کلاینت، سمت سرور هم زمان ارسال پیامها را کنترل کنیم که در ادامه به یکی از روشهای انجام این کار میپردازیم.
برای شروع کار ابتدا نیاز به کلاسی داریم که اطلاعات فعالیتهای کانکشنها را برای ما نگهداری کند:
سپس برای اینکه بتوانیم تمامی درخواستهای ارسالی از سمت کلاینتها را مدیریت کنیم، به یک کلاس از نوع HubPipelineModule احتیاج داریم که ما در اینجا با استفاده از کلاس SpamDetectionPiplelineModule که از HubPipelineModule مشتق شده، این کار را انجام میدهیم.
در کدهای بالا ابتدا یک HashSet را جهت نگه داشتن فعالیت کاربران ایجاد کردیم که بعد از هر درخواستی که به سمت سرور ارسال میشود، بهروز خواهد شد. متد OnBeforeIncoming قبل از اینکه درخواست کاربر به Hub برنامه برسد، اجرا میشود که میتوانیم با استفاده از آن، فعالیتهای کانکشن کاربر را در طی چند ثانیهی اخیر کنترل کنیم که برای مثال چه تعدادی درخواست را در طی 3 ثانیهی قبل ارسال کرده است. اگر تعداد درخواستهای ارسالی در طی 3 ثانیهی قبل، بیشتر از تعداد مورد نظر ما بود، با بازگشت دادن False، از اجرای درخواست آن جلوگیری میکنیم. (در اینجا ما کاربران را به ارسال 2 پیام در طی هر 3 ثانیه، محدود کردهایم).
کد اولیهی هاب چت برنامه:
<HubName("chat")> Public Class ChatHub Inherits Hub Public Sub sendMessage(msg As String) Clients.All.getMessage(msg) 'Call getMessage function client side End Sub End Class
Public Class ActivityInfo Sub New(connectionId As String) Me.ConnectionID = connectionId Me.Time = Now End Sub Public Property ConnectionID As String Public Property Time As DateTime End Class
Public Class SpamDetectionPiplelineModule Inherits HubPipelineModule Public Property SpamDetection As New HashSet(Of ActivityInfo) Private _SpamDetectionLock As New Object Public Function IsSpam(ConnectionId As String) As Boolean SyncLock _SpamDetectionLock 'Remove all old info before 3 seconds ago SpamDetection.RemoveWhere(Function(q) q.Time < Now.AddSeconds(-3)) SpamDetection.Add(New ActivityInfo(ConnectionId)) 'Check activities from 3 seconds ago If SpamDetection.Where(Function(q) q.ConnectionID = ConnectionId).Count > 2 Then Return True Else Return False End If End SyncLock End Function Protected Overrides Function OnBeforeIncoming(context As IHubIncomingInvokerContext) As Boolean If IsSpam(context.Hub.Context.ConnectionId) Then Return False End If Return MyBase.OnBeforeIncoming(context) End Function End Class
حال برای استفادهی از PipelineModule سفارشی خودمان، باید آن را قبل از مپ کردن SignaR، در StartUp برنامه اضافه کنیم:
Public Class Startup Public Sub Configuration(app As IAppBuilder) GlobalHost.HubPipeline.AddModule(New SpamDetectionPiplelineModule) app.MapSignalR End Sub End Class