نظرات مطالب
پیاده سازی یک سیستم دسترسی Role Based در Web API و AngularJs - بخش سوم (پایانی)
در پاسخ به سوال شما باید عرض کنم که حرف شما کاملا درست است. به این دلیل که با هر فراخوانی از سمت کلاینت لیست permissionهای کاربر واکشی میشود. برای کم کردن این فراخوانی‌ها و واکشی‌ها ما چند راه در پیش رو خواهیم داشت.
1. استفاده از  caching اولین راه حل و ساده‌ترین راه حل هست. به این صورت که در هنگامی که کاربر وارد برنامه میشود تمامی دسترسی‌های او در cache ذخیره شود. برای این کار نیاز به نوشتن سرویسی برای مدیریت این حافظه است. فرض کنید در این حین دسترسی نقشی که کاربر در آن نقش است تغییر کند. بنابراین باید تمامی cache از نو ایجاد شود. توجه کنید که ما نمیتوانیم در معماری REST و Web API از Session استفاده کنیم. به این دلیل که فرایندها در این معماری Stateless هستند، به این معنی که چیزی را در خود نگه نمیدارند.
2. روش دیگر استفاده از Claim در Asp.Net Identity 2 است. در سیستم Identity جدید Asp شما قادر هستید چندین Claim برای کاربر تنظیم (set) کنید. برای این کار میتوانید مقاله ای در این زمینه را از سایت bitOfTech مطالعه کنید. در این مجموعه  مقالات تشریح شده است که چگونه میتوان Claim‌های شخصی سازی شده در سیستم تعریف کرد. در نهایت این Claimها hash شده و در Token کاربر ذخیره میشوند. توصیه میکنم اگر تعداد دسترسی‌های شما در هر نقش زیاد نیست میتوانید از این روش استفاده کنید. البته این را مد نظر داشته باشید در این مقالات نحوه Authorize کردن کاربر با آنچه ما در این مجموعه مقالات تشریح کردیم کمی متفاوت است. 
نظرات مطالب
معرفی JSON Web Token
خیلی ممنون از مطلب مفیدتون.
روش token base authentication روش بسیار خوبی برای جایگزین شدن با روش‌های پیشین است، به خصوص در برنامه‌های تک صفحه ای. سوالی که برای بنده مطرح شد. بنده در پروژه ای token و اطلاعات تکمیلی کاربر را در Local Storage ذخیره میکنم. حال اینکه وقتی token به کاربر داده شد، اگر اطلاعات را کپی کنم و در مرورگر دیگری این اطلاعات را وارد کنم من را تایید صلاحیت میکنه. در صورتی که این نکته میتونه خیلی خطر ساز باشه، چون token که به کاربر اعطا میشه کلید refresh token را هم به همراه خودش داره و در نهایت کاربر تا هر زمانی که بخواد میتونه لاگین شده باقی بمونه. ممنون میشم اگر راه حلی پیشنهاد بدید به بنده.
یک سوالی در مورد عملکرد برنامه تلگرام هم دیده بودم، در تلگرام هم فکر میکنم از token استفاده میشه. اما خیلی جالبه که شما میتونید sessionهای فعالتون را ببینید که از چند دستگاه لاگین شده اید. آیا اگر اطلاعات token را در دیتابیس نگه دارم میتونم مثل تلگرام این وضعیت را هم کنترل کنم؟ مثلا آیا میتونم از سمت سرور یک token که تایید صلاحیت هست را expire کنم؟
مطالب
چک لیست امنیتی پروژه های نرم افزاری تحت وب
 مقدمه:

امروزه یکی از بزرگترین دغدغه‌های فعالان حوزه آی تی، برقراری امنیت اطلاعات می‌باشد. با پدید آمدن بانک‌های داده‌ای آماری و مالی، حساسیت مسئله صد چندان می‌شود. در ادامه چک لیستی را ارائه می‌نمایم که با کمک آن می‌توانید تا حدود بسیار خوبی امنیت نرم افزار تحت وب خود را برقرار نمایید. در برخی از موارد مثال‌هایی از تکنولوژی مایکروسافت آورده شده است که این بدلیل تخصص نویسنده در تکنولوژی‌های مایکروسافت می‌باشد. در صورتیکه شما از تکنولوژی‌ها و زبان‌های سورس باز بهره می‌برید، می‌بایست معادل مورد ذکر شده را در زبان مورد استفاده خود بیابید .
ابتدا اجازه دهید مقداری با حملات آشنا شویم و سپس راه مقابله را در کنار هم بررسی نماییم.
 

مهمترین و خطرناک‌ترین حملات سطح وب :

حمله XSS

این نوع حملات بدین صورت است که هکر با استفاده از فرم‌های عمومی یا خصوصی (پنل‌های سایت) اقدام به ثبت کدهای مخرب جاوااسکریپت درون دیتابیس شما می‌نماید. همانطور که می‌دانید پایه اصلی سیستم‌های احراز هویت، ساخت فایل کوکی بر روی کامپیوتر کاربران می‌باشد. زمانی که مطلب ثبت شده‌ی هکر برای کاربران شما نمایش داده می‌شود، کدهای جاوا اسکریپت هکر روی مرورگر کاربر، اجرا شده و اطلاعات کوکی‌های کاربر به راحتی برای سایت هکر ارسال می‌شود (معمولا هکر یک صفحه روی وب می‌سازد تا بتواند اطلاعات دریافتی از کدهای جاوا اسکریپت خود را دریافت و در جایی ذخیره کند).
حال هکر به راحتی کوکی را بر روی مرورگر خودش تنظیم می‌کند و بعد وارد سایت شما می‌شود. سیستم شما او را با کاربر شما اشتباه می‌گیرد و به راحتی هکر به اطلاعات پنل کاربری کاربر(ان) شما دست پیدا می‌کند.
 

حمله SQL Injection

این حمله معروفترین حمله است که تقریبا با قدرت می‌توانم بگویم که درتکنولوژی ASP.Net با امکانات فوق العاده‌ای که بصورت توکار در دات نت در نظر گرفته شده است، بصورت کامل به فراموشی سپرده شده است. فقط 2 تا نکته‌ی ریز هست که باید در کدهایتان رعایت کنید و تمام.
این حمله بدین صورت است که هکر یک سری دستورات SQL را در کوئری استرینگ، به صفحات تزریق می‌کند و بدین صورت می‌تواند در کدهای کوئری TSQL شما اختلال ایجاد کند و اطلاعات جداول شما را بدست بیاورد. در این نوع حمله، هکر از طریق باگ سطح کد نویسی کدهای نرم افزار، به دیتابیس حمله می‌کند و اطلاعاتی مثل نام کاربری و کلمه‌ی عبور ادمین یا کاربران را می‌دزد و بعد می‌رود داخل پنل و خرابکاری می‌کند.
 

حمله CSRF

این حمله یکی از جالب‌ترین و جذاب‌ترین نوع حملات است که هوش بالای دوستان هکر را نشون می‌دهد. عبارت CSRF مخفف Cross Site Request Forgery است (احتمالا دوستان ام وی سی کار، این عبارت برایشان آشناست).
در این نوع حمله هکر یک فایل برای کاربر شما از طریق ایمیل یا روش‌های دیگر ارسال می‌کند و کاربر را به این سمت سوق می‌دهد که فایل را باز کند. کاربر یک فایل به ظاهر معمولی مثل عکس یا ... را می‌بیند و فایل را باز می‌کند. وقتی فایل باز می‌شود دیتای خاصی دیده نمی‌شود و گاهی هم اروری مبنی بر ناقص بودن فایل یا ... به کاربر نمایش داده می‌شود و کاربر فکر می‌کند که فایل، ناقص برای ارسال شده ...
اما در حقیقت با کلیک بر روی فایل و باز کردن آن یک درخواست POST از کامپیوتر کاربر برای سایت شما ارسال می‌شود و در صورتیکه کاربر در آن زمان در سایت شما لاگین باشد، سایت درخواست را با روی باز می‌پذیرد و درخواست را اجرا می‌کند. بدین صورت هکر می‌تواند درخواست‌هایی را به سرویس‌های سایت شما که مثلا برای حذف یک سری داده است، ارسال کند و اطلاعات کاربر را حذف کند.
 

حمله Brute Force

در این حمله، هکر از یک سری برنامه برای ارسال درخواست‌های مکرر به فرم‌های سایت شما استفاده می‌کند و بدین صورت فرم‌های عمومی سایت شما مورد حجوم انبوهی از درخواست‌ها قرار می‌گیرد که این امر در بهترین حالت موجب ثبت کلی دیتای اسپم در دیتابیس شما و در بدترین حالت موجب داون شدن سایت شما می‌شود.
 

حمله DDOS

این نوع حمله مانند حمله Brute Force است؛ با این تفاوت که درخواست به همه‌ی صفحات شما ارسال می‌شود و معمولا درخواست‌ها از چندین سرور مختلف برای سایت شما ارسال می‌شوند و حجم درخواست‌ها به قدری زیاد است که عملا سرور شما هنگ می‌کند و کاملا از دسترس خارج می‌شود. این نوع حمله در سطح کد راه حل زیادی ندارد و در سطح سرور و فایروال باید حل شود و حل آن هم بدین صورت است که درخواست‌های بیش از حد طبیعی از یک آی پی خاص تشخیص داده شده و به سرعت، آی پی بلاک می‌شود و از آن به بعد درخواست‌های آن آی پی در فایروال از بین می‌رود و دیگه به سرور نمیرسد.


حمله SHELL

شل فایلی است خطرناک که اگر بر روی سرور سایت شما آپلود و اجرا شود، هکر از طریق آن دسترسی کاملی به کل سرور سایت شما خواهد داشت. فایل‌های دیگری با نام بک‌دور [1] نیز وجود دارند که نویسنده تمایل دارد آنها را نیز از نوع حمله SHELL معرفی نماید. این نوع از فایل‌ها به مراتب بسیار خطرناک‌تر از فایل‌های شل می‌باشند؛ تا جایی که ممکن است سال‌ها هکر به سروی دسترسی داشته باشد و مدیر سرور کاملا از آن بی خبر باشد. اینجاست که باید شدیدا مراقب فایل‌هایی که روی سایت شما آپلود می‌شوند باشید. نویسنده به تمامی خوانندگان پیشنهاد می‌نماید، در صورتیکه نرم افزار حساسی دارند، حتما از سرور اختصاصی استفاده نمایند؛ چرا که در هاست‌های اشتراکی که در آنها فضا و امکانات یک سرور بصورت اشتراکی در اختیار چندین سایت قرار می‌گیرد، وجود باگ امنیتی در سایر سایت‌های موجود بر روی سرور اشتراکی می‌تواند امنیت سایت شما را نیز به مخاطره بیاندازد. نویسنده تهیه‌ی سرور اختصاصی را شدیدا به توسعه دهندگان سایت‌های دارای تراکنش‌های بانکی بالا (داخلی یا خارجی) پیشنهاد می‌نماید. زیرا درگاه تراکنش‌های بانکی بر روی آی پی هاست شما قفل می‌شوند و در صورتیکه سرور بصورت اختصاصی تهیه شده باشد، آی پی سرور شما فقط و فقط در اختیار شماست و هکر نمی‌تواند با تهیه هاستی بر روی سرور اشتراکی شما، به راحتی آی پی قفل شده در درگاه بانکی شما را در اختیار داشته باشد. بدیهی است تنها در اختیار داشتن آی پی سرور شما جهت انجام خرابکاری در درگاه بانکی شما کافی نیست. ولی به نظر نویسنده این مورد در بدترین حالت ممکن 30% کار هکر می‌باشد. البته بحث حمله شل به سطح مهارت متخصصان سرورها نیز بستگی دارد. نویسنده اظهار می‌دارد اطلاعات دقیقی از تنظیماتی که بتواند جلوی اجرای انواع شل و یا جلوی دسترسی فایل‌های شل را بگیرد، ندارد. بنابراین از متخصصان این حوزه دعویت می‌نماید اطلاعاتی درباره این موضوع ارائه نمایند.
 

حمله SNIFF

در این نوع حملات، هکر پکت‌های رد و بدل شده‌ی بین کاربران و سرور شما را شنود می‌نماید و به راحتی می‌تواند اطلاعات مهمی مثل نام کاربری و رمز عبور کاربران شما را بدست آورد.



چک لیست امنیتی پروژه‌های نرم افزاری تحت وب

- بررسی کامل ورودی‌های دریافتی از فرم‌های سایت؛ هم در سمت کلاینت و هم در سطح سرور .
- در تکنولوژی دات نت به منظور تمیز سازی ورودی‌ها و حذف تگهای خطرناکی همچون تگ script، کتابخانه‌ای با نام Microsoft.Security.Application وجود دارد. کتابخانه‌های سورس باز دیگری نیز وجود دارند که نمونه آن کتابخانه AntiXss [2] سایت نوگت [3] می‌باشد.
- بررسی کامل ورودی‌های دریافتی از کوئری استرینگ‌های [4] سایت. اگر از ASP.Net MVC استفاده می‌نمایید، تا حدی زیادی نیاز به نگرانی نخواهد داشت، زیرا تبدیلات [5] در سیستم Model Binding انجام می‌پذیرد و این موضوع تا حد زیادی شما را در برابر حملات SQL Injection مقاوم می‌نماید.
- حتما در فرم‌های عمومی سایتتان از تصویر کپچا با امنیت بالا استفاده نمایید. این موضوع جهت شناخت روبات‌ها از انسان‌ها می‌باشد و شما را در برابر حملات Brute Force مقاوم می‌نماید.
-  حتما سیستم شخصی سازی صفحات ارور را فعال نمایید و از نمایش صفحات ارور حاوی اطلاعات مهمی مانند صفحات ارور ASP.Net جلوگیری نمایید. این موضوع بسیار حساس می‌باشد و می‌تواند نقاط ضعف نرم افزار شما را برای هکر نمایان کند. حتی ممکن است اطلاعات حساسی مانند نام بانک اطلاعاتی، نام کاربری اتصال به بانک اطلاعاتی و نام جداول بانک اطلاعاتی شما را در اختیار هکر قرار دهد.
- استفاده از ORM ها یا استفاده از پروسیجرهای پارامتریک. این موضوع کاملا شما را در برابر حملات SQL Injection مقاوم می‌نماید. کما اینکه ORM ها، سطحی از کش را بصورت توکار دارا می‌باشند و این موضوع در سرعت دستیابی به داده‌ها نیز بسیار تاثیر گذار است. از طرف دیگر بانک اطلاعاتی SQL نیز امکانات توکاری جهت کش نمودن پرس و جو‌های [6] پارامتریک دارد.
- لاگ کردن ارورهای سطح کد و سطح روتینگ [7] . یکی از مهمترین خصیصه‌های پروژه‌های با کیفیت، لاگ شدن خطاهای سطح کد می‌باشد. این امر شما را با نقاط حساس و ضعف‌های نرم افزار آگاه می‌سازد و به شما اجازه می‌دهد به سرعت در جهت رفع آنها اقدام نمایید. لاگ نمودن خطاهای سطح روتینگ شما را از فعالیت‌های هکر‌ها جهت یافتن صفحات لاگین و صفحات مدیریتی پنل مدیریتی سایت اگاه می‌نماید، همچنین شما را از حملات SQL Injection نیز آگاه می‌نماید.
- جلوگیری از ایندکس شدن صفحات لاگین پنل مدیریت سایت در موتورهای جستجو. بخش مهمی از عملیات هکر ها، قرار دادن روبات‌های تشخیص رمز بر روی صفحات لاگین می‌باشد که به نوعی می‌توان این نوع حملات را در دسته حملات Brute Force قرار داد. موتورهای جستجو یکی از ابزارهای مهم هکرها می‌باشد. عملیات هایی مانند یافتن صفحات لاگین پنل مدیریتی یکی از کاربردهای موتورهای جستجو برای هکرها می‌باشد.
- لاگ کردن ورود و خروج افراد به همراه تاریخ، زمان، آی پی افراد و وضعیت لاگین. با کمک این موضوع شما می‌توانید ورود و خروج کاربران نرم افزار خود را کنترل نمایید و موارد غیر طبیعی و مشکوک را در سریعترین زمان مورد بررسی قرار دهید.
- استفاده از روال‌های استاندارد جهت بخش "فراموشی کلمه عبور". همیشه از استاندارهای نرم افزارهای بزرگ پیروی نمایید. بدیهی است استاندارهای استفاده شده در این نرم افزارها بارها و بارها تست شده و سپس بعنوان یک روال استاندارد در همه‌ی نرم افزارهای بزرگ بکار گرفته شده است. استاندارد جهانی بخش "فراموشی کلمه عبور" که در اغلب نرم افزارهای معروف جهان بکار گرفته شده است، عبارت است از دریافت آدرس ایمیل کاربر، احراز هویت ایمیل وارد شده، ارسال یک نامه‌ی الکترونیکی [8] حاوی نام کاربری و لینک تنظیم کلمه عبور جدید به ایمیل کاربر. بهتر است لینک ارسال شده به ایمیل کاربر بصورت یکبار مصرف باشد. کاربر پس از کلیک بر روی لینک تنظیم کلمه عبور جدید، وارد یکی از صفحات سایت شده و می‌تواند کلمه‌ی عبور جدیدی را برای خود ثبت نماید. در پایان، کاربر به صفحه‌ی ورود سایت هدایت شده و پیامی مبنی بر موفقیت آمیز بودن عملیات تغییر کلمه‌ی عبور به او نمایش داده می‌شود. البته روال ذکر شده حداقل رول استانداردی می‌باشد و می‌توان در کنار آن از روال‌های تکمیل کننده‌ای مانند پرسش‌های امنیتی و غیره نیز استفاده نمود.
- قراردادن امکاناتی جهت بلاک نمودن آی پی‌ها و غیر فعال نمودن حساب کاربری اعضای سایت. در نرم افزار باید این امکان وجود داشته باشد که آی پی هایی که بصورت غیر طبیعی در سایت فعالیت می‌نمایند و یا مکررا اقدام به ورود به پنل مدیریتی و پنل کاربران می‌نمایند را بلاک نماییم. همچنین در صورت تخلف کاربران باید بتوان حساب کاربری کاربر خاطی را مسدود نمود. این موضوع می‌تواند بسته به اندازه پروژه و یا سلیقه تیم توسعه بصورت خودکار، دستی و یا هر دو روش در نرم افزار در تعبیه شود.
- امن سازی سرویس‌های ای جکس و چک کردن ای جکس بودن درخواست ها. حتما جلوی اجرای سرویس‌های درون نرم افزاری از بیرون از نرم افزار را بگیرید. سرویس‌های ای جکس یکی از این نوع سرویس‌ها می‌باشند که در نرم افزار‌ها جهت استفاده‌های داخلی در نظر گرفته می‌شوند. در این نوع سرویس‌ها حتما نوع درخواست را بررسی نمایید و از پاسخگویی سرویس‌ها به درخواست‌های غیر ای جکسی جلوگیری نمایید. در ASP.Net MVC این امر توسط متد Request.IsAjaxRequest انجام می‌پذیرد .
- محدود کردن سرویس‌های حساس به درخواست‌های POST. حتما از دسترسی به سرویس هایی از نوع Insert,Update و Delete از طریق فعل GET جلوگیری نمایید. در ASP.Net MVC این سرویس‌ها را به فعل POST محدود نموده و در ASP.Net Web API این سرویس‌ها را به افعال POST,PUT و DELETE محدود نمایید.
- عدم استفاده از آی دی در پنل‌های کاربران بالاخص در آدرس صفحات (کوئری استرینگ) و استفاده از کد غیر قابل پیش بینی مثل GUID به جای آن. حتی الامکان بررسی مالکیت داده‌ها در همه بخش‌های پنل‌های کاربری سایت را جهت محکم کاری بیشتر انجام دهید تا خدای نکرده کاربر با تغییر اطلاعات کوئری استرینگ صفحات نتوانند به داده‌های یک کاربر دیگه دسترسی داشته باشند.
- حتی الامکان پنل مدیران را از کاربران بصورت فیزیکی جدا نمایید. این مورد جهت جلوگیری از خطاهایی است که ممکن است توسط توسعه دهنده در سطح سیستم مدیریت نقش رخ دهد و موجب دسترسی داشتن کاربران به بخش هایی از پنل مدیریتی شود.
- استفاده از الگوریتم‌های کدگذاری ترکیبی و کد کردن اطلاعات حساس قبل از ذخیره سازی در بانک اطلاعاتی. اطلاعات حساسی مانند کلمات عبور را حتما توسط چند الگوریتم کدگذاری، کدگذاری نمایید و سپس درون بانک اطلاعاتی ذخیره نمایید.
- تنظیمات حساس نرم افزار را درون فایل web.config قرار دهید و حتی الامکان آنها را نیز کدگذاری نمایید. بصورتی که اطلاعات قابلیت دیکد شدن را داشته باشند.
- ساخت پروژه بصورت چند لایه. این موضوع جهت جلوگیری از دستیابی هکر به ساختار لایه‌های پروژه‌های شما می‌باشد. به بیان دیگر اگر نهایتا هکر بتواند به اطلاعات FTP هاست شما دست یابد، استفاده از تکنولوژی چند لایه در بدترین حالت هکر را از دستیابی به اطاعات لایه‌های زیرین نرم افزار باز می‌دارد. البته این کار برای هکر‌ها غیر ممکن نیست، اما بسیار سخت و زمان بر می‌باشد.
- اشتراک گذاری اینترفیس در سرویس‌های خارج برنامه ای و عدم اشتراک گذاری کلاس اصلی. این موضوع از دستیابی هکر به بدنه سرویس‌ها و پیاده سازی‌های آنها جلوگیری می‌نماید.
- استفاده از تکنیک‌های مقابله با CSRF در همه سرویس‌های POST. در ASP.NET MVC اتریبیوتی با نام AntiForgery جهت مقاوم سازی سرویس‌ها از حملات CSRF وجود دارد. مکانیزم بدین صورت است که در تمامی فرم‌های سایت یک کد منحصر به فرد تولید می‌گردد که همراه درخواست GET به کامپیوتر کاربر ارسال می‌شود و در هنگام ارسال درخواست POST به سرور، صحت کد مورد نظر بررسی شده و در صورت صحت، اجازه‌ی اجرای سرویس به درخواست داده می‌شود. بدین صورت وقتی کاربر سایت شما فایل آلوده‌ای را باز می‌نماید، در خواست ارسالی هکر که توسط فایل باز شده، به سرور سایت ما ارسال می‌گردد، فاقد کد منحصر به فرد بوده و از اجرای سرویس جلو گیری می‌شود.
- استفاده از سیستم‌های مدیریت نقش امن مانند IDENTITY در ASP.Net MVC و یا استفاده از امکانات توکار دات نت در سیستم‌های مدیریت نقش شخصی سازی شده [9] . بدیهی است امنیت این سیستم‌ها بارها و بارها تست شده است.
- بررسی فرمت و پسوند فایل‌های آپلود شده. توجه نمایید که بررسی پسوند فایل‌ها کافی نبوده و فرمت فایل‌ها نیز می‌بایست بررسی شود. حتی نویسنده پیشنهاد می‌نماید فایل‌ها را به نوع‌های مرتبطشان تبدیل [10] نمایید. در حوزه هک بایند نمودن انواع ویروس، تروجان، شل و بک دور [11] به فایل‌های تصویری و متنی یک امر بسیار رایج است. بنابراین حساسیت زیادی روی این موضوع قرار دهید. نویسنده توصیه می‌نماید کتابخانه‌های کاملی برای این موضوع تدارک ببینید تا در تمامی پروژه‌ها نیاز به ایجاد مجدد آنها نداشته باشید و سعی نمایید در هر پروژه این کتابخانه‌ها را تکمیل‌تر و بهتر نمایید.
- تنظیم IIS  جهت جلوگیری از اجرای فایل‌های اجرایی در مسیر آپلود فایل‌ها. شاید جمله بیان شده به نظر ترسناک و یا سخت برسد، اما این کار با نوشتن چند تگ ساده در فایل Web.Config به راحتی قابل انجام است و نیاز به هیچ نوع کدنویسی ندارد.
- آپلود فایل‌ها در پوشه App_Data و دسترسی به فایل‌ها از طریق سرویس‌های خود شما. پوشه App_Data پوشه‌ای امن است و دسترسی مستقیم از طریق آدرس بار مرورگر به فایل‌های درون آن توسط IIS داده نمی‌شود و افراد فقط از طریق سرویس‌های خود شما می‌توانند به فایل‌های داخل این پوشه دسترسی داشته باشند. بدین صورت در سرویس‌های خود می‌توانید با تبدیل نمودن [12] فایل‌ها به نوع خودشان (تصویر. پی دی اف یا ...) هکر را نا امید نمایید. این موضوع شما را در مقابل حملات SHELL مقاوم می‌نماید.
- استفاده از تکنیک‌های لاگین چند سطحی برای پنل ادمین. در این روش شما حتی با داشتن نام کاربری و کلمه‌ی عبور ادمین، قادر نخواهید بود وارد پنل ادمین شوید. نویسنده ابزار می‌دارد که این روش، یک روش ابداعی می‌باشد که از ترکیبی از احرا هویت ساده توسط نام کاربری و کلمه‌ی عبور به همراه تکنیک‌های احراز هویت ایمیل و موبایل مدیریت سایت می‌باشد.
- استفاده از SSL بسیار اهمیت دارد. بالاخص اگر نرم افزار شما Service Oriented باشد و نرم افزار شما سرویس هایی جهت اتصال به اپلیکیشن‌های خارجی مثل اپلیکیشن اندروید دارد. این مورد در صفحات لاگین نیز بسیار مهم است و موجب می‌شود نام کاربری و کلمه عبور کاربران شما بصورت هش شده بین کامپیوتر کاربر و سرور شما رد و بدل شود و عملا شنود پکت‌ها فایده ای برای هکر نخواهد داشت، زیرا داده‌ها توسط الگوریتم‌های امنیتی که بین سرور و مرورگر کاربران توافق می‌شود کدگذاری شده و سپس رد و بدل می‌شوند.



[1] Back Door
[2] https://www.nuget.org/packages/AntiXss/
[3] www. Nuget.org
[4] Query String
[5] Casting
[6] Procedure
[7] Routing
[8] Email
[9] Custom Role Provider
[10] Cast
[11] Back Door
[12] Cast
مطالب
محدود سازی نرخ دسترسی به منابع در برنامه‌های ASP.NET Core - قسمت دوم - پیاده سازی
در قسمت قبل با مفاهیم، اصطلاحات و الگوریتم‌های مرتبط با میان‌افزار جدید Rate limiting مخصوص ASP.NET Core 7 آشنا شدیم که در پشت صحنه از امکانات موجود در فضای نام System.Threading.RateLimiting استفاده می‌کند. در این قسمت نحوه‌ی استفاده‌ی از آن‌را مرور خواهیم کرد.


روش افزودن میان‌افزار RateLimiter به برنامه‌های ASP.NET Core

شبیه به سایر میان‌افزارها، جهت فعالسازی میان‌افزار RateLimiter، ابتدا باید سرویس‌های متناظر با آن‌را به برنامه معرفی کرد و پس از فعالسازی میان‌افزار مسیریابی، آن‌‌را به زنجیره‌ی مدیریت یک درخواست معرفی نمود. برای نمونه در مثال زیر، امکان دسترسی به تمام درخواست‌ها، به 10 درخواست در دقیقه، محدود می‌شود که پارتیشن بندی آن (در مورد پارتیشن بندی در قسمت قبل بیشتر بحث شد)، بر اساس username کاربر اعتبارسنجی شده و یا hostname یک کاربر غیراعتبارسنجی شده‌است:
var builder = WebApplication.CreateBuilder(args);

builder.Services.AddRateLimiter(options =>
{
    options.GlobalLimiter = PartitionedRateLimiter.Create<HttpContext, string>(httpContext =>
        RateLimitPartition.GetFixedWindowLimiter(
            partitionKey: httpContext.User.Identity?.Name ?? httpContext.Request.Headers.Host.ToString(),
            factory: partition => new FixedWindowRateLimiterOptions
            {
                AutoReplenishment = true,
                PermitLimit = 10,
                QueueLimit = 0,
                Window = TimeSpan.FromMinutes(1)
            }));
});

// ...

var app = builder.Build();

// ...

app.UseRouting();
app.UseRateLimiter();

app.MapGet("/", () => "Hello World!");

app.Run();
توضیحات:
- فراخوانی builder.Services.AddRateLimiter، سبب معرفی سرویس‌های میان‌افزار rate limiter به سیستم تزریق وابستگی‌های ASP.NET Core می‌شود.
- در اینجا می‌توان برای مثال خاصیت options.GlobalLimiter تنظیمات آن‌را نیز مقدار دهی کرد. GlobalLimiter، سبب تنظیم یک محدود کننده‌ی سراسری نرخ، برای تمام درخواست‌های رسیده‌ی به برنامه می‌شود.
- GlobalLimiter را می‌توان با هر نوع PartitionedRateLimiter مقدار دهی کرد که در اینجا از نوع FixedWindowLimiter انتخاب شده‌است تا بتوان «الگوریتم‌های بازه‌ی زمانی مشخص» را به برنامه اعمال نمود تا برای مثال فقط امکان پردازش 10 درخواست در هر دقیقه برای هر کاربر، وجود داشته باشد.
- در پایان کار، فراخوانی app.UseRateLimiter را نیز مشاهده می‌‌کنید که سبب فعالسازی میان‌افزار، بر اساس تنظیمات صورت گرفته می‌شود.

برای آزمایش برنامه، آن‌را  اجرا کرده و سپس به سرعت شروع به refresh کردن صفحه‌ی اصلی آن کنید. پس از 10 بار ریفرش، پیام  503 Service Unavailable را مشاهده خواهید کرد که به معنای مسدود شدن دسترسی به برنامه توسط میان‌افزار rate limiter است.


بررسی تنظیمات رد درخواست‌ها توسط میان‌افزار rate limiter

اگر پس از محدود شدن دسترسی به برنامه توسط میان افزار rate limiter از status code = 503 دریافتی راضی نیستید، می‌توان آن‌را هم تغییر داد:
builder.Services.AddRateLimiter(options =>
{
    options.RejectionStatusCode = 429;

    // ...
});
برای مثال بسیاری از سرویس‌ها بجای 503، از status code دیگری مانند 429 Too Many Requests استفاده می‌کنند که نحوه‌ی تنظیم آن‌را در مثال فوق مشاهده می‌کنید.
علاوه بر آن در اینجا گزینه‌ی OnRejected نیز پیش بینی شده‌است تا بتوان response ارائه شده را در حالت رد درخواست، سفارشی سازی کرد تا بتوان پیام بهتری را به کاربری که هم اکنون دسترسی او محدود شده‌است، ارائه داد:
builder.Services.AddRateLimiter(options =>
{
    options.OnRejected = async (context, token) =>
    {
        context.HttpContext.Response.StatusCode = 429;
        if (context.Lease.TryGetMetadata(MetadataName.RetryAfter, out var retryAfter))
        {
            await context.HttpContext.Response.WriteAsync(
                $"Too many requests. Please try again after {retryAfter.TotalMinutes} minute(s). " +
                $"Read more about our rate limits at https://example.org/docs/ratelimiting.", cancellationToken: token);
        }
        else
        {
            await context.HttpContext.Response.WriteAsync(
                "Too many requests. Please try again later. " +
                "Read more about our rate limits at https://example.org/docs/ratelimiting.", cancellationToken: token);
        }
    };

    // ...
});
برای نمونه در مثال فوق ابتدا status code، به 429 تنظیم می‌شود و سپس یک response با معنا به سمت کاربر ارسال می‌گردد که دقیقا مشخص می‌کند آن کاربر چه زمانی می‌تواند مجددا سعی کند و همچنین لینکی را به مستندات محدود سازی برنامه جهت توضیحات بیشتر ارائه می‌دهد.

یک نکته: باتوجه به اینکه در اینجا به HttpContext دسترسی داریم، یعنی به context.HttpContext.RequestServices نیز دسترسی خواهیم داشت که توسط آن می‌توان برای مثال سرویس ILogger را از آن درخواست کرد و رخ‌داد واقع شده را برای بررسی بیشتر لاگ نمود؛ برای مثال چه کاربری مشکل پیدا کرده‌است؟
context.HttpContext.RequestServices.GetService<ILoggerFactory>()?
                .CreateLogger("Microsoft.AspNetCore.RateLimitingMiddleware")
                .LogWarning("OnRejected: {RequestPath}", context.HttpContext.Request.Path);
همچنین باید دقت داشت که اگر در اینجا از بانک اطلاعاتی استفاده کرده‌اید، تعداد کوئری‌های آن‌را محدود کنید؛ وگرنه واقعا rate limiter از لحاظ محدود کردن دسترسی به منابع، کمک زیادی را به شما نخواهد کرد.

طراحی فعلی میان‌افزار rate limiter، کمی محدود است. برای مثال «retry after»، تنها metadata مفیدی است که جهت بازگشت ارائه می‌دهد و همچنین مانند GitHub مشخص نمی‌کند که در لحظه‌ی جاری چند درخواست دیگر را می‌توان ارسال کرد و امکان دسترسی به اطلاعات آماری درونی آن وجود ندارد. اگر نیاز به یک چنین اطلاعاتی دارید شاید استفاده از میان‌افزار ثالث دیگری به نام AspNetCoreRateLimit برای شما مفیدتر باشد!


الگوریتم‌های پشتیبانی شده‌ی توسط میان‌افزار rate limiter

در قسمت قبل با چند الگوریتم استاندارد طراحی میان‌افزارهای rate limiter آشنا شدیم که میان‌افزار توکار rate limiter موجود در ASP.NET Core 7x، اکثر آن‌ها را پشتیبانی می‌کند:
- Concurrency limit: ساده‌ترین نوع محدود سازی نرخ درخواست‌ها است و کاری به زمان ندارد و فقط برای آن، تعداد درخواست‌های همزمان مهم است. برای مثال پیاده سازی «مجاز بودن تنها 10 درخواست همزمان».
- Fixed window limit: توسط آن می‌توان محدودیت‌هایی مانند «مجاز بودن تنها 60 درخواست در دقیقه» را اعمال کرد که به معنای امکان ارسال یک درخواست در هر ثانیه در هر دقیقه و یا حتی ارسال یکجای 60 درخواست در یک ثانیه است.
- Sliding window limit: این محدودیت بسیار شبیه به حالت قبل است اما به همراه قطعاتی که کنترل بیشتری را بر روی محدودیت‌ها میسر می‌کند؛ مانند مجاز بودن 60 درخواست در هر دقیقه که فقط در این حالت یک درخواست در هر ثانیه مجاز باشد.
- Token bucket limit: امکان کنترل نرخ سیلان را میسر کرده و همچنین از درخواست‌های انفجاری نیز پشتیبانی می‌کند (این مفاهیم در قسمت قبل بررسی شدند).

علاوه بر این‌ها امکان ترکیب گزینه‌های فوق توسط متد کمکی PartitionedRateLimiter.CreateChained نیز میسر است:
builder.Services.AddRateLimiter(options =>
{
    options.GlobalLimiter = PartitionedRateLimiter.CreateChained(
        PartitionedRateLimiter.Create<HttpContext, string>(httpContext =>
            RateLimitPartition.GetFixedWindowLimiter(httpContext.ResolveClientIpAddress(), partition =>
                new FixedWindowRateLimiterOptions
                {
                    AutoReplenishment = true,
                    PermitLimit = 600,
                    Window = TimeSpan.FromMinutes(1)
                })),
        PartitionedRateLimiter.Create<HttpContext, string>(httpContext =>
            RateLimitPartition.GetFixedWindowLimiter(httpContext.ResolveClientIpAddress(), partition =>
                new FixedWindowRateLimiterOptions
                {
                    AutoReplenishment = true,
                    PermitLimit = 6000,
                    Window = TimeSpan.FromHours(1)
                })));

    // ...
});
برای نمونه در مثال فوق به ازای یک آدرس IP مشخص، تنها می‌توان 600 درخواست را در دقیقه ارسال کرد؛ با این محدودیت که جمع آن‌ها در ساعت، بیشتر از 6000 مورد نباشد.
در این مثال فرضی، متد الحاقی ResolveClientIpAddress اهمیتی ندارد. بهتر است برای برنامه‌ی خود از کلید پارتیشن بندی بهتر و معقول‌تری استفاده کنید.


امکان در صف قرار دادن درخواست‌ها بجای رد کردن آن‌ها

در تنظیمات مثال‌های فوق، در کنار PermitLimit، می‌توان QueueLimit را نیز مشخص کرد. به این ترتیب با رسیدن به PermitLimit، به تعداد QueueLimit، درخواست‌ها در صف قرار می‌گیرند، بجای اینکه کاملا رد شوند:
PartitionedRateLimiter.Create<HttpContext, string>(httpContext =>
    RateLimitPartition.GetFixedWindowLimiter(httpContext.ResolveClientIpAddress(), partition =>
        new FixedWindowRateLimiterOptions
        {
            AutoReplenishment = true,
            PermitLimit = 10,
            QueueLimit = 6,
            QueueProcessingOrder = QueueProcessingOrder.OldestFirst,
            Window = TimeSpan.FromSeconds(1)
        })));
در این مثال هر کلاینت می‌تواند 10 درخواست در ثانیه را ارسال کند. در صورت رسیدن به این محدودیت، تا 6 عدد از درخواست‌های جدید رسیده، بجای رد شدن، در صف قرار می‌گیرند تا در ثانیه‌ی بعدی که این بازه‌ی مشخص به پایان می‌رسد، پردازش شوند.
این تنظیم، تجربه‌ی کاربری بهتری را برای استفاده کنندگان از برنامه‌ی شما به همراه خواهد داشت؛ بجای رد قاطع درخواست‌های ارسالی توسط آن‌ها.

یک نکته: بهتر است QueueLimitهای بزرگی را انتخاب نکنید؛ خصوصا برای بازه‌های زمانی طولانی. چون یک مصرف کننده نیاز دارد تا سریع، پاسخی را دریافت کند و اگر این‌طور نباشد، دوباره سعی خواهد کرد. تنها چند ثانیه‌ی کوتاه در صف بودن برای کاربران معنا دارد.


امکان ایجاد سیاست‌های محدود سازی سفارشی

اگر الگوریتم‌های توکار میان‌افزار rate limiter برای کار شما مناسب نیستند، می‌توانید با پیاده سازی <IRateLimiterPolicy<TPartitionKey، یک نمونه‌ی سفارشی را ایجاد کنید. پیاده سازی این اینترفیس، نیاز به دو متد را دارد:
الف) متد GetPartition که بر اساس HttpContext جاری، یک rate limiter مخصوص را باز می‌گرداند.
ب) متد OnRejected که امکان سفارشی سازی response رد درخواست‌ها را میسر می‌کند.

در مثال زیر پیاده سازی یک rate limiter سفارشی را مشاهده می‌کنید که نحوه‌ی پارتیشن بندی آن بر اساس user-name کاربر اعتبارسنجی شده و یا host-name کاربر وارد نشده‌ی به سیستم است. در اینجا کاربر وارد شده‌ی به سیستم، محدودیت بیشتری دارد:
public class ExampleRateLimiterPolicy : IRateLimiterPolicy<string>
{
    public RateLimitPartition<string> GetPartition(HttpContext httpContext)
    {
        if (httpContext.User.Identity?.IsAuthenticated == true)
        {
            return RateLimitPartition.GetFixedWindowLimiter(httpContext.User.Identity.Name!,
                partition => new FixedWindowRateLimiterOptions
                {
                    AutoReplenishment = true,
                    PermitLimit = 1_000,
                    Window = TimeSpan.FromMinutes(1),
                });
        }

        return RateLimitPartition.GetFixedWindowLimiter(httpContext.Request.Headers.Host.ToString(),
            partition => new FixedWindowRateLimiterOptions
            {
                AutoReplenishment = true,
                PermitLimit = 100,
                Window = TimeSpan.FromMinutes(1),
            });
    }

    public Func<OnRejectedContext, CancellationToken, ValueTask>? OnRejected { get; } =
        (context, _) =>
        {
            context.HttpContext.Response.StatusCode = 418; // I'm a 🫖
            return new ValueTask();
        };
}
و نحوه‌ی معرفی آن به سیستم به صورت زیر است:
options.AddPolicy<string, ExampleRateLimiterPolicy>("myPolicy");


امکان تعریف سیاست‌های محدود سازی نرخ دسترسی به گروهی از endpoints

تا اینجا روش‌های سراسری محدود سازی دسترسی به منابع برنامه را بررسی کردیم؛ اما ممکن است در برنامه‌ای بخواهیم محدودیت‌های متفاوتی را به گروه‌های خاصی از endpoints اعمال کنیم و یا شاید اصلا نخواهیم تعدادی از آن‌ها را محدود کنیم:
builder.Services.AddRateLimiter(options =>
{
    options.AddFixedWindowLimiter("Api", options =>
    {
        options.AutoReplenishment = true;
        options.PermitLimit = 10;
        options.Window = TimeSpan.FromMinutes(1);
    });

    options.AddFixedWindowLimiter("Web", options =>
    {
        options.AutoReplenishment = true;
        options.PermitLimit = 10;
        options.Window = TimeSpan.FromMinutes(1);
    });

    // ...
});
در این مثال روش تعریف دو سیاست مختلف محدودسازی را مشاهده می‌کنید که اینبار «نامدار» هستند؛ نام یکی Api است و نام دیگری Web.
البته باید درنظر داشت که متدهای الحاقی Add داری را که در اینجا ملاحظه می‌کنید، محدود سازی را بر اساس نام درنظر گرفته شده انجام می‌دهند. یعنی درحقیقت یک محدودسازی سراسری بر اساس گروهی از endpoints هستند و امکان تعریف پارتیشنی را به ازای یک کاربر یا آدرس IP خاص، ندارند. اگر نیاز به اعمال این نوع پارتیشن بندی را دارید، باید از متدهای AddPolicy استفاده کنید:
options.AddPolicy("Api", httpContext =>
        RateLimitPartition.GetFixedWindowLimiter(httpContext.ResolveClientIpAddress(),
        partition => new FixedWindowRateLimiterOptions
        {
            AutoReplenishment = true,
            PermitLimit = 10,
            Window = TimeSpan.FromSeconds(1)
        }));
متدهای AddPolicy دار، هم امکان دسترسی به httpContext جاری را میسر می‌کنند و هم نامدار هستند که قابلیت اعمال آن‌ها را به گروهی از endpoints ممکن می‌کند.


محدود سازی نرخ دسترسی به منابع در ASP.NET Core Minimal API

پس از تعریف نامی برای سیاست‌های دسترسی، اکنون می‌توان از آن‌ها به صورت زیر جهت محدود سازی یک endpoint و یا گروهی از آن‌ها استفاده کرد:
// Endpoint
app.MapGet("/api/hello", () => "Hello World!").RequireRateLimiting("Api");

// Group
app.MapGroup("/api/orders").RequireRateLimiting("Api");
و یا حتی می‌توان بطور کامل محدود سازی نرخ دسترسی را برای یک endpoint و یا گروهی از آن‌ها غیرفعال کرد:
// Endpoint
app.MapGet("/api/hello", () => "Hello World!").DisableRateLimiting();

// Group
app.MapGroup("/api/orders").DisableRateLimiting();


محدود سازی نرخ دسترسی به منابع در ASP.NET Core MVC

می‌توان سیاست‌های نرخ دسترسی تعریف شده را بر اساس نام آن‌ها به کنترلرها و یا اکشن متدها اعمال نمود:
[EnableRateLimiting("Api")]
public class Orders : Controller
{
    [DisableRateLimiting]
    public IActionResult Index()
    {
        return View();
    }

    [EnableRateLimiting("ApiListing")]
    public IActionResult List()
    {
        return View();
    }
}
در اینجا سیاست نرخ دسترسی با نام Api، به کل کنترلر و اکشن متدهای آن اعمال شده، اما اکشن متد Index آن با بکارگیری ویژگی DisableRateLimiting، از این محدودیت خارج و اکشن متد List، از سیاست نام دار دیگری استفاده کرده‌است.
و یا حتی می‌توان این سیاست‌های محدود سازی نرخ دسترسی را به تمام کنترلرها و صفحات razor نیز به صورت زیر اعمال کرد:
app.UseConfiguredEndpoints(endpoints =>
{
    endpoints.MapRazorPages()
        .DisableRateLimiting();

    endpoints.MapControllers()
        .RequireRateLimiting("UserBasedRateLimiting");
});
مطالب
معرفی Bit Platform
پلتفرم Bit یک پروژه تماما Open Source در GitHub میباشد که هدف آن تسهیل توسعه نرم افزار با کیفیت و پرفرمنس بالا بر بستر ASP.NET Core و زبان #C است که با آن بتوان فقط با یکبار کدنویسی و با کمک استانداردهای وب همچون HTML / CSS و Web Assembly ، خروجی‌هایی چون Android / iOS / Windows را با دسترسی کامل به امکانات سیستم‌عامل به همراه برنامه‌های تحت وب SPA و PWA (با یا بدون Pre-Rendering) گرفت.
پلتفرم Bit تا به اینجا از دو قسمت Bit Blazor Components (شامل بیش از ۳۰ کمپوننت کاربردی، کم حجم و High Performance مانند Tree / Multi Select / Data Grid / Date Picker / Color Picker و...) به همراه Bit Project Templates (قالب پروژه‌های حاوی امکانات پر استفاده) تشکیل شده است.
برخی مواردی که در رابطه با آنها صحبت شد، ممکن است برای شما آشنا نباشند، بنابراین قبل از بررسی مفصل‌‌تر Bit Platform، نگاهی به آن می‌اندازیم:


وب اسمبلی چیست؟

برای درک بهتر وب اسمبلی ابتدا باید بدانیم این تکنولوژی اصلا از کجا آمده و هدف آن چیست؟
میدانیم که مرورگر‌ها پروایدر صفحات وب هستند و ما برای اینکه بتوانیم اپلیکیشنی که ساختیم را در محیط وب برای کاربران به اشتراک بگذاریم باید از این مرورگر‌ها و زبان ارتباطی آن‌ها پیروی کنیم. این زبان‌های ارتباطی مشخصا سه چیز است: HTML CSS JavaScript
اما آیا راهی هست که بتوان بجای JavaScript از زبان‌های دیگر هم در سمت مرورگر استفاده کرد؟
وب اسمبلی یا همان WASM، آمده تا به ما اجازه دهد از هر زبانی که خروجی به Web Assembly دارد، برای تعاملات UI استفاده کنیم. یعنی با زبان هایی مثل #C / C++ / C و... میتوان کدی نوشت که مرورگر آن را اجرا کند. این یک تحول بزرگ است که امروزه تمامی مرورگرها (به جز مرورگرهایی که از دور خارج شده اند) از آن پشتیانی می‌کنند چرا که Web Assembly به یکی از اجزای استاندارد وب تبدیل شده است.
اطلاعات بیشتر در رابطه با وب اسمبلی را میتوانید از این مقاله بخوانید. 


تعریف SPA و PWA:
SPA: Single Page Application
PWA: Progressive Web Application
در گذشته برای رندر کردن صفحات وب با عوض شدن URL یا درخواست کاربر برای دریافت اطلاعات جدید از سرور و نمایش آن ، صفحه مرورگر ملزم به رفرش شدن مجدد و از سر گیری کل فرایند تولید HTML میشد. طبیعتا این تکرار برای کاربر هنگام استفاده از اپلیکیشن خیلی خوشایند نبود چرا که هربار میبایست زمانی بیشتر صرف تولید مجدد صفحات را منتظر میماند. اما در مقابل آن Single Page Application (SPA)‌‌ها این پروسه را تغیر داد.
در رویکرد SPA، کل CSS , HTML و JS ای که برای اجرای هر صفحه ای از اپلیکیشن نیاز هست در همان لود اولیه برنامه توسط مرورگر دانلود خواهد شد. با این روش هنگام تغییر URL صفحات مرورگر دیگر نیازی به لود دوباره اسکریپت‌ها ندارد. همچنین وقتی قرار است اطلاعات جدیدی از سرور آپدیت و نمایش داده شود این درخواست بصورت یک دستور Ajax به سرور ارسال شده و سرور با فرمت JSON اطلاعات درخواست شده را پاسخ میدهد. در نهایت مرورگر نیز اطلاعات برگشتی از سرور را مجدد جای گذاری میکند و کل این روند بدون هیچگونه رفرش دوباره صفحه انجام میشود.
در نتیجه‌ی این امر، کاربر تجربه خوشایند‌تری هنگام کار کردن با SPA‌ها خواهد داشت. اما همانقدر که این تجربه در طول زمان استفاده از برنامه بهبود یافت، لود اولیه اپلیکیشن را کند‌تر کرد، چرا که اپلیکیشن میبایست همه کدهای مورد نیاز خود برای صفحاتش را در همان ابتدا دانلود کند.(در ادامه با قابلیت Pre-Rendering این اشکال را تا حدود زیادی رفع میکنیم)
با استفاده از PWA میتوانید وبسایت‌های SPA را بصورت یک برنامه نصبی و تمام صفحه، با آیکن مجزایی همانند اپلیکیشن‌های دیگر در موبایل و دسکتاپ داشته باشید. همچنین وقتی از PWA استفاده میکنیم برنامه وب میتواند به صورت آفلاین نیز کار کند.
البته حتی در برنامه‌هایی که لازم نیست آفلاین کار کنند، در صورت قطعی ارتباط کاربر با شبکه، به جای دیدن دایناسور معروف، اینکه برنامه در هر حالتی باز شود و به صورتی کاربر پسند و قطعی نت به وی اعلام شود ایده خیلی بهتری است (": 


قابلیت Pre-Rendering:
هدف Pre-Rendering بهبود گشت گذار کاربر در سایت است. شیوه کارکرد آن به این صورت است که وقتی کاربر وارد وبسایت میشود، سرور در همان ابتدای کار و جلوتر از اتمام دانلود اسمبلی‌ها، فایلی حاوی HTML ، CSS‌های صفحه ای که کاربر درخواست کرده را در سمت سرور می‌سازد و به مرورگر ارسال میکند. در همین حین، اسمبلی‌ها نیز توسط مرورگر دانلود می‌شوند و برنامه از محتوای صرف خارج شده و حالت تعاملی می‌گیرد. اصطلاحا به این قابلیت Server-Side Rendering(SSR) نیز میگویند. در این حالت کاربر زودتر محتوایی از برنامه را میبیند و تجربه بهتری خواهد داشت. این امر در بررسی Search Engine‌‌ها و سئو وبسایت نیز تاثیر بسزایی دارد. 


نگاهی به Blazor:
تا اینجا هر آنچه که نیاز بود برای درک بهتر از Blazor بدانیم را فهمیدیم، اما خود Blazor چیست؟ در یک توضیح کوتاه، فریمورکی ارائه شده توسط مایکروسافت میباشد برای پیاده‌سازی UI و منطق برنامه‌ها شامل امکانات Routing ، Binding و...
بلیزر در انواع مختلفی ارائه شده که هرکدام کاربرد مشخصی دارد:

Blazor Server
در بلیزر سرور پردازش‌ها جهت تعامل UI درون سرور اجرا خواهد شد. برای مثال وقتی کاربر روی دکمه ای کلیک میکند و آن دکمه مقداری عددی را افزایش می‌دهد که از قضا متن یک Label به آن عدد وابسته است، رویداد کلیک شدن این دکمه توسط SignalR WebSocket به سرور ارسال شده و سرور تغیر متن Label را روی همان وب سوکت به کلاینت ارسال می‌کند.
با توجه به این که تعامل کاربر با صفحات برنامه، بسته به میزان کندی اینترنت کاربر، ممکن است کند شود و همچنین Blazor Server قابلیت PWA شدن ندارد و علاوه بر این بار پردازش زیادی روی سرور می‌اندازد (بسته به پیچیدگی پروژه) و در نهایت ممکن است در آن از Component هایی استفاده کنیم که چون در حالت Blazor Server پردازش سمت سرور بوده، متوجه حجم دانلود بالای آنها نشویم و کمی بعدتر که با Blazor Hybrid می‌خواهیم خروجی Android / iOS بگیریم متوجه حجم بالای آنها شویم، استفاده از Blazor Server را در Production توصیه نمی‌کنیم، ولی این حالت برای Debugging بهترین تجربه را ارائه می‌دهد، بالاخص با امکان Hot Reload و دیدن آنی تغییرات C# / HTML / CSS در ظاهر و رفتار برنامه موقع کدنویسی.

Blazor WebAssembly
در بلیزر وب اسمبلی منطق مثال قبلی که با C# .NET نوشته شده است، روی مرورگر و با کمک Web Assembly اجرا می‌شود و نیازی به ارتباط جاری با سرور توسط SignalR نیست. این باعث میشود که با بلیزر وب اسمبلی بتوان اپلیکیشن‌های PWA نیز نوشت.
یک برنامه Blazor Web Assembly می‌تواند چیزی در حدود دو الی سه مگ حجم داشته باشد که در دنیای امروزه حجم بالایی به حساب نمیاید، با این حال با کمک Pre Rendering و CDN می‌توان تجربه کاربر را تا حدود زیادی بهبود داد.
برای مثال سایت Component‌های Bit Platform جزو معدود دموهای Component‌های Blazor است که در حالت Blazor Web Assembly ارائه می‌شود و شما می‌توانید با باز کردن آن، تجربه حدودی کاربرانتان را در حین استفاده از Blazor Web Assembly ببینید. به علاوه، در dotnet 7 سرعت عملکرد Blazor Web Assembly بهبود قابل توجهی پیدا کرده است.

Blazor Hybrid
از Blazor Hybrid زمانی استفاده می‌کنیم که بخواهیم برنامه‌های موبایل را برای Android , iOS و برنامه‌های Desktop را برای ویندوز، با کمک HTML , CSS توسعه دهیم. نکته اصلی در Blazor Hybrid این است که اگر چه از Web View برای نمایش HTML / CSS استفاده شده، اما منطق سمت کلاینت برنامه که با C# .NET توسعه داده شده است، بیرون Web View و به صورت Native اجرا می‌شود که ضمن داشتن Performance بالا، به تمامی امکانات سیستم عامل دسترسی دارد. علاوه بر دسترسی به کل امکانات Android / iOS Sdk در همان C# .NET ، عمده کتابخانه‌های مطرح مانند Firebase، با ابزار Binding Library ارائه شده توسط مایکروسافت، دارای Wrapper قابل استفاده در C# .NET هستند و ساختن Wrapper برای هر کتابخانه Objective-C ، Kotlin، Java، Swift با این ابزار فراهم است.

اگر شما در حال حاضر فقط #HTML , CSS , C بدانید، اکنون با بلیزر میتوانید هر اپلیکیشنی که بخواهید توسعه دهید. از وبسایت‌های SPA گرفته تا اپ‌های موبایل Android ، IOS و برنامه‌های دسکتاپی برای Windows , Mac و بزودی نیز برای Linux
سری آموزش بلیزر را میتوانید از این لینک‌ها (1 ، 2) دنبال کنید. 


معرفی پکیج Bit Blazor UI:
پکیج Bit Blazor UI مجموعه ای از کامپوننت‌های مرسومی است که بر پایه بلیزر نوشته شده و به ما این امکان را میدهد تا المان‌های پیچیده ای مثل Date Picker , Grid , Color Picker , File Upload , DropDown و بسیاری از المان‌های دیگر را با شکلی بهینه، بدون نیاز به اینکه خودمان بخواهیم برای هر یک از اینها از نو کدنویسی کنیم، آن را در اختیار داشته باشیم.
عمده مشکل کامپوننت‌های ارائه شده برای بلیزر حجم نسبتا بالای آن است که باعث میشد بیشتر در مصارفی از قبیل ایجاد Admin Panel کارایی داشته باشد. اما این موضوع به خوبی در Bit Blazor UI مدیریت شده و در حال حاضر با بیش از 30 کامپوننت با حجم بسیار پایینی، چیزی حدود 200 کیلوبایت قابل نصب است. از لحاظ حجمی نسبت به رقبای خود برتری منحصر به فردی دارد که باعث میشود به راحتی حتی در اپلیکیشن‌های موبایل هم قابل استفاده باشد و کماکان پرفرمنس خوبی ارائه دهد.
این کامپوننت‌ها با ظاهر Fluent پیاده سازی شده و میتوانید لیست کامپوننت‌های بلیزر Bit را از این لینک ببینید. 


معرفی Bit TodoTemplate:
قبل از اینکه به معرفی Bit TodoTemplate بپردازیم باید بدانیم که اصلا پروژه‌های Template چه هستند. در واقع وقتی شما Visual Studio را باز میکنید و روی گزینه Create New Project کلیک میکنید با لیستی از پروژه‌های تمپلیت روبرو میشوید که هرکدام چهارچوب خاصی را با اهدافی متفاوت در اختیارتان قرار میدهند.
Bit Platform هم Project Template ای با نام TodoTemplate توسعه داده که میتوانید پروژه‌های خودتان را از روی آن بسازید، اما چه امکاناتی به ما میدهد؟
در یک جمله، هر آنچه تا به اینجا توضیح داده شد بصورت یکجا در TodoTemplate وجود دارد.
در واقع TodoTemplate چهارچوبی را فراهم کرده تا شما تنها با دانستن همین مفاهیمی که در این مقاله خواندید، از همان ابتدا امکاناتی چون صفحات SignUp، SignIn یا Email Confirmation و... را داشته باشید و در نهایت بتوانید تمامی خروجی‌های قابل تصور را بگیرید.
اما چگونه؟
در TodoTemplate همه این قابلیت‌ها تنها درون یک فایل و با کمترین تغیر ممکن نوع خروجی کدی که نوشته اید را مشخص میکند. این تنظیمات به شکل زیر است :
<BlazorMode> ... </BlazorMode>
<WebAppDeploymentType> ... </WebAppDeploymentType>
شما میتوانید با تنظیم <BlazorMode> بین انواع hosting model‌های بلیزر سوییچ کنید. مثلا برای زمانی که در محیط development هستید نوع بلیزر را Blazor Server قرار دهید تا از قابلیت‌های debugging بهتری برخوردار باشید ، وقتی میخواهید وبسایت تکمیل شده تان را بصورت SPA / PWA پابلیش کنید نوع بلیزر را به Blazor WebAssembly و برای پابلیش‌های Android ، IOS ، Windows ، Mac نوع بلیزر را به Blazor Hybrid تغیر دهید.
به علاوه، شما تنها با تغیر <WebAppDeploymentType> قادر خواهید بود بین SPA (پیش فرض)، SSR و PWA سوئیچ کنید.
قابلیت‌های TodoTemplate در اینجا به پایان نمیرسد و بخشی دیگر از این قابلیت‌ها به شرح زیر است :
  • وجود سیستم Exception handling در سرور و کلاینت (این موضوع به گونه ای بر اساس Best Practice‌‌ها پیاده سازی شده که اپلیکیشن را از بروز هر خطایی که بخواهد موجب Crash کردن برنامه شود ایزوله کرده)
  • وجود سیستم User Authentication بر اساس JWT که شما در همان ابتدا که از این تمپلیت پروژه جدیدی میسازید صفحات SignIn ، SignUp را خواهید داشت.
  • پکیج Bit Blazor UI که بالاتر درمورد آن صحبت کرده ایم از همان ابتدا در TodoTemplate نصب و تنظیم شده تا بتوانید به راحتی صفحات جدید با استفاده از آن بسازید.
  • کانفیگ استاندارد Swagger در سمت سرور.
  • ارسال ایمیل در روند SignUp.
  • وجود خاصیت AutoInject برای ساده‌سازی تزریق وابستگی ها.
  • و بسیاری موراد دیگر که در داکیومنت‌های پروژه میتوانید آنهارا ببینید.
با استفاده از TodoTemplate پروژه ای با نام Todo ساخته شده که میتوانید چندین مدل از خروجی‌های این پروژه را در لینکهای پایین ببینید و پرفرمنس آن را بررسی کنید.
توجه داشته باشید هدف TodoTemplate ارائه ساختار Clean Architecture نبوده ، بلکه هدف ارائه بیشترین امکانات با ساده‌ترین حالت کدنویسی ممکن بوده که قابل استفاده برای همگان باشد و شما میتوانید از هر پترنی که میخواهید براحتی در آن استفاده کنید.
پلتفرم Bit یک تیم توسعه کاملا فعال تشکیل داده که بطور مداوم در حال بررسی و آنالیز خطاهای احتمالی ، ایشو‌های ثبت شده و افزودن قابلیت‌های جدید میباشد که شما به محض استفاده از این محصولات میتوانید در صورت بروز هر اشکال فنی برای آن ایشو ثبت کنید تا تیم مربوطه آن را بررسی و در دستور کار قرار دهد. در ادامه پلتفرم Bit قصد دارد بزودی تمپلیت جدیدی با نام Admin Panel Template با امکاناتی مناسب برای Admin Panel مثل Dashboard و Chart و... با تمرکز بر Clean Architecture نیز ارائه کند. چیزی که مشخص است اوپن سورس بودن تقریبا %100 کارها میباشد از جلسات و گزارشات کاری گرفته تا جزئیات کارهایی که انجام میشود و مسیری که در آینده این پروژه طی خواهد کرد.
میتوانید اطلاعات بیشتر و مرحله به مرحله برای شروع استفاده از این ابزار‌ها را در منابعی که معرفی میشود دنبال کنید.

منابع:

مشارکت در پروژه:
  • شما میتوانید این پروژه را در گیتهاب مشاهده کنید.
  • برای اشکالات یا قابلیت هایی که میخواهید برطرف شود Issue ثبت کنید.
  • پروژه را Fork کنید و Star دهید.
  • ایشوهایی که وجود دارد را برطرف کنید و Pull Request ارسال کنید.
  • برای در جریان بودن از روند توسعه در جلسات برنامه ریزی (Planning Meeting) و گزارشات هفتگی (Standup Meeting ) که همه اینها در Microsoft Teams برگزار میشود شرکت کنید. 
مطالب
آزمایش ساده‌تر Web APIs توسط strest
در سری کار با Postman، یک روش بسیار متداول آزمایش Web APIs را بررسی کردیم. اما ... برای کار آن با مدام نیاز است از این برگه به آن برگه مراجعه کرد و ارتباط دادن درخواست‌های متوالی در آن مشکل است. به همین منظور تابحال راه‌حل‌های زیادی برای جایگزین کردن postman ارائه شده‌اند که یکی از آن‌ها strest است. این ابزار خط فرمان:
- بسیار سبک ورزن است و تنها نیاز به نصب بسته‌ی npm آن‌را دارد.
- با فایل‌های متنی معمولی کار می‌کند که ویرایش و copy/paste در آن‌ها بسیار ساده‌است.
- قرار دادن فایل‌های نهایی متنی آن در ورژن کنترل بسیار ساده‌است.
- امکان نوشتن درخواست‌های به هم وابسته و آزمودن نتایج حاصل را دارا است.
- چون یک ابزار خط فرمان است، امکان استفاده‌ی از آن به سادگی در فرآینده‌های توسعه‌ی مداوم وجود دارد.
- ابزارهای npm، چندسکویی هستند.


نصب strest

در ادامه قصد داریم مطلب «آزمایش Web APIs توسط Postman - قسمت ششم - اعتبارسنجی مبتنی بر JWT» را با استفاده از strest بازنویسی کنیم. به همین جهت در ابتدا نیاز است بسته‌ی npm آن‌را به صورت سراسری نصب کنیم:
npm i -g @strest/cli
پس از آن فایل جدید JWT.strest.yml را در پوشه‌ای ایجاد کرده و آن‌را تکمیل می‌کنیم. برای اجرای فرامین موجود در آن تنها کافی است دستور strest JWT.strest.yml را درخط فرمان صادر کنیم.


مرحله 1: خاموش کردن بررسی مجوز SSL برنامه
مرحله 2: ایجاد درخواست login و دریافت توکن‌ها

مجوز SSL آزمایشی برنامه‌ی ASP.NET Core ما، از نوع خود امضاء شده‌است. به همین جهت اگر سعی در اجرای strest را با درخواست‌های ارسالی به آن داشته باشیم، باشکست مواجه خواهند شد. بنابراین در ابتدا، خاصیت allowInsecure را به true تنظیم می‌کنیم:
version: 2

variables:
  baseUrl: https://localhost:5001/api
  logResponse: false

allowInsecure: true
- این تنظیمات با فرمت yaml نوشته می‌شوند. به همین جهت در اینجا تعداد spaceها مهم است.
- همچنین در ابتدای این تنظیمات، روش تعریف متغیرها را نیز مشاهده می‌کنید که برای مثال توسط آن‌ها baseUrl تعریف شده‌است.
درست در سطر پس از این تنظیمات، دستور اجرا و اعتبارسنجی درخواست Login را می‌نویسیم:
requests:
  loginRequest:
    request:
      url: <$ baseUrl $>/account/login
      method: POST
      postData:
        mimeType: application/json
        text:
          username: "Vahid"
          password: "1234"
    log: <$ logResponse $>
    validate:
      - jsonpath: content.access_token
        type: [string]
      - jsonpath: content.refresh_token
        type: [string]
توضیحات:
- درخواست‌ها با requests شروع می‌شوند. سپس ذیل آن می‌توان نام چندین درخواست یا request را ذکر کرد که برای مثال نام درخواست تعریف شده‌ی در اینجا loginRequest است. این نام مهم است؛ از این جهت که با اشاره‌ی به آن می‌توان به فیلدهای خروجی response حاصل، در درخواست‌های بعدی، دسترسی یافت.
- سپس، آدرس درخواست مشخص شده‌است. در اینجا روش کار با متغیرها را نیز مشاهده می‌کنید.
- نوع درخواست POST است.
- در ادامه جزئیات اطلاعات ارسالی به سمت سرور باید مشخص شوند. برای مثال در اینجا با فرمت application/json قرار است یک شیء تشکیل شده‌ی از username و password ارسال شوند.
- در سطر بعدی، خاصیت log با متغیر logResponse مقدار دهی شده‌است. اگر به true تنظیم شود، اصل خروجی response را توسط برنامه‌ی خط فرمان strest می‌توان مشاهده کرد. اگر اینکار خروجی را شلوغ کرد، می‌توان آن‌را به false تنظیم کرد و این خروجی را در فایل strest_history.json نهایی که حاصل از اجرای آزمایش‌های تعریف شده‌است، در کنار فایل JWT.strest.yml خود یافت و مشاهده کرد.
- سپس به قسمت آزمودن نتیجه‌ی درخواست می‌رسیم. در اینجا انتظار داریم که درخواست حاصل که با فرمت json است، دارای دو خاصیت رشته‌ای access_token و refresh_token باشد.


 مرحله‌ی 3: ذخیره سازی توکن‌های دریافتی در متغیرهای سراسری
 مرحله‌ی 3: ذخیره سازی مراحل انجام شده
در حین کار با strest نیازی به ذخیره سازی نتیجه‌ی حاصل از response، در متغیرهای خاصی نیست. برای مثال اگر بخواهیم به نتیجه‌ی حاصل از عملیات لاگین فوق در درخواست‌های بعدی دسترسی پیدا کنیم، می‌توان نوشت <$ loginRequest.content.access_token $>
در اینجا درج متغیرها توسط <$ $> صورت می‌گیرد. سپس loginRequest به نام درخواست مرتبط اشاره می‌کند. خاصیت content.access_token نیز مقدار خاصیت access_token شیء response را بر می‌گرداند.

همچنین ذخیره سازی مراحل انجام شده نیز نکته‌ی خاصی را به همراه ندارد. یک تک فایل متنی JWT.strest.yml وجود دارد که آزمایش‌های ما در آن درج می‌شوند.


مرحله‌ی 4: دسترسی به منابع محافظت شده‌ی سمت سرور

در ادامه روش تعریف دو درخواست جدید دیگر را در فایل JWT.strest.yml مشاهده می‌کنید که از نوع Get هستند و به اکشن متدهای محافظت شده ارسال می‌شوند:
  myProtectedApiRequest:
    request:
      url: <$ baseUrl $>/MyProtectedApi
      method: GET
      headers:
        - name: Authorization
          value: Bearer <$ loginRequest.content.access_token $>
    log: <$ logResponse $>
    validate:
      - jsonpath: content.title
        expect: "Hello from My Protected Controller! [Authorize]"

  mProtectedAdminApiRequest:
    request:
      url: <$ baseUrl $>/MyProtectedAdminApi
      method: GET
      headers:
        - name: Authorization
          value: Bearer <$ loginRequest.content.access_token $>
    log: <$ logResponse $>
    validate:
      - jsonpath: content.title
        expect: "Hello from My Protected Admin Api Controller! [Authorize(Policy = CustomRoles.Admin)]"
دو نکته‌ی جدید در اینجا قابل مشاهده‌است:
- چون نیاز است به همراه درخواست خود، هدر اعتبارسنجی مبتنی بر JWT را که به صورت Bearer value است نیز به سمت سرور ارسال کنیم، خاصیت headers را توسط یک name/value مشخص کرده‌ایم. همانطور که عنوان شد در فایل‌های yaml، فاصله‌ها و تو رفتگی‌ها مهم هستند و حتما باید رعایت شوند.
- سپس دومین آزمون نوشته شده را نیز مشاهده می‌کنید. در قسمت validate، مشخص کرده‌ایم که خاصیت title دریافتی از response باید مساوی مقدار خاصی باشد.

دقیقا همین نکات برای درخواست دوم به MyProtectedAdminApi تکرار شده‌اند.


مرحله‌ی 5: ارسال Refresh token و دریافت یک سری توکن جدید

اکشن متد account/RefreshToken در سمت سرور، نیاز دارد تا یک شیء جی‌سون با خاصیت refreshToken را دریافت کند. مقدار این خاصیت از طریق response متناظر با درخواست نام‌دار loginRequest استخراج می‌شود که در قسمت postData مشخص شده‌است:
  refreshTokenRequest:
    request:
      url: <$ baseUrl $>/account/RefreshToken
      method: POST
      postData:
        mimeType: application/json
        text:
          refreshToken: <$ loginRequest.content.refresh_token $>
    log: <$ logResponse $>
    validate:
      - jsonpath: content.access_token
        type: [string]
      - jsonpath: content.refresh_token
        type: [string]
در آخر، به قسمت آزمودن نتیجه‌ی درخواست می‌رسیم. در اینجا انتظار داریم که درخواست حاصل که با فرمت json است، دارای دو خاصیت رشته‌ای access_token و refresh_token باشد که بیانگر صدور توکن‌های جدیدی هستند.


مرحله‌ی 6: آزمایش توکن جدید دریافتی از سرور

در قسمت قبل، توکن‌های جدیدی صادر شدند که اکنون برای کار با آن‌ها می‌توان از متغیر refreshTokenRequest.content.access_toke استفاده کرد:
  myProtectedApiRequestWithNewToken:
    request:
      url: <$ baseUrl $>/MyProtectedApi
      method: GET
      headers:
        - name: Authorization
          value: Bearer <$ refreshTokenRequest.content.access_token $>
    log: <$ logResponse $>
    validate:
      - jsonpath: content.title
        expect: "Hello from My Protected Controller! [Authorize]"
در اینجا با استفاده از توکن جدید درخواست نام‌دار refreshTokenRequest، آزمون واحد نوشته شده با موفقیت به پایان می‌رسد (یا باید برسد که اجرای نهایی آزمایش‌ها، آن‌را مشخص می‌کند).


مرحله‌ی 7: آزمایش منقضی شدن توکنی که در ابتدای کار پس از لاگین دریافت کردیم

اکنون که refresh token صورت گرفته‌است، دیگر نباید بتوانیم از توکن دریافتی پس از لاگین استفاده کنیم و برنامه باید آن‌را برگشت بزند:
  myProtectedApiRequestWithOldToken:
    request:
      url: <$ baseUrl $>/MyProtectedApi
      method: GET
      headers:
        - name: Authorization
          value: Bearer <$ loginRequest.content.access_token $>
    log: <$ logResponse $>
    validate:
      - jsonpath: status
        expect: 401
به همین جهت، درخواستی ارسال شده که به نتیجه‌ی درخواست نام‌دار loginRequest اشاره می‌کند. در این حالت برای آزمایش عملیات، اینبار status بازگشتی از سرور که باید 401 باشد، بررسی شده‌است.


مرحله‌ی 8: آزمایش خروج از سیستم

در اینجا نیاز است به آدرس account/logout، یک کوئری استرینگ را با کلید refreshToken و مقدار ریفرش‌توکن دریافتی از درخواست نام‌دار refreshTokenRequest، به سمت سرور ارسال کنیم:
  logoutRequest:
    request:
      url: <$ baseUrl $>/account/logout
      method: GET
      headers:
        - name: Authorization
          value: Bearer <$ refreshTokenRequest.content.access_token $>
      queryString:
        - name: refreshToken
          value: <$ refreshTokenRequest.content.refresh_token $>
    log: <$ logResponse $>
    validate:
      - jsonpath: content
        expect: true
خروجی آزمایش شده‌ی در اینجا، دریافت مقدار true از سمت سرور است.


مرحله‌ی 9: بررسی عدم امکان دسترسی به منابع محافظت شده‌ی سمت سرور، پس از logout

در مرحله‌ی قبل، از سیستم خارج شدیم. اکنون می‌خواهیم بررسی کنیم که آیا توکن دریافتی پیشین هنوز معتبر است یا خیر؟ آیا می‌توان هنوز هم به منابع محافظت شده دسترسی یافت یا خیر:
  myProtectedApiRequestWithNewTokenAfterLogout:
    request:
      url: <$ baseUrl $>/MyProtectedApi
      method: GET
      headers:
        - name: Authorization
          value: Bearer <$ refreshTokenRequest.content.access_token $>
    log: <$ logResponse $>
    validate:
      - jsonpath: status
        expect: 401
به همین جهت هدر Authorization را با اکسس‌توکنی که در مرحله‌ی ریفرش‌توکن دریافت کردیم (پیش از logout)، مقدار دهی می‌کنیم و سپس درخواستی را به یک منبع محافظت شده ارسال می‌کنیم. نتیجه‌ی حاصل باید status code ای مساوی 401 داشته باشد که به معنای برگشت خوردن آن است


مرحله‌ی 10: اجرای تمام آزمون‌های واحد نوشته شده

همانطور که در ابتدای بحث نیز عنوان شد فقط کافی است دستور strest JWT.strest.yml را در خط فرمان اجرا کنیم تا آزمون‌های ما به ترتیب اجرا شوند:


فایل نهایی این آزمایش را در اینجا می‌توانید مشاهده می‌کنید.
مطالب
امن سازی برنامه‌های ASP.NET Core توسط IdentityServer 4x - قسمت سوم - بررسی مفاهیم OpenID Connect
پیش از نصب و راه اندازی IdentityServer، نیاز است با یک سری از مفاهیم اساسی پروتکل OpenID Connect، مانند Identity token ،Client types ،Flow و Endpoints، آشنا شویم تا بتوانیم از امکانات این IDP ویژه استفاده و آن‌ها را تنظیم کنیم. بدون آشنایی با این مفاهیم، اتصال برنامه‌ای که در قسمت قبل تدارک دیدیم به IdentityServer میسر نیست.


پروتکل OpenID Connect چگونه کار می‌کند؟

در انتهای قسمت اول این سری، پروتکل OpenID Connect معرفی شد. در ادامه جزئیات بیشتری از این پروتکل را بررسی می‌کنیم.
هر برنامه‌ی کلاینت متصل به WebAPI مثال قسمت قبل، نیاز به دانستن هویت کاربر وارد شده‌ی به آن‌را دارد. در اینجا به این برنامه‌ی کلاینت، اصطلاحا relying party هم گفته می‌شود؛ از این جهت که این برنامه‌ی کلاینت، به برنامه‌ی Identity provider و یا به اختصار IDP، جهت دریافت نتیجه‌ی اعتبارسنجی کاربر، وابسته‌است. برنامه‌ی کلاینت یک درخواست Authentication را به سمت IDP ارسال می‌کند. به این ترتیب کاربر به صورت موقت از برنامه‌ی جاری خارج شده و به برنامه‌ی IDP منتقل می‌شود. در برنامه‌ی IDP است که کاربر مشخص می‌کند کیست؛ برای مثال با ارائه‌ی نام کاربری و کلمه‌ی عبور. پس از این مرحله، در صورت تائید هویت کاربر، برنامه‌ی IDP یک Identity Token را تولید و امضاء می‌کند. سپس برنامه‌ی IDP کاربر را مجددا به برنامه‌ی کلاینت اصلی هدایت می‌کند و Identity Token را در اختیار آن کلاینت قرار می‌دهد. در اینجا برنامه‌ی کلاینت، این توکن هویت را دریافت و اعتبارسنجی می‌کند. اگر این اعتبارسنجی با موفقیت انجام شود، اکنون کاربر تعیین اعتبار شده و هویت او جهت استفاده‌ی از قسمت‌های مختلف برنامه مشخص می‌شود. در برنامه‌های ASP.NET Core، این توکن هویت، پردازش و بر اساس آن یکسری Claims تولید می‌شوند که در اغلب موارد به صورت یک کوکی رمزنگاری شده در سمت کلاینت ذخیره می‌شوند.
به این ترتیب مرورگر با هر درخواستی از سمت کاربر، این کوکی را به صورت خودکار به سمت برنامه‌ی کلاینت ارسال می‌کند و از طریق آن، هویت کاربر بدون نیاز به مراجعه‌ی مجدد به IDP، استخراج و استفاده می‌شود.


مراحل انتقال کاربر به IDP، صدور توکن هویت، بازگشت مجدد به برنامه‌ی کلاینت، اعتبارسنجی، استخراج Claims و ذخیره‌ی آن به صورت یک کوکی رمزنگاری شده را در تصویر فوق ملاحظه می‌کنید. بنابراین در حین کار با یک IDP، مرحله‌ی لاگین به سیستم، دیگر در برنامه یا برنامه‌های کلاینت قرار ندارد. در اینجا دو فلش به سمت IDP و سپس به سمت کلاینت را بخاطر بسپارید. در ادامه از آن‌ها برای توضیح Flow و Endpoints استفاده خواهیم کرد.

البته OpenID Connect برای کار همزمان با انواع و اقسام برنامه‌های کلاینت طراحی شده‌است؛ مانند برنامه‌ی سمت سرور MVC، برنامه‌های سمت کلاینت جاوا اسکریپتی مانند Angular و برنامه‌های موبایل. برای این منظور باید در IDP نوع کلاینت و یکسری از تنظیمات مرتبط با آن‌را مشخص کرد.


کلاینت‌های عمومی و محرمانه

زمانیکه قرار است با یک IDP کار کنیم، این IDP باید بتواند بین یک برنامه‌ی حسابداری و یک برنامه‌ی پرسنلی که از آن برای احراز هویت استفاده می‌کنند، تفاوت قائل شود و آن‌ها را شناسایی کند.

- کلاینت محرمانه (Confidential Client)
هر کلاینت با یک client-id و یک client-secret شناخته می‌شود. کلاینتی که بتواند محرمانگی این اطلاعات را حفظ کند، کلاینت محرمانه نامیده می‌شود.
در اینجا هر کاربر، اطلاعات هویت خود را در IDP وارد می‌کند. اما اطلاعات تعیین هویت کلاینت‌ها در سمت کلاینت‌ها ذخیره می‌شوند. برای مثال برنامه‌های وب ASP.NET Core می‌توانند هویت کلاینت خود را به نحو امنی در سمت سرور ذخیره کنند و این اطلاعات، قابل دسترسی توسط کاربران آن برنامه نیستند.

- کلاینت عمومی (Public Client)
این نوع کلاینت‌ها نمی‌توانند محرمانگی هویت خود را حفظ و تضمین کنند؛ مانند برنامه‌های جاوا اسکریپتی Angular و یا برنامه‌های موبایل که بر روی وسایل الکترونیکی کاربران اجرا می‌شوند. در این حالت هرچقدر هم سعی کنیم، چون کاربران به اصل این برنامه‌ها دسترسی دارند، نمی‌توان محرمانگی اطلاعات ذخیره شده‌ی در آن‌ها را تضمین کرد.


مفهوم OpenID Connect Endpoints

در تصویر ابتدای بحث، دو فلش را مشاهده می‌کنید؛ برای مثال چگونه می‌توان به Identity token دسترسی یافت (Authentication) و همچنین زمانیکه صحبت از Authorization می‌شود، چگونه می‌توان Access tokens را دریافت کرد. اینکه این مراحله چگونه کار می‌کنند، توسط Flow مشخص می‌شود. Flow مشخص می‌کند که چگونه باید توکن‌ها از سمت IDP به سمت کلاینت بازگشت داده شوند. بسته به نوع کلاینت‌ها که در مورد آن‌ها بحث شد و نیازمندی‌های برنامه، باید از Flow مناسبی استفاده کرد.
هر Flow با Endpoint متفاوتی ارتباط برقرار می‌کند. این Endpointها در حقیقت جایگزین راه‌حل‌های خانگی تولید برنامه‌های IDP هستند.
- در ابتدا یک Authorization Endpoint وجود دارد که در سطح IDP عمل می‌کند. این مورد همان انتهای فلش اول ارسال درخواست به سمت IDP است؛ در تصویر ابتدای بحث. کار این Endpoint، بازگشت Identity token جهت انجام عملیات Authentication و بازگشت Access token برای تکمیل عملیات Authorization است. این عملیات نیز توسط Redirection کلاینت انجام می‌شود (هدایت کاربر به سمت برنامه‌ی IDP، دریافت توکن‌ها و سپس هدایت مجدد به سمت برنامه‌ی کلاینت اصلی).

نکته‌ی مهم: استفاده‌ی از TLS و یا همان پروتکل HTTPS برای کار با OpenID Connect Endpoints اجباری است و بدون آن نمی‌توانید با این سیستم کار کنید. به عبارتی در اینجا ترافیک بین کلاینت و IDP، همواره باید رمزنگاری شده باشد.
البته مزیت کار با ASP.NET Core 2.1، یکپارچگی بهتر و پیش‌فرض آن با پروتکل HTTPS است؛ تا حدی که مثال پیش‌فرض local آن به همراه یک مجوز موقتی SSL نصب شده‌ی توسط SDK آن کار می‌کند.

- پس از Authorization Endpoint که در مورد آن توضیح داده شد، یک Redirection Endpoint وجود دارد. در ابتدای کار، کلاینت با یک Redirect به سمت IDP هدایت می‌شود و پس از احراز هویت، مجددا کاربر به سمت کلاینت Redirect خواهد شد. به آدرسی که IDP کاربر را به سمت کلاینت Redirect می‌کند، Redirection Endpoint می‌گویند و در سطح کلاینت تعریف می‌شود. برنامه‌ی IDP، اطلاعات تولیدی خود را مانند انواع توکن‌ها، به سمت این Endpoint که در سمت کلاینت قرار دارد، ارسال می‌کند.

- پس از آن یک Token Endpoint نیز وجود دارد که در سطح IDP تعریف می‌شود. این Endpoint، آدرسی است در سمت IDP، که برنامه‌ی کلاینت می‌تواند با برنامه نویسی، توکن‌هایی را از آن درخواست کند. این درخواست عموما از نوع HTTP Post بدون Redirection است.


مفهوم OpenID Connect Flow


- اولین Flow موجود، Authorization Code Flow است. کار آن بازگشت کدهای Authorization از Authorization Endpoint و همچنین توکن‌ها از طریق Token Endpoint می‌باشد. در ایجا منظور از «کدهای Authorization»، اطلاعات دسترسی با طول عمر کوتاه است. هدف Authorization Code این است که مشخص کند، کاربری که به IDP لاگین کرده‌است، همانی است که Flow را از طریق برنامه‌ی وب کلاینت، شروع کرده‌است. انتخاب این نوع Flow، برای کلاینت‌های محرمانه مناسب است. در این حالت می‌توان مباحث Refresh token و داشتن توکن‌هایی با طول عمر بالا را نیز پیاده سازی کرد.

- Implicit Flow، تنها توکن‌های تولیدی را توسط Authorization Endpoint بازگشت می‌دهد و در اینجا خبری از بازگشت «کدهای Authorization» نیست. بنابراین از Token Endpoint استفاده نمی‌کند. این نوع Flow، برای کلاینت‌های عمومی مناسب است. در اینجا کار client authentication انجام نمی‌شود؛ از این جهت که کلاینت‌های عمومی، مناسب ذخیره سازی client-secret نیستند. به همین جهت در اینجا امکان دسترسی به Refresh token و توکن‌هایی با طول عمر بالا میسر نیست. این نوع از Flow، ممکن است توسط کلاینت‌های محرمانه نیز استفاده شود.

- Hybrid Flow، تعدادی از توکن‌ها را توسط Authorization Endpoint و تعدادی دیگر را توسط Token Endpoint بازگشت می‌دهد؛ بنابراین ترکیبی از دو Flow قبلی است. انتخاب این نوع Flow، برای کلاینت‌های محرمانه مناسب است. در این حالت می‌توان مباحث Refresh token و داشتن توکن‌هایی با طول عمر بالا را نیز پیاده سازی کرد. از این نوع Flow ممکن است برای native mobile apps نیز استفاده شود.

آگاهی از انواع Flowها، انتخاب نوع صحیح آن‌ها را میسر می‌کند که در نتیجه منتهی به مشکلات امنیتی نخواهند شد. برای مثال Hybrid Flow توسط پشتیبانی از Refresh token امکان تمدید توکن جاری و بالا بردن طول عمر آن‌را دارد و این طول عمر بالا بهتر است به کلاینت‌های اعتبارسنجی شده ارائه شود. برای اعتبارسنجی یک کلاینت، نیاز به client-secret داریم و برای مثال برنامه‌های جاوا اسکریپتی نمی‌توانند محل مناسبی برای ذخیره سازی client-secret باشند؛ چون از نوع کلاینت‌های عمومی محسوب می‌شوند. بنابراین نباید از Hybrid Flow برای برنامه‌های Angular استفاده کرد. هرچند انتخاب این مساله صرفا به شما بر می‌گردد و چیزی نمی‌تواند مانع آن شود. برای مثال می‌توان Hybrid Flow را با برنامه‌های Angular هم بکار برد؛ هرچند ایده‌ی خوبی نیست.


انتخاب OpenID Connect Flow مناسب برای یک برنامه‌ی کلاینت از نوع ASP.NET Core

برنامه‌های ASP.NET Core، از نوع کلاینت‌های محرمانه به‌شمار می‌روند. بنابراین در اینجا می‌توان تمام Flowهای یاد شده را انتخاب کرد. در برنامه‌های سمت سرور وب، به ویژگی به روز رسانی توکن نیاز است. بنابراین باید دسترسی به Refresh token را نیز داشت که توسط Implicit Flow پشتیبانی نمی‌شود. به همین جهت از Implicit Flow در اینجا استفاده نمی‌کنیم. پیش از ارائه‌ی OpenID Connect، تنها Flow مورد استفاده‌ی در برنامه‌های سمت سرور وب، همان Authorization Code Flow بود. در این Flow تمام توکن‌ها توسط Token Endpoint بازگشت داده می‌شوند. اما Hybrid Flow نسبت به آن این مزیت‌ها را دارد:
- ابتدا اجازه می‌دهد تا Identity token را از IDP دریافت کنیم. سپس می‌توان آن‌را بدون دریافت توکن دسترسی، تعیین اعتبار کرد.
- در ادامه OpenID Connect این Identity token را به یک توکن دسترسی، متصل می‌کند.
به همین جهت OpenID Connect نسبت به OAuth 2 ارجحیت بیشتری پیدا می‌کند.

پس از آشنایی با این مقدمات، در قسمت بعدی، کار نصب و راه اندازی IdentityServer را انجام خواهیم داد.
مطالب
پَرباد - راهنمای اتصال و پیاده‌سازی درگاه‌های پرداخت اینترنتی (شبکه شتاب)

پَرباد چیست؟

همانطور که همه ما میدانیم، اتصال و راه اندازی درگاه‌های پرداخت اینترنتی (شبکه شتاب)، از همان ابتدا کاری مشکل و  پر دردسر برای برنامه نویسان بود. هر بانک، سیستم متفاوت و مخصوص به خود را دارد و این بدان معنا است که برنامه نویسان باید کدهای کاملا متفاوت و همچنین پیاده سازی‌های متفاوتی را از روی فایل‌های PDF راهنمای بانکی، که در نهایت منجر به بی نظمی در پروژه‌ها می‌شود، بنویسند و البته مشکل بزرگتر آن است که پس از پیاده سازی هم اطمینان کاملی از صحت کدهای نوشته شده وجود ندارد؛ چه بسا که واحد‌های پشتیبانی درگاه‌های پرداخت هم افراد حرفه‌ای و آشنا با توسعه نرم افزار نیستند و اکثر اوقات نمی‌توان به آنها تکیه کرد.
برای راحتی کار برنامه نویسان حوضه فریم ورک دات نت، سیستمی جامع، اوپن سورس و کاملا رایگان، بدون نیاز به اضافه کردن هیچ گونه وب سرویسی تهیه شده است که به برنامه نویسان اجازه می‌دهد تنها با نوشتن چند خط کد، وب سایت خود را به پرداخت اینترنتی مجهز کنند. لطفا پیشنهادات، بحث‌ها و نظرات خود را در صفحه مخصوص این پروژه ارسال کنید.  
این سیستم در حال حاضر متشکل از درگاه‌های پرداخت اینترنتی بانک‌های ملت، سامان، پارسیان، تجارت و پاسارگاد است.
همچنین این سیستم در قالب یک Nuget Package برای نصب راحت در اپلیکیشن آماده شده است.


آنچه که شما در این مطلب یاد خواهید گرفت:

  • طریقه نصب
  • ایجاد صورتحساب و ارسال کاربر به درگاه پرداخت
  • تایید صورتحساب
  • مردود کردن صورتحساب قبل از انتقال وجه از مشتری به فروشنده
  • برگشت وجه به حساب مشتری پس از تأیید صورتحساب
  • درگاه مجازی پرداخت (برای تست وب اپلیکیشن، بدون داشتن حساب واقعی در درگاه‌های بانکی)
  • تنظیمات
  • ذخیره سازی اطلاعات پرداخت


طریقه نصب

PM> Install-Package Parbad

برای وب سایت‌های بر پایه فریم ورک MVC

PM> Install-Package Parbad.MVC5


ایجاد صورتحساب و ارسال کاربر به درگاه پرداخت

ابتدا یک شیٔ صورتحساب را به صورت زیر ایجاد کنید
var invoice = new Invoice( [Order Number], [Amount], [Verify URL]);

- Order Number شماره صورتحساب است و باید همیشه یک عدد یکتا باشد (تکراری نباشد).
- Amount مبلغ قابل پرداخت به ریال است.
- Verify URL یک آدرس در وب سایت شما، برای بازگشت مشتری پس از پرداخت و تأیید صورتحساب است.
برای مثال:
var invoice = new Invoice(1, 30000, "http://www.mywebsite.com/payment/verify" );
سپس صورتحساب را به درگاه مورد نظر ارسال میکنیم.
var result = Payment.Request(Gateways.Mellat, invoice);

شیٔ result حاوی شماره یکتا رجوع و وضعیت درخواست (موفقیت یا عدم موفقیت درخواست) است.
if (result.Status == RequestResultStatus.Success)
{
    // این متد، کاربر را به سمت وب سایت درگاه پرداخت هدایت میکند
    result.Process(Context);
}
else
{
    // در صورت تمایل می‌توانید پیغام مورد نظر از درگاه پرداخت را نمایش دهید
    var msg = result.Message;
}

در وب سایت‌های MVC می‌توانید به روش زیر عمل کنید

if (result.Status == RequestResultStatus.Success)
{
   // کاربر را به سمت وب سایت درگاه پرداخت هدایت میکند 
   return new RequestActionResult(result);
}
else
{
   return View("Error");
}


تأیید صورتحساب

پس از بازگشت کاربر از وب سایت بانک، باید از پرداخت صورتحساب توسط کاربر اطمینان حاصل کنید. کد زیر را باید در آدرسی که هنگام ساخت صورتحساب ذکر کرده بودید، قرار دهید.
var result = Payment.Verify(System.Web.HttpContext.Current);

شیٔ result در اینجا حاوی اطلاعاتی مانند: درگاه بانکی (که کاربر در آن صورتحساب را پرداخت کرده)، شماره رجوع، شماره تراکنش یکتای بانکی، وضعیت پرداخت و پیام درگاه است.
شما می‌توانید با بررسی این شیٔ، تصمیمات لازم را بگیرید.
if(result.Status == VerifyResultStatus.Success)
{
    // کاربر، صورتحساب را پرداخت کرده است و شما میتوانید ادامه عملیات خرید را انجام دهید
}
else
{
    // کاربر بنا به دلایلی صورتحساب را پرداخت نکرده است
    // شما همچنین میتوانید علت را در قالب یک پیام از پراپرتی پیام مشاهده کنید

    // بنابراین شما میتوانید این صورتحساب را در پایگاه داده خود مردود اعلام کنید
}


مردود کردن صورتحساب قبل از انتقال وجه از مشتری به فروشنده

در بعضی شرایط، پس از پرداخت صورتحساب توسط مشتری، شما متوجه می‌شوید که باید عملیات را لغو کنید.  
سناریو زیر را در نظر بگیرید:
در زمانیکه مشتری در وب سایت بانکی، صورتحساب را پرداخت میکرده است،  موجودی کالای خریداری شده توسط او در فروشگاه شما، به پایان رسیده ! حال باید این وجه پرداخت شده را فورا مردود اعلام کنید.
برای این منظور متد تأیید صورتحساب را به روش زیر بازنویسی کنید



همانطور که در تصویر می‌بینید، در هنگام بازگشت مشتری به وب سایت شما و تأیید کردن صورتحساب، شما می‌توانید اطلاعات تراکنش مورد نظر را که شامل، درگاه پرداخت بانکی، شماره سفارش و شماره رجوع است را دریافت کنید و سپس با استفاده از این اطلاعات، پایگاه داده خود را بررسی کرده و در صورت لزوم، متد Cancel را فراخوانی کنید. به این ترتیب به درگاه بانکی، هیچگونه تأییدیه ای اعلام نمی‌شود و این بدان معناست که اگر وجهی به حساب فروشگاه واریز شده باشد، پس از چند دقیقه (معمولا ۱۵ دقیقه) به حساب مشتری برگشت داده خواهد شد.


برگشت وجه به حساب مشتری پس از تأیید صورتحساب

var refundResult = Payment.Refund(new RefundInvoice([Order Number], [Amount]));
در اینجا، Order Number همان شماره سفارش صورتحساب و Amount مقداری از وجه و یا کل وجه برای برگشت به حساب مشتری است.
پس از آن شما می‌توانید نتیجه این عملیات را در شیٔ refundResult بررسی کنید.


درگاه مجازی پرداخت

درصورتیکه شما نیاز به تست عملکرد اپلیکیشن خود داشته باشید، نیازی به داشتن یک حساب واقعی در بانک‌های اینترنتی ندارید و می‌توانید اپلیکیشن خود را با یک درگاه مجازی بسیار ساده تست کنید. برای انجام این کار در هنگام ارسال صورتحساب، از میان درگاه‌های بانکی، درگاه مجازی پَرباد را انتخاب کنید.
var result = Payment.Request(Gateways.ParbadVirtualGateway, invoice);


در نتیجه در هنگام هدایت کاربر به درگاه پرداخت، کاربر به درگاه مجازی هدایت خواهد شد.

اما قبل از کار با درگاه مجازی باید در فایل web.config وب اپلیکیشن خود، تنظیمات زیر را قرار دهید:
<system.webServer>
  <handlers>
   <add name="ParbadGatewayPage" verb="*" path="Parbad.axd" type="Parbad.Web.Gateway.ParbadVirtualGatewayHandler" />
  </handlers>
</system.webServer>
در اینجا، درگاه مجازی به عنوان یک HttpHandler معرفی شده است. مقداری که در مشخصه path ذکر شده، در واقع آدرس درگاه مجازی است که شما می‌توانید به دلخواه خود آن را وارد کنید. ما در این مثال از آدرس parbad.axd استفاده کرده ایم.
و در نهایت در وب اپلیکیشن خود، مسیر ذکر شده را به صورت زیر معرفی کنید:
ParbadConfiguration.Gateways.ConfigureParbadVirtualGateway(new ParbadVirtualGatewayConfiguration("Parbad.axd"));
در نتیجه در هنگام هدایت کاربر به درگاه مجازی، شما باید در نوار آدرس مرورگر خود، مقداری را که تنظیم کرده اید مشاهده کنید.


نکته مهم: فراموش نکنید، قبل از انتشار نهایی وب سایت بر روی سرور (نمایش عمومی)، تنظیمات HttpHandler مربوط به این درگاه مجازی را از درون فایل web.config حذف کنید. بدین صورت، این درگاه از دسترس عموم خارج خواهد بود.

تنظیمات پَرباد

بهترین مکان برای درج این تنظیمات در اپلیکیشن‌های ASP.NET WebForms فایل Global.asax.cs و در اپلیکیشن‌های ASP.NET MVC فایل Startup.cs است.
ASP.NET Web Forms
public class Global : HttpApplication
{
    void Application_Start(object sender, EventArgs e)
    {
        // configurations
    }
}

ASP.NET MVC
public class Startup
{
    public void Configuration(IAppBuilder app)
    {
        // configurations
    }
}

تنظیمات درگاه‌های پرداخت

قبل از ارتباط با درگاه‌های بانکی شبکه شتاب، باید مشخصات درگاه بانکی را که استفاده می‌کنید، تنظیم کنید.
برای مثال: تنظیم درگاه پرداخت بانک ملت


تنظیمات ذخیره سازی اطلاعات پرداخت

پَرباد برای ذخیره و بازیابی اطلاعات پرداخت، نیاز به یک منبع ذخیره سازی دارد.
منبع پیش فرض پَرباد، کلاس TemporaryMemoryStorage است که همانطور که از نام آن پیداست، اطلاعات را به صورت موقت در حافظه رَم سرور ذخیره میکند. اگر شما خودتان اطلاعات پرداخت را در پایگاه داده ذخیره میکنید، این منبع، گزینه مناسبی است به دلیل سرعت بسیار بالای حافظه رَم.
توجه: در نظر داشته باشید که اگر به هر دلیلی سرور و یا وب سایت شما، ری‌استارت شود، کلیه اطلاعات موجود در این منبع هم از بین خواهد رفت.
ذخیره و بازیابی توسط SQL Server
برای این منظور در قسمت تنظیمات، کد زیر را قرار داده و رشته اتصال و نام جدول پرداخت را معرفی کنید.
ParbadConfiguration.Storage = new SqlServerStorage("Connection String", "MyPaymentTableName");

فیلد‌های مورد نیاز در این جدول:

ذخیره و بازیابی اطلاعات توسط روش مورد نظر شما:
در صورتیکه مایلید ذخیره و بازیابی را به روش خود انجام دهید، کلاس Storage را پیاده سازی کنید
public class MyStorage : Storage
{
    // Implement methods here...
}

و کلاس مورد نظر را در تنظیمات به عنوان منبع، معرفی کنید.
ParbadConfiguration.Storage = new MyStorage();

لازم به ذکر است که این کلاس شامل متد‌های synchronous و همچنین asynchronous است. بنابراین در صورتیکه برای مثال در هنگام ارسال درخواست به بانک، از متد‌های async استفاده می‌کنید، نیازی به پیاده سازی کردن متد‌های synchronous نیست.
در صورتیکه هر گونه پیشنهاد یا انتقاد نسبت به کارکرد این سیستم دارید، صمیمانه منتظر شنیدن آن در راستای توسعه این سیستم هستم.
همچنین در صورت تمایل به توسعه آن، می‌توانید آن را در گیت هاب دنبال کنید و یا لطفا پیشنهادات، بحث‌ها و نظرات خود را در صفحه مخصوص این پروژه ارسال کنید. 
با تشکر.
مطالب
ایجاد دامنه‌های سفارشی در IIS Express
اگر شما در حال راه اندازی برنامه‌ای هستید که از یک زیر دامنه یا نام دامنه برای شناسایی یک کاربر یا زیرمجموعه دینامیک استفاده می‌کند، ممکن است در تلاش برای آزمایش قابلیت‌های زیر دامنه به صورت محلی مشکلی داشته باشید. در این مقاله قصد داریم یک وب سایت با استفاده از domain و subdomains  را به صورت محلی بر روی IIS Express اجرا کنیم.

اولا، اجازه بدهید نگاهی به تنظیم یک دامنه محلی داشته باشیم. زمانیکه شما برنامه محلی را اجرا می‌کنید IIS Express به صورت محلی، پورتی خاص را به برنامه اختصاص می‌دهد:


فرض کنید می‌خواهیم برای سایت خود، درگاه بانک را راه اندازی کنیم. برنامه را به صورت محلی اجرا کرده و  زمانیکه قصد ارتباط با بانک را دارید، با پیامی که دامنه شما در سیستم پرداخت بانکی ثبت نشده، مواجه می‌شوید. در اینجا بانک انتظار دارد که ما از طریق دامنه‌ای که قبلا در سیستم پرداخت بانک ثبت کرده‌ایم برای مثال (www.elemarket.ir) با آن ارتباط برقرار کنیم؛ ولی به دلیل ارتباط به صورت محلی با یک‌چنین دامنه‌ای (localhost:59395) روبه‌رو شده و پیغام عدم امکان برقراری ارتباط را میدهد.

حال قصد داریم با تغییر دامنه به صورت سفارشی، این مشکل را برطرف کرده، تا درحقیقت  قبل از قرار دادن سایت بر روی سرور و تست عملیات بتوانیم به صورت محلی نتیجه را دریافت کنیم.


استفاده از Telerik Fiddler برای ایجاد یک دامنه‌ی سفارشی

برای این کار می‌توانید از برنامه‌ی سبک وزن Telerik Fiddler استفاده کنید و تنها کافیست به قسمت Tools>Host برنامه بروید و آدرس محلی برنامه (localhost:59395 ) و آدرس دامنه‌ی مورد نظر را وارد کنید تا برنامه هم به صورت local و هم توسط یک دامنه‌ی سفارشی، در دسترس باشد. برنامه‌ی Fiddler را باز نگه داشته و به ویژوال استودیو بازگرید.
 


در نهایت، پیکربندی IIS Express خود را با اتصالهای جدید به روز کنید. شما معمولا میتوانید پیکربندی IIS Express Application خود را در این مسیر پیدا کنید .
  C: \ Users \ YOUR_USERNAME \ Documents \ IISExpress \ config / applicationhost.config
 اگر از نسخه‌های ویژوال استودیو 2015 به بعد استفاده میکنید، فایل applicationhost.config  شما در داخل پوشه‌ی برنامه شما  vs\config\applicationhost.config.\~
در دسترس است.
فایل را باز کرده و گره <sites> را جستجو کنید. شما باید بتوانید درخواست خود را در فهرست سایت‌ها مشاهده کنید. ما قصد داریم HTTP binding را با تغییر localhost به نام دامنه‌ی سفارشی خود به‌روز رسانی کنیم. در اینجا HTTP binding به صورت پیشفرض بر روی localhost میباشد:


پس از  تغییر localhost و ذخیره کردن تنظیمات، بررسی کنید تا IIS Express در حال اجرا نباشد. حال برنامه را اجرا کنید.


نکته:
اگر هنگام اجرای برنامه با خطای
  “Unable to create the virtual directory. You must use specify ‘localhost’ for the server name”
و یا
 " Invalid URL:the hostame could not be parsed "
مواجه شدید، برنامه را بسته و در حالت Run as Administrator اجرا کنید.

بعد از اجرای برنامه به طور پیشفرض بر روی همان پورت localhost اجرا شده، حال به دامنه‌ی مورد نظر که ایجاد کرده‌اید بروید:


در اینجا بعد از تلاش برای ارتباط با بانک، دیگر با پیام «دامنه شما در سیستم پرداخت بانکی ثبت نشده‌است» مواجه نشده و با موفقیت امکان تست برنامه را داریم:


 
نظرات مطالب
افزودن و اعتبارسنجی خودکار Anti-Forgery Tokens در برنامه‌های Angular مبتنی بر ASP.NET Core
ممنون از راهنماییتون

موارد رو انجام دادم، با توجه به جدا بودن دامین API و FRONT یه مشکلی برخورد کردم. 
با زدن درخواست کوکی XSRF-TOKEN ساخته میشه، ولی وقتی درخواست POST میزنم X-XSRF-TONEK توی هدر درخواست اضافه نمیشه
- XsrfInterceptor رو اضافه کردم، توی Module برنامه تعاریف رو انجام دادم
- سمت سرور اومدم کوکی رو به صورت زیر تغییر دادم:
var xsrfTokenCookie = new HttpCookie("XSRF-TOKEN")
{
     Value = ComputeXsrfTokenValue(),
     HttpOnly = false, // Now JavaScript is able to read the cookie
     Path = "/",
     Domain = "localhost"
};
- Cross Origin رو متناسب با دامنه فعال کردم
- حتی از طریق proxy.config آدرس API رو ست کردم
ولی متاسفانه باز جوابی نگرفتم. میخواستم ببینم نکته ای وجود داره که رعایت نکرده باشم در این زمینه؟