نظرات مطالب
مخفی کردن کوئری استرینگ‌ها در ASP.NET MVC توسط امکانات Routing
- بحث مطلب جاری در مورد ASP.NET MVC است و ساز و کار استاندارد آن؛ نه در مورد وب فرم‌ها یا روش‌های سفارشی دیگر. هنگام تعریف مسیر اسکریپت‌ها در MVC اگر از Url.Content و ~ برای ذکر ریشه سایت، استفاده شده باشد، موتور توکار MVC مسیرها را به صورت خودکار اصلاح می‌کند. مثلا:
<script src="@Url.Content("~/Scripts/jquery-1.4.4.min.js")" type="text/javascript"></script>
- پیشتر در مورد دیباگ اسکریپت‌های یک سایت مطلبی تهیه شده بود: (^)

الان مرورگر، مسیر اسکریپت‌های شما را از انتهای مسیر یک مطلب دریافت می‌کند نه از ریشه سایت. برای وب فرم‌ها هم روش ذیل وجود دارد:
<script language="javascript" src='<%=ResolveUrl("~/App_Themes/MainTheme/jquery.js")%>' type='text/javascript'></script>
البته در این حالت هدر صفحه باید runat server داشته باشد:
<head runat="server">
و یا از اسکریپت منیجر استفاده کنید:
<asp:ScriptManager ID="ScriptManager1" runat="server">
        <Scripts>
            <asp:ScriptReference Path="~/js/somefile.js" />
        </Scripts>
</asp:ScriptManager>
اشتراک‌ها
تحریم شکن بگذر!

«با بگذر میتونی همه سایتهایی که ایران را تحریم کردن باز کنی. این سایت‌ها حتی میتونن سایت‌های دانشگاهی، خدماتی و یا هر سایت دیگه ای باشن که دسترسی کاربرای ایرانی را مسدود کردند» 

تحریم شکن بگذر!
اشتراک‌ها
محتوای shape شده با CSS

یکی از جالبترین کارها در یک وب سایت فانتزی شکل دادن به ناحیه متنی است. بعضی از وب سایت‌ها از قبیل مجلات و سرگرمی و بازی و .. از این کارها استقبال می‌کنند.

محتوای shape شده با CSS
مطالب
ساخت کلیدهای امنیتی GunPG
یکی از روش‌های ارسال و رمزگذاری اطلاعات، استفاده از کلیدهای امنیتی مورد استفاده‌ی در سیستم یونیکس یا GnuPG است. استفاده از نرم افزار Gnu Privacy Guard یا گارد حفاظتی گنو، به ما این اجازه را می‌دهد که بتوانیم اطلاعاتمان را در بسترهای ارتباطی، با خیالی راحت‌تر ارسال کنیم و تا حد زیادی مطمئن باشیم که تنها فرد هدف توانایی دسترسی به اطلاعات را خواهد داشت. گارد امنیتی گنو زیر مجموعه‌ای از پروژه‌ی گنو است که دولت آلمان پایه ریز اصلی آن بوده است. این نرم افزار از یک روش رمزگذاری ترکیبی استفاده می‌کند که الگوریتم‌های کلیدهای برابر(متقارن) و کلید‌های عمومی (نامتقارن) جهت تبادل آسان کلید را شامل می‌شود. در حال حاضر که نسخه‌ی دو این برنامه ارائه شده است، برای رمزگذاری‌ها از کتابخانه‌ای به اسم libgcrypt استفاده می‌کند. یکی از مشکلات فعلی این پروژه، عدم وجود api‌های مناسبی جهت دسترسی راحت‌تر است و برای حل این مشکل، GPGME که مخفف GnuPG Made Easy ایجاد شد. بسیاری از برنامه‌ها و پلاگین‌های ارسال اطلاعات، امروزه همچون ارسال ایمیل، از این کلیدها بهره می‌برند.

پروژه‌های مرتبط با این قضیه اسم‌های مشابهی دارند که گاها بعضی افراد، هر کدام از اسم‌ها را که دوست دارند، به همه اطلاق می‌کنند؛ ولی تفاوت‌هایی در این بین وجود دارد:

  • OpenPGP: یک برنامه نیست و یک قانون و استانداری برای تهیه‌ی آن است؛ که رعایت اصول آن الزامی است و برنامه‌ی بالا، یک پیاده سازی از این استاندارد است.
  • PGP: یک برنامه، برای رمزگذاری اطلاعات است که مخفف Pretty Good Privacy است.
  • و GnuPG یا GPG که در بالا به آن اشاره شد.
برای ساخت کلید، ما از دستور یا برنامه‌ی GPG که که عمدتا در همه‌ی لینوکس‌ها مثل دبیان و مشتقات آن نصب است، استفاده می‌کنیم و اگر نصب نیست از طریق توزیع آن اقدام نمایید.
در صورتیکه از ویندوز استفاده می‌کنید، نیاز است ابتدا خط فرمان یونیکس را روی آن نصب کنید. برنامه‌ی Cygwin این امکان را به شما می‌دهد تا خط فرمان یونیکس و دستورات پیش فرض آن را داشته باشید. این برنامه در دو حالت ۳۲ بیتی و ۶۴ بیتی ایجاد شده است. از آنجا که گفتیم این برنامه شامل دستورات پیش فرض آن است، برای همین GPG باید به صورت یک بسته‌ی جداگانه نصب شود که در سایت آن می‌توانید بسته‌های مختلف آن‌را برای پلتفرم‌های مختلف را مشاهده کنید.

ساخت کلید


برای ساخت کلید دستور زیر را صادر کنید:
gpg --gen-key
اگر از نسخه‌های جدیدتر GPG استفاده می‌کنید، گزینه‌هایی به شکل زیر ایجاد می‌شوند؛ ولی اگر خیر، ممکن است تعداد و شماره‌ی گزینه‌ها متفاوت باشند که در این مورد دقت کنید. من در اینجا همان حالت پیش فرض، یعنی ۱ را انتخاب می‌کنم. این گزینه نحوه‌ی امضاء و یا رمزگذاری شما با استفاده از الگوریتم‌های RSA و DSA را مشخص می‌کند.
Please select what kind of key you want:
   (1) RSA and RSA (default)
   (2) DSA and Elgamal
   (3) DSA (sign only)
   (4) RSA (sign only)
در کل در هر حالتی، استفاده‌ی از RSA پیشنهاد می‌شود. بعد از آن، از شما اندازه‌ی کلید را می‌پرسد که همان مقدار پیش فرض خودش را وارد می‌کنیم:
What keysize do you want? (2048)
البته بسیاری ۱۰۲۴ بایت را نیز کافی می‌دانند.
بعد از آن مدت زمان اعتبار این کلید را از شما جویا می‌شود:
Key is valid for? (0)
هنگام این پرسش نحوه‌ی ورود زمان را به شما خواهد گفت که می‌تواند به شکل‌های زیر باشد:
دو هفته
2w
دو سال
2y
پس از آن هم یک تاییدیه از شما می‌گیرد و تاریخ انقضاء را به طور کامل برای شما می‌نویسد و سپس نیاز است که اطلاعاتی از قبیل نام و ایمیل و توضیح را وارد کنید:
You need a user ID to identify your key; the software constructs the user ID
from the Real Name, Comment and Email Address in this form:
    "Heinrich Heine (Der Dichter) <heinrichh@duesseldorf.de>"

Real name: ali yeganeh.m
Email address: yeganehaym@gmail.com
Comment: androidbreadcrumb
You selected this USER-ID:
    "ali yeganeh.m (androidbreadcrumb) <yeganehaym@gmail.com>"
بعد از آن از شما می‌خواهد که کل عملیات را تایید و یا کنسل کنید؛ یا اگر اطلاعات بالا را اشتباه وارد کرده‌اید، اصلاح کنید. با زدن کلید O عملیات را تایید کنید. در این حین از شما یک کلید برای رمزگذاری می‌پرسد که باید آن را دو بار بدهید و کارتان در اینجا به پایان می‌رسد و کلید ایجاد می‌شود.
اگر مشکلی در ساخت کلید نباشد با ارسال دستور زیر باید آن را در لیست کلیدها ببینید:
ali@alipc:~$ gpg --list-keys
/home/ali/.gnupg/pubring.gpg
----------------------------
pub   2048R/8708016A 2015-10-23 [expires: 2065-10-10]
uid                  ali yeganeh.m (androidbreadcrumb) <yeganehaym@gmail.com>
sub   2048R/533B7E96 2015-10-23 [expires: 2065-10-10]
در اینجا کلید عمومی در خط pub بعد از  / قرار دارد؛ یعنی عبارت ۸۷۰۸۰۱۶A کلید عمومی ماست که بر روی هر سیستم و هر کلیدی متفاوت است.

تبدیل کد متنی به کد دودویی
یکی از روش‌های ارسال کدهای دودویی تبدیل آنان به یک قالب متنی ASCII است که به آن قالب ASCII Armor هم می‌گویند. سایت‌های زیادی وجود دارند که این عبارت متنی را از شما می‌خواهند. چرا که مثلا این امکان وجود دارد که کلیدی که کاربر به سمت آنان می‌فرستد، آسیب دیده باشد یا اینکه KeyServer‌ها در دسترس نباشند. در مورد این سرورها در ادامه صحبت خواهیم کرد. مثلا یکی از سایت‌هایی که به این عبارت‌ها نیاز دارد ‌‌‌‌Bintray است.

برای دریافت این کلید متنی باید دستور زیر را صادر کنید:
gpg --output mykey.asc --export -a $GPGKEY
که برای مثال ما می‌شود:
gpg --output mykey.asc --export -a 8708016A
و اگر کلید را با یک ویرایشگر متنی باز کنید، محتوایی شبیه محتوای زیر را خواهید دید:
ali@alipc:~$ cat mykey.asc
-----BEGIN PGP PUBLIC KEY BLOCK-----
Version: GnuPG v1

mQENBFYqAJABCADcw5xPonh5Vj7nDk1CxDskq/VsO08XOa/i2OLOzatB4oK5x+0x
jxORxXMnIAR83PCK5/WkOBa64jnu3eiP3jKEwAykGGz/Z1bezC9TIP8y+PnsiDhT
aFArluUJx+RT5q7s27aKjqoc3fR/xuwLWopZt9uYzE/DQAPDsHdUoUg+fh4Hevm+
a8/3ncR7q6nM8gc9wk621Urb1HaRrILdmeh7ZpJcl8ZUbc+NObw357fGsjnpfHXO
rdCr7ClvNUq6I+IeGMQG/6040LeeaqhaRxPrUhbFjLA155gkSqzecxl7wQaYc71M
Zdlv+6Pt1B8nPAA3WXq0ypjU8A5bvmAQRD5LABEBAAG0OGFsaSB5ZWdhbmVoLm0g
KGFuZHJvaWRicmVhZGNydW1iKSA8eWVnYW5laGF5bUBnbWFpbC5jb20+iQE+BBMB
AgAoBQJWKgCQAhsDBQld/A8ABgsJCAcDAgYVCAIJCgsEFgIDAQIeAQIXgAAKCRDS
Lhq8hwgBanaHB/4reGxUjR6dB08ykfwQOx+raYHGqJlgawisE4qUHTkGaspyQaNy
yxh0vwKkGvg6nNy2VN1XFBc7jlHlrYqPPuPdg2B+1LvEghb30ESDbHUvk8NrJgDJ
C0257gxqWvUQTWvMC3FkSLdw3tyQ8dF7FxmSU79XcxVqGeseaDzMQrEasP0yJHsm
NJf8pvuD6qiWu3KSSoQmI/17Sj8s7eGJMh6o5YRFGHc1Bt9tCD+52bvt579Ju4vZ
tmQvxR4fNQo9sAeMqAJhIpF7IYcuyCEy+CQ847UkzE4f/OCCPxfV3samV/nnBJJ9
Ouu+68lk6Fpx4A0a3nEwqoAmMWxrbSSUFW97uQENBFYqAJABCAC4CzrUOKskE4hK
GVCjaOJKxhbuUdOrep6n3vof0fscs5Dy7h2oVh2vb12WH9X6pijJVPiUpGR4Mpu0
lO2Bu9Rwt38AQ6mRmL/hfzjEXSvKkdX7osk+1CVnnUaSdM9Ek2hWUH8JcN28z/WT
X9Bw8MCdZF7j1HvX/5ojghzMZyYM4elWJLBr1gON6xXAI6HR7DlnRkaVr8L9SYGm
FyAXZ0LzWYwG1Z1AnTyxff6v/Mn3p1/1E3aBA+LkQqBzHg2nBm4jCaFWfeCdiNBf
CHkY9r/Evo9hUPD+CtBNFwsUm1D4maZ0FFtIQ701QhVmupnub+rKoObC0AFj3abK
MCw9uo8TABEBAAGJASUEGAECAA8FAlYqAJACGwwFCV38DwAACgkQ0i4avIcIAWrz
rAf+K1IIMtBq3WlabfZQrgzFHQ62ugVJO/yI1ITkm4l08XHDf+ShqDg4urNuMDEe
oQD35MvB2BhER1jL6VR3qjLkZyZYJ+EQiSxEDWXooav3KvpWjhcqjQy79GFs8waH
E7ssGmWwaugVS/PJAmGQ+s8YWDNa6aCClmp2dJRiwBTyFdewNBLA2V32xzWCYxhI
YtEp+Kg15XuCDTRatOPWSFGSPe/paytmpGZc0XzU/W9sBpabhxVmcL4H6L07uCef
IOn/S5QXo3P9X/3ckmJ9GUb7rjdq1ivYgX53xI75jlePsmN/2f+3fNffUaZgFTTd
Uls+XCun7OVYSBBfjgRfQbTvoA==
=6j7i
-----END PGP PUBLIC KEY BLOCK-----
در صورتی که قصد دارید متن کلید خصوصی را به دست بیاورید، لازم است بعد از export- عبارت secret-key- را نیز اضافه کنی د؛ یعنی:
gpg --output mykey.asc --export-secret-key -a 8708016A


آپلود کلید به سرورهای کلید (Key Servers
)

 یکی از روش‌های به اشتراک گذاری کلید برای کاربران این است که از سرورهای کلید استفاده کنیم. یکبار آپلود روی یکی از این سرورها باعث می‌شود که به بقیه‌ی سرورها هم اضافه شود. یکی از این سرورهای کلید که خودم از آن استفاده می‌کنم، سرور ابونتو است و با استفاده از دستور زیر، همان کلید بالا را برای آن سرور ارسال می‌کنم:
gpg --send-keys --keyserver keyserver.ubuntu.com $GPGKEY

==>
gpg --send-keys --keyserver keyserver.ubuntu.com 8708016A
سپس از طریق کلید متنی، کلید آپلود شده را تایید می‌کنیم. به این آدرس رفته و محتوای کلید متنی خود را به طور کامل به همراه تگ‌های شروع و پایان کپی کنید و حتی می‌توانید کلید خود را از طریق کادر جست و جو پیدا کنید.

رمزگذاری
ابتدا در محیط یونیکس، یک فایل متنی ساده با متن hello ubuntu را ایجاد میکنم. در ادامه قصد دارم این فایل را رمزنگاری کنم:
ali@alipc:~$ cat >ali.txt
hello ubuntu
سپس همین فایل را رمزنگاری می‌کنم:
ali@alipc:~$ gpg --output myali.gpg --encrypt --recipient yeganehaym@gmail.com ali.txt
در این دستور ابتدا گفتیم که نام فایل خروجی ما myali.gpg است و می‌خواهیم آن را رمزگذاری کنیم که توسط کلیدی با ایمیل yeganehaym@gmail.com می‌باشد فایل ali.txt را رمزگذاری می‌کنیم.

رمزگشایی
برای رمزگشایی می‌توانید از طریق دستور زیر اقدام کنید:
gpg --output output.txt --decrypt myali.gpg

You need a passphrase to unlock the secret key for
user: "ali yeganeh.m (androidbreadcrumb) <yeganehaym@gmail.com>"
2048-bit RSA key, ID 533B7E96, created 2015-10-23 (main key ID 8708016A)
در اینجا دستور دادیم محتوای فایل رمزشده‌ی myali.gpg را رمزگشایی کن و محتوای آن را داخل فایلی با نام output.txt قرار بده. بعد از اجرای این دستور از شما عبارت رمزی را که در مرحله‌ی ساخت کلید دوبار از شما پرسید، درخواست می‌کند. در بعضی سیستم‌ها در همان ترمینال می‌پرسد، ولی بعضی سیستم‌ها مثل ابونتو که من از آن استفاده می‌کنم، به صورت گرافیکی یک کادر باز کرده و از شما خواهش می‌کند عبارت رمز را وارد کنید.
عبارت رمز را وارد کنید و حالا فایل output.txt را باز کنید:
ali@alipc:~$ cat output.txt 
hello ubuntu
مطالب
آشنایی با ساختار IIS قسمت سوم
همانطور که در مطلب قبلی گفتم، در این مطلب قرار است به WAS بپردازیم؛ در دنباله متن قبلی گفتیم که دومین وظیفه WWW Service این است: موقعی‌که یک درخواست جدید در صف درخواست‌ها وارد شد، به اطلاع WAS برساند.

WAS یا Windows Process Activation Service
در نسخه 7 به بعد، WAS مدیریت پیکربندی application pool و پروسه‌های کارگر را به جای WWW Service به عهده گرفته است. این مورد شما را قادر می‌سازد تا همان پیکربندی که برای Http در نظر گرفته‌اید، بر روی درخواست هایی که Http نیستند هم اعمال کنید. همچنین موقعی که سایت شما نیازی به درخواست‌های Http ندارد می‌توانید WAS را بدون WWW Service راه اندازی کنید. به عنوان یک مثال فرض کنید شما یک وب سرویس WCF را از طریق WCF Listener Adapter مدیریت می‌کنید و احتیاجی به درخواست‌های نوع Http listener ندارید و http.sys کاری برای انجام ندارد پس نیازی هم به راه اندازی www service نیست.

پیکربندی مدیریتی در WAS
در زمان شروع کار IIS، سرویس WAS اطلاعاتی را از فایل ApplicationHost.config می‌خواند و آن‌ها را به دست listener adapter‌های مربوطه می‌رساند و lsitener adapter‌ها ارتباط بین WAS و listener‌های مختلف را در IIS، برقرار می‌کنند. آداپتورها اطلاعات لازم را از WAS می‌گیرند و به listener‌های مربوطه انتقال می‌دهند تا listener‌ها بر اساس آن تنظیمات یا پیکربرندی‌ها، به درخواست‌ها گوش فرا دهند.
در مورد WCF ، ابتدا WAS تنظیمات را برای آداپتور WCF که NetTcpActivator نام دارد ارسال کرده و این آداپتور بر اساس آن  listener مربوطه را پیکربندی کرده تا به درخواست هایی که از طریق پروتوکل net.tcp می‌رسد گوش فرا دهد.
لیست زیر تعدادی از اطلاعاتی را که از فایل پیکربندی می‌خواند و ارسال می‌کند را بیان کرده است:
  • Global configuration information 
  • Protocol configuration information for both HTTP and non-HTTP protocols
  • Application pool configuration, such as the process account information 
  • Site configuration, such as bindings and applications 
  • Application configuration, such as the enabled protocols and the application pools to which the applications belong 
نکته پایانی اینکه اگر فایل ApplicationHost.config  تغییری کند، WAS یک اعلان دریافت کرده و اطلاعات آداپتورها را به روز می‌کند.

مدیریت پروسه‌ها Process Managment
گفتیم که مدیریت پول و پروسه‌های کارگر جزء وظایف این سرویس به شمار می‌رود. موقعی که یک protocol listener درخواستی را دریافت می‌کند، WAS چک می‌کند که آیا یک پروسه کارگر در حال اجراست یا خیر. اگر application pool پروسه‌ای داشته باشد که در حال سرویس دهی به درخواست هاست، آداپتور درخواست را به پروسه کارگر ارسال می‌کند. در صورتی که پروسه‌ای در application pool در حال اجرا نباشد، WAS یک پروسه جدید را آغاز می‌کند و آداپتور درخواست را به آن پاس می‌کند.
نکته: از آنجایی که WAS هم پروسه‌های http و هم non-http را مدیریت می‌کند، پس میتوانید از یک applicatio pool برای چندین protocol استفاده کنید. به عنوان مثال شما یکی سرویس XML دارید که می‌توانید از آن برای سرویس دهی به پروتوکل‌های Http و net.tcp بهره بگیرید.

ماژول‌ها در IIS
قبلا مقاله ای در مورد module‌ها با نام "کمی در مورد httpmoduleها" قرار داده بودیم که بهتر است برای آشنایی بیشتر، به آن رجوع کنید. به غیر از وب کانفیگ که برای معرفی ماژول‌ها استفاده می‌کردیم ، میتوانید به صورت گرافیکی و دستی هم این کار را انجام بدهید. ابتدا یک پروژه class library ایجاد کرده و ماژول خود را بنویسید و سپس آن را به یک dll تبدیل کنید و dll را در شاخه bin که این شاخه در ریشه وب سایتتان قرار دارد کپی کنید. سپس در IIS قسمت module گزینه Add را انتخاب کنید و در قسمت اول نامی برای آن و در قسمت بعدی دقیقا همان قوانین type که در وب کانفیگ مشخص می‌کردید را مشخص کنید: Namespace.ClassName
گزینه invoke only for requests to asp.net and manage handlers را هم تیک بزنید. کار تمام است.

ماژول‌های کد ماشین یا  native
این ماژول‌ها به صورت پیش فرض به سیستم اضافه شده‌اند و در صورتی که میخواهید جایگزینی به منظور خصوصی سازی انجام دهید آن‌ها را پاک کنید و ماژول جدید را اضافه کنید.

جدول ماژول‌های HTTP
نام ماژول
توضیحات
نام فایل منبع
CustomErrorModule  موقعی که هنگام response، کد خطایی تولید می‌گردد، پیام خطا را پیکربندی و سپس ارسال می‌کند.  Inetsrv\Custerr.dll 
 HttpRedirectionModule   تنظمیات redirection برای درخواست‌های http را در دسترس قرار می‌دهد.  Inetsrv\Redirect.dll 
 ProtocolSupportModule   انجام عملیات مربوط به پروتوکل‌ها بر عهده این ماژول است؛ مثل تنظیم کردن قسمت هدر برای response.  Inetsrv\Protsup.dll 
 RequestFilteringModule   این ماژول از IIS 7.5 به بعد اضافه شد. درخواست‌ها را فیلتر می‌کند تا پروتوکل و رفتار محتوا را کنترل کند.  Inetsrv\modrqflt.dll 
 WebDAVModule   این ماژول از IIS 7.5 به بعد اضافه شد. امنیت بیشتر در هنگام انتشار محتوا روی HTTP SSL  Inetsrv\WebDAV.dll 

ماژول‌های امنیتی
نام ماژول  توضیحات  نام فایل منبع 
 AnonymousAuthenticationModule  موقعی که هیچ کدام از عملیات authentication  با موفقیت روبرو نشود، عملیات  Anonymous authentication انجام می‌شود.  Inetsrv\Authanon.dll 
 BasicAuthenticationModule   عمل ساده و اساسی authentication  را انجام می‌دهد.  Inetsrv\Authbas.dll 
 CertificateMappingAuthenticationModule   انجام عمل Certificate Mapping authentication  در Active Directory  Inetsrv\Authcert.dll
 
 DigestAuthenticationModule   Digest authentication   Inetsrv\Authmd5.dll 
 IISCertificateMappingAuthenticationModule  همان Certificate Mapping authentication  ولی اینبار با IIS Certificate .  Inetsrv\Authmap.dll 
 RequestFilteringModule   عملیات اسکن URL از قبیل نام صفحات و دایرکتوری‌ها ، توع verb و یا کاراکترهای مشکوک و خطرآفرین  Inetsrv\Modrqflt.dll 
 UrlAuthorizationModule   عمل URL authorization   Inetsrv\Urlauthz.dll 
 WindowsAuthenticationModule   عمل NTLM integrated authentication   Inetsrv\Authsspi.dll 
 IpRestrictionModule   محدود کردن IP‌های نسخه 4 لیست شده در IP Security در قسمت پیکربندی  Inetsrv\iprestr.dll 
 

ماژول‌های محتوا
 نام ماژول  توضیحات نام فایل منبع
 CgiModule   ایجاد پردازش‌های (Common Gateway Interface (CGI به منظور ایجاد خروجی response  Inetsrv\Cgi.dll 
 DefaultDocumentModule   تلاش برای ساخت یک سند پیش فرض برای درخواست هایی که دایرکتوری والد ارسال می‌شود  Inetsrv\Defdoc.dll 
 DirectoryListingModule   لیست کردن محتوای یک دایرکتوری  Inetsrv\dirlist.dll 
 IsapiModule   میزبانی فایل های ISAPI Inetsrv\Isapi.dll
 IsapiFilterModule   پشتیبانی از فیلتر های ISAPI  Inetsrv\Filter.dll 
 ServerSideIncludeModule   پردازش کدهای include شده سمت سرور  Inetsrv\Iis_ssi.dll 
 StaticFileModule   ارائه فایل‌های ایستا  Inetsrv\Static.dll 
 FastCgiModule   پشتبانی از CGI  Inetsrv\iisfcgi.dll 

ماژول‌های فشرده سازی
 DynamicCompressionModule  فشرده سازی پاسخ response با gzip  Inetsrv\Compdyn.dll   
 StaticCompressionModule   فشرده سازی محتوای ایستا  Inetsrv\Compstat.dll 

ماژول‌های کش کردن
 FileCacheModule  تهیه کش در مد کاربری برای فایل‌ها.    Inetsrv\Cachfile.dll 
 HTTPCacheModule   تهیه کش مد کاربری و مد کرنل برای http.sys  Inetsrv\Cachhttp.dll 
 TokenCacheModule   تهیه کش مد کاربری بر اساس جفت نام کاربری و یک token که توسط  Windows user principals تولید شده است.   Inetsrv\Cachtokn.dll 
 UriCacheModule   تهیه یک کش مد کاربری از اطلاعات URL  Inetsrv\Cachuri.dll 

ماژول‌های عیب یابی و لاگ کردن
 CustomLoggingModule  بارگزاری ماژول‌های خصوصی سازی شده جهت لاگ کردن  Inetsrv\Logcust.dll
 FailedRequestsTracingModule   برای ردیابی درخواست‌های ناموفق  Inetsrv\Iisfreb.dll 
 HttpLoggingModule   دریافت اطلاعات  و پردازش وضعیت http.sys برای لاگ کردن  Inetsrv\Loghttp.dll 
 RequestMonitorModule   ردیابی درخواست هایی که در حال حاضر در پروسه‌های کارگر در حال اجرا هستند و گزارش اطلاعاتی در مورد وضعیت اجرا و کنترل رابط برنامه نویسی کاربردی.  Inetsrv\Iisreqs.dll 
 TracingModule   گزارش رخدادهای Microsoft Event Tracing for Windows یا به اختصار ETW  Inetsrv\Iisetw.dll 

ماژول‌های مدیریتی و نظارتی بر کل ماژول‌ها
 ManagedEngine   مدیرتی بر ماژول‌های غیر native که در پایین قرار دارند. Microsoft.NET\Framework\v2.0.50727\webengine.dll
 ConfigurationValidationModule  اعتبارسنجی خطاها، مثل موقعی که برنامه در حالت integrated اجرا شده و ماژول‌ها یا هندلرها در system.web تعریف شده‌اند. Inetsrv\validcfg.dll 
از IIS6 به بعد در حالت integrated و ماقبل، در حالت کلاسیک می‌باشند. اگر مقاله ماژول ها را خوانده باشید می‌دانید که تعریف آن‌ها در وب کانفیگ در بین این دو نسخه متفاوت هست و رویداد سطر آخر در جدول بالا این موقعیت را چک می‌کند و اگر به خاطر داشته باشید با اضافه کردن یک خط اعتبارسنجی آن را قطع می‌کردیم. در مورد هندلرها هم به همین صورت می‌باشد.
به علاوه ماژول‌های native بالا، IIS این امکان را فراهم می‌آورند تا از ماژول‌های کد مدیریت شده (یعنی CLR) برای توسعه توابع و کارکرد IIS بهره مند شوید:
 ماژول توضیحات   منبع
 AnonymousIdentification   مدیریت منابع تعیین هویت برای کاربران ناشناس مانند asp.net profile System.Web.Security.AnonymousIdentificationModule  
 DefaultAuthentication   اطمینان از وجود شی Authentication در context مربوطه  System.Web.Security.DefaultAuthenticationModule 
 FileAuthorization   تایید هویت کاربر برای دسترسی به فایل درخواست  System.Web.Security.FileAuthorizationModule 
 FormsAuthentication   با این قسمت که باید کاملا آشنا باشید؛ برای تایید هویت کاربر  System.Web.Security.FormsAuthenticationModule 
OutputCache  مدیریت کش  System.Web.Caching.OutputCacheModule 
 Profile   مدیریت پروفایل کاربران که تنظیماتش را در یک منبع داده‌ای چون دیتابیس ذخیره و بازیابی می‌کند.  System.Web.Profile.ProfileModule 
 RoleManager   مدیریت نقش و سمت کاربران  System.Web.Security.RoleManagerModule 
 Session   مدیریت session ها  System.Web.SessionState.SessionStateModule 
 UrlAuthorization   آیا کاربر جاری حق دسترسی به URL درخواست را دارد؟  System.Web.Security.UrlAuthorizationModule 
 UrlMappingsModule   تبدیل یک Url واقعی به یک Url کاربرپسند  System.Web.UrlMappingsModule 
 WindowsAuthentication  شناسایی و تایید و هویت یک کاربر بر اساس لاگین او به ویندوز   System.Web.Security.WindowsAuthenticationModule 
نظرات مطالب
خلاصه اشتراک‌های روز شنبه 14 آبان 1390
برای مشکل Solution Explorer و... از این اکستنشن استفاده کن
http://visualstudiogallery.msdn.microsoft.com/20cd93a2-c435-4d00-a797-499f16402378/
و این تم رو استفاده کن Expression Dark
http://www.nerdpad.com/wp-content/uploads/2010/04/Expression_ColorScheme.zip
اطلاعات بیشتر
http://www.nerdpad.com/visual-studio/visual-studio-2010-dark-expression-blend-color-theme
برای فونت هم من بشخصه از این فونت خیلی خوشم میاد
https://github.com/serialseb/config
یه نمونه از استفادش تو سایت معروف codebetter
http://codebetter.com/sebastienlambla/2011/09/14/extending-configuration-in-openrasta-2-1/
نظرات مطالب
یکسان سازی ی و ک دریافتی حین استفاده از NHibernate
یک روش ساده پیدا کردم که اگر استاده هم در مورد آن نظر بدهند ممنون میشوم
پس از نصب ویندوز 7 هیچ نوع فارسی سازی روی آن نصب نکردم .
تنها در بخش
Region-> admin-> Change System local
Current System Local را به Persian تبدیل کردم

پس از ری استارت ویندوز تمامی علامت سواالها فارسی شد.
سپس در برنامه ام کد پیج فارسی (عربی)
LoadKeyboardLayout('00000429', 1) ;
را لود کردم .

براحتی کیبوردی که پ و ژ آن صحیح است ارائه شد. ی و ک هم عربی خورد .نیم فاصله هم کاملا صحیح عمل میکند.
توضیح در مورد ویندوز مورد استفاده ام
Product Id : 00426-OEM-8992662-00497
System Type : 32-bit

در بخش Regional and Language
Format : English
Location : United states
Keyboard : English-US (فقط.هیچ فارسی اضافه نشده )
System Locale : Persian
مطالب
پیاده سازی کتابخانه PagedList.MVC برای صفحه بندی اطلاعات در ASP.NET MVC
یکی از مواردی که در هر پروژه‌ای به چشم می‌خورد و وجود دارد، نمایش داده‌های ذخیره شده‌ی در بانک اطلاعاتی، به کاربر می‌باشد. احتمالا وب سایت‌هایی را دیده‌اید که تمامی اطلاعات را در یک صفحه بدون هیچ صفحه بندی به کاربر نشان میدهند که حس خوبی را به کاربر استفاده کننده منتقل نمیکند و نتیجه منفی هم بر روی  سئو خواهد گذاشت ( عدم داشتن Url‌های منحصر بفرد به ازای هر صفحه).
بعضا دیده می‌شود که برنامه نویس یک Paging را به صورت Ajax ی پیاده سازی میکند که با تغییر صفحه‌ها، اطلاعات را خوانده و به کاربر نمایش میدهد و هیچ اتفاقی در آدرس بار صفحه نمی‌افتد  و خیلی خرسند است از کاری که انجام داده‌است. در چنین مواردی اگر کاربر استفاده کننده از برنامه بخواهد لینک صفحه دهم گرید و یا لیست اطلاعات را برای کسی بفرستد، باید چکار کند؟
توضیح بالا صرفا به این دلیل بیان شد تا به ضرورت داشتن Url‌های منحصر بفرد برای هر Page برسیم؛ هر چند بحث جاری درباره سئو نیست. ولی ترجیح دادم در کنار  موضوع مقاله، توجهی هم داشته باشیم به این موضوع که رعایت آن حس بهتری را به کاربران برنامه می‌دهد.
کتابخانه‌های زیادی برای صفحه بندی اطلاعات وجود دارند و یا اینکه بعضی از برنامه نویسان و یا شرکت‌ها ترجیح می‌دهند خود چرخ را  مطابق میل خود از نو طراحی کنند. در ادامه قصد داریم به پیاده سازی کتابخانه PagedList که خیلی محبوب و پر طرفدار می‌باشد در ASP.NET MVC بپردازیم.

1- ابتدا قبل از هر کاری، با استفاده از دستور زیر اقدام به نصب کتابخانه آن می‌نماییم:
Install-Package PagedList.Mvc
2- بعد از نصب این کتابخانه، متد الحاقی ToPagedList در اختیار ما قرار داده می‌شود که بر روی IQueryable , IEnumerable در دسترس می‌باشد. 
3- در کنترلر فقط کافیست متد ToPagedList را فراخوانی کرده و مقدار بازگشتی را به View ارسال نمود.
4 - و در نهایت در داخل View (ها) فقط کافیست برای نمایش صفحه بندی، دستور Html.PagedListPager را فراخوانی کنیم.

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

ابتدا یک مدل فرضی را همانند زیر تهیه می‌کنیم:
 public class Post
    {
        public int Id { get; set; }
        public string Title { get; set; }
        public string Body { get; set; }

    }
و کلاسی را همانند زیر برای دریافت یک لیست از پست‌ها می‌نویسیم:
public static class PostService
    {
        public static IEnumerable<Post> posts = new List<Post>() {
            new Post{Id=1,Title="Title 1",Body="Body 1"},
            new Post{Id=2,Title="Title 2",Body="Body 2"},
            new Post{Id=3,Title="Title 3",Body="Body 3"},
            new Post{Id=4,Title="Title 4",Body="Body 4"},
            new Post{Id=5,Title="Title 5",Body="Body 5"},
            new Post{Id=6,Title="Title 6",Body="Body 6"},
            new Post{Id=7,Title="Title 7",Body="Body 7"},
            new Post{Id=8,Title="Title 8",Body="Body 8"},
            new Post{Id=9,Title="Title 9",Body="Body 9"},
            new Post{Id=10,Title="Title 10",Body="Body 10"},
            new Post{Id=11,Title="Title 11",Body="Body 11"},
            new Post{Id=12,Title="Title 12",Body="Body 12"},
            };
    
        public static IEnumerable<Post> GetAll()
        {
            return posts
            .OrderBy(row => row.Id);
        }
    }  
ابتدا یک کنترلر را ایجاد نمایید. در اکشن متدی که قصد داریم لیستی از اطلاعات را به کاربر نمایش دهیم، باید یک متغییر از نوع int برای شماره صفحه در نظر گرفته شود:
    public ActionResult Index(int? page)
        {
           var pageNumber = page ?? 1; 
            var posts = PostService.GetAll(); 
            var result = posts.ToPagedList(pageNumber, 10); 
            ViewBag.posts = result;
            return View();        
        }
و در view مربوطه داریم:
@using PagedList.Mvc; 
@using PagedList; 
<link href="/Content/PagedList.css" rel="stylesheet" type="text/css" />
<h2>List of posts</h2>
<ul>
    @foreach (var post in ViewBag.posts)
    {
        <li>@post.Title</li>
    }
</ul>
@Html.PagedListPager((IPagedList)ViewBag.posts, page => Url.Action("Index", new { page }))
توسط متد postService.Getall، تمامی پست‌ها از دیتابیس خوانده شده که جمعا 12 رکورد می‌باشند. فراخوانی ToPagedList به تعداد پارامتر دوم  رکوردها را بر میگرداند و در متغییر result قرار می‌دهد و در پایان برای نمایش صفحه بندی، اقدام به فراخوانی متد الحاقی PagedListPager نموده‌ایم.
بله، درست حدس زده‌اید! این روش دارای یک عیب می‌باشد و آن این است که ابتدا ما تمامی رکورد‌ها را از دیتابیس فراخونی کرده و بعد از آن به تعداد 10 رکورد را از آن انتخاب نموده‌ایم. هر چند در مثال جاری تعداد رکورد‌ها زیاد نمی‌باشد، ولی با مرور زمان و حجیم شدن دیتابیس، کوئری فوق امکان دارد به کندی اجرا شود. به همین دلیل نیاز به متدی داریم که با توجه به صفحه جاری، تعداد n رکورد را از دیتابیس خوانده و نمایش دهد. برای این منظور متدی همانند زیر را به کلاس postService اضافه می‌نماییم:
       public static IEnumerable<Post> GetAll(int page, int recordsPerPage,out int totalCount)
        {
            totalCount = posts.Count();
            return posts
            .OrderBy(row => row.Id).Skip(page * recordsPerPage).Take(recordsPerPage); // in real projects change like this .skip(()=>resultforSkip).Take(()=>recordsPerPage )
        }
از totalCount برای نگه داری جمع کل رکورد‌ها استفاده میکنیم و قصد نداریم تمامی اطلاعات را از دیتابیس واکشی نماییم.

در صفحه بندی به صورت دستی، تا حدودی اکشن Index تغییر خواهد کرد. در این روش داریم:
public ActionResult Index(int? page)
        {
              var pageIndex = (page ?? 1) - 1; 
            var pageSize = 10;
            int totalPostCount; 
            var posts = PostService.GetAll(pageIndex, pageSize, out totalPostCount);
            var result = new StaticPagedList<Post>(posts, pageIndex + 1, pageSize, totalPostCount);
            ViewBag.posts = result;
            return View();    
        }
نکته: مقدار PagedIndex نمی‌تواند صفر باشد؛ چون شروع اعداد صفحه بندی  از یک هست. به این خاطر، PageIndex با یک واحد، جمع شده است. در روش قبلی مقدار پیش فرض  آن را 1 قرار دادیم. ولی در این روش ابتدا یک واحد از آن کم میکنیم؛ به این خاطر که در متد Skip شاهد اطلاعات دقیقی باشیم. محتوای view به همان روش قبلی می‌باشد و نیازی به تغییر آن نیست.

مقدار page به صورت کوئری استرینگ به انتهای url اضافه خواهد شد. جهت نظم بخشیدن به آن می‌بایست اقدام به افزودن route سفارشی نمایید که در حالت پیش فرض به صورت زیر می‌باشد:
http://localhost:53192/?page=2

   routes.MapRoute(
               name: "paging",
               url: "{controller}/{action}/{page}",
               defaults: new { controller = "Home", action = "Index", page = UrlParameter.Optional }
           );

در روش‌هایی که شرح آنها گذشت، از viewbag برای انتقال داده‌ها استفاده کردیم. می‌توان view مورد نظر را strongly-typed معرفی نمود و داده‌ها را از طریق return view به سمت view بفرستید. در این حالت در view مربوطه داریم :
@model PagedList.IPagedList <pagedListmvc.Models.Post>
و تغییر حلقه for به صورت زیر :
  @foreach (var post in Model)
    {
        <li>@post.Title</li>
    }

سفارشی کردن Url: فرض کنید همراه با کلیک بر روی شماره‌ی صفحات، بخواهیم یک سری دیتای دیگر را هم به اکشن پاس دهیم؛ برای مثال tag=mvc. برای این منظور داریم:
@Html.PagedListPager( myList, page => Url.Action("Index", new { page = page, tag= "mvc" }) )

استایل خروجی html حاصل از Html.PagedListPager بر اساس کتابخانه Bootstrap می‌باشد. در صورتیکه در پروژه خود از این کتابخانه استفاده نمی‌کنید، می‌توانید فقط فایل PagedList.css را از Nuget دریافت نموده و به پروژه‌ی خود اضافه نمایید.
یکی از overload‌های Html.PagedListPager پارامتری را تحت عنوان PagedListRenderOptions دارد که از آن می‌توانید برای پیکربندی صفحه بندی استفاده نمایید. برای نمونه نمایش فقط 5 صفحه:
@Html.PagedListPager((IPagedList)ViewBag.posts, page => Url.Action("Index", new { page = page }), PagedListRenderOptions.OnlyShowFivePagesAtATime)
همچنین قادر خواهید بود یکسری تنظیمات دستی را بر روی شماره صفحات تولید شده انجام دهید؛ برای نمونه تغییر عناوین Next , Prev با عناوین فارسی:
@Html.PagedListPager((IPagedList)ViewBag.posts, page => Url.Action("Index", new { page = page }), new PagedListRenderOptions { LinkToFirstPageFormat = "<< ابتدا", LinkToPreviousPageFormat = "< قبلی", LinkToNextPageFormat = "بعدی>", LinkToLastPageFormat = "آخرین >>" })
مطالب
سفارشی سازی ASP.NET Core Identity - قسمت پنجم - سیاست‌های دسترسی پویا
ASP.NET Core Identity به همراه دو قابلیت جدید است که پیاده سازی سطوح دسترسی پویا را با سهولت بیشتری میسر می‌کند:
الف) Policies
ب) Role Claims


سیاست‌های دسترسی یا Policies در ASP.NET Core Identity

ASP.NET Core Identity هنوز هم از مفهوم Roles پشتیبانی می‌کند. برای مثال می‌توان مشخص کرد که اکشن متدی و یا تمام اکشن متدهای یک کنترلر تنها توسط کاربران دارای نقش Admin قابل دسترسی باشند. اما نقش‌ها نیز در این سیستم جدید تنها نوعی از سیاست‌های دسترسی هستند.
[Authorize(Roles = ConstantRoles.Admin)]
public class RolesManagerController : Controller
برای مثال در اینجا دسترسی به امکانات مدیریت نقش‌های سیستم، به نقش ثابت و از پیش تعیین شده‌ی Admin منحصر شده‌است و تمام کاربرانی که این نقش به آن‌ها انتساب داده شود، امکان استفاده‌ی از این قابلیت‌ها را خواهند یافت.
اما نقش‌های ثابت، بسیار محدود و غیر قابل انعطاف هستند. برای رفع این مشکل مفهوم جدیدی را به نام Policy اضافه کرده‌اند.
[Authorize(Policy="RequireAdministratorRole")]
public IActionResult Get()
{
   /* .. */
}
سیاست‌های دسترسی بر اساس Requirements و یا نیازهای سیستم تعیین می‌شوند و تعیین نقش‌ها، تنها یکی از قابلیت‌های آن‌ها هستند.
برای مثال اگر بخواهیم تک نقش Admin را به صورت یک سیاست دسترسی جدید تعریف کنیم، روش کار به صورت ذیل خواهد بود:
public void ConfigureServices(IServiceCollection services)
{
    services.AddMvc();
services.AddAuthorization(options =>
    {
        options.AddPolicy("RequireAdministratorRole", policy => policy.RequireRole("Admin"));
    });
}
در تنظیمات متد AddAuthorization، یک سیاست دسترسی جدید تعریف شده‌است که جهت برآورده شدن نیازمندی‌های آن، کاربر سیستم باید دارای نقش Admin باشد که نمونه‌ای از نحوه‌ی استفاده‌ی از آن‌را با ذکر [Authorize(Policy="RequireAdministratorRole")] ملاحظه کردید.
و یا بجای اینکه چند نقش مجاز به دسترسی منبعی را با کاما از هم جدا کنیم:
 [Authorize(Roles = "Administrator, PowerUser, BackupAdministrator")]
می‌توان یک سیاست دسترسی جدید را به نحو ذیل تعریف کرد که شامل تمام نقش‌های مورد نیاز باشد و سپس بجای ذکر Roles، از نام این Policy جدید استفاده کرد:
options.AddPolicy("ElevatedRights", policy => policy.RequireRole("Administrator", "PowerUser", "BackupAdministrator"));
به این صورت
[Authorize(Policy = "ElevatedRights")]
public IActionResult Shutdown()
{
   return View();
}

سیاست‌های دسترسی تنها به نقش‌ها محدود نیستند:
services.AddAuthorization(options =>
{
   options.AddPolicy("EmployeeOnly", policy => policy.RequireClaim("EmployeeNumber"));
});
برای مثال می‌توان دسترسی به یک منبع را بر اساس User Claims یک کاربر به نحوی که ملاحظه می‌کنید، محدود کرد:
[Authorize(Policy = "EmployeeOnly")]
public IActionResult VacationBalance()
{
   return View();
}


سیاست‌های دسترسی پویا در ASP.NET Core Identity

مهم‌ترین مزیت کار با سیاست‌های دسترسی، امکان سفارشی سازی و تهیه‌ی نمونه‌های پویای آن‌ها هستند؛ موردی که با نقش‌های ثابت سیستم قابل پیاده سازی نبوده و در نگارش‌های قبلی، جهت پویا سازی آن، یکی از روش‌های بسیار متداول، تهیه‌ی فیلتر Authorize سفارشی سازی شده بود. اما در اینجا دیگر نیازی نیست تا فیلتر Authorize را سفارشی سازی کنیم. با پیاده سازی یک AuthorizationHandler جدید و معرفی آن به سیستم، پردازش سیاست‌های دسترسی پویای به منابع، فعال می‌شود.
پیاده سازی سیاست‌های پویای دسترسی شامل مراحل ذیل است:
1- تعریف یک نیازمندی دسترسی جدید
public class DynamicPermissionRequirement : IAuthorizationRequirement
{
}
ابتدا باید یک نیازمندی دسترسی جدید را با پیاده سازی اینترفیس IAuthorizationRequirement ارائه دهیم. این نیازمندی مانند روشی که در پروژه‌ی DNT Identity بکار گرفته شده‌است، خالی است و صرفا به عنوان نشانه‌ای جهت یافت AuthorizationHandler استفاده کننده‌ی از آن استفاده می‌شود. در اینجا در صورت نیاز می‌توان یک سری خاصیت اضافه را تعریف کرد تا آن‌ها را به صورت پارامترهایی ثابت به AuthorizationHandler ارسال کند.

2- پیاده سازی یک AuthorizationHandler استفاده کننده‌ی از نیازمندی دسترسی تعریف شده
پس از اینکه نیازمندی DynamicPermissionRequirement را تعریف کردیم، در ادامه باید یک AuthorizationHandler استفاده کننده‌ی از آن را تعریف کنیم:
    public class DynamicPermissionsAuthorizationHandler : AuthorizationHandler<DynamicPermissionRequirement>
    {
        private readonly ISecurityTrimmingService _securityTrimmingService;

        public DynamicPermissionsAuthorizationHandler(ISecurityTrimmingService securityTrimmingService)
        {
            _securityTrimmingService = securityTrimmingService;
            _securityTrimmingService.CheckArgumentIsNull(nameof(_securityTrimmingService));
        }

        protected override Task HandleRequirementAsync(
             AuthorizationHandlerContext context,
             DynamicPermissionRequirement requirement)
        {
            var mvcContext = context.Resource as AuthorizationFilterContext;
            if (mvcContext == null)
            {
                return Task.CompletedTask;
            }

            var actionDescriptor = mvcContext.ActionDescriptor;
            var area = actionDescriptor.RouteValues["area"];
            var controller = actionDescriptor.RouteValues["controller"];
            var action = actionDescriptor.RouteValues["action"];

            if(_securityTrimmingService.CanCurrentUserAccess(area, controller, action))
            {
                context.Succeed(requirement);
            }
            else
            {
                context.Fail();
            }

            return Task.CompletedTask;
        }
    }
کار با ارث بری از AuthorizationHandler شروع شده و آرگومان جنریک آن، همان نیازمندی است که پیشتر تعریف کردیم. از این آرگومان جنریک جهت یافتن خودکار AuthorizationHandler متناظر با آن، توسط ASP.NET Core Identity استفاده می‌شود. بنابراین در اینجا DynamicPermissionRequirement تهیه شده صرفا کارکرد علامتگذاری را دارد.
در کلاس تهیه شده باید متد HandleRequirementAsync آن‌را بازنویسی کرد و اگر در این بین، منطق سفارشی ما context.Succeed را فراخوانی کند، به معنای برآورده شدن سیاست دسترسی بوده و کاربر جاری می‌تواند به منبع درخواستی، بلافاصله دسترسی یابد و اگر context.Fail فراخوانی شود، در همینجا دسترسی کاربر قطع شده و HTTP status code مساوی 401 (عدم دسترسی) را دریافت می‌کند.

منطق سفارشی پیاده سازی شده نیز به این صورت است:
نام ناحیه، کنترلر و اکشن متد درخواستی کاربر از مسیریابی جاری استخراج می‌شوند. سپس توسط سرویس سفارشی ISecurityTrimmingService تهیه شده، بررسی می‌کنیم که آیا کاربر جاری به این سه مؤلفه دسترسی دارد یا خیر؟

3- معرفی سیاست دسترسی پویای تهیه شده به سیستم
معرفی سیاست کاری پویا و سفارشی تهیه شده، شامل دو مرحله‌ی زیر است:
        private static void addDynamicPermissionsPolicy(this IServiceCollection services)
        {
            services.AddScoped<IAuthorizationHandler, DynamicPermissionsAuthorizationHandler>();
            services.AddAuthorization(opts =>
            {
                opts.AddPolicy(
                    name: ConstantPolicies.DynamicPermission,
                    configurePolicy: policy =>
                    {
                        policy.RequireAuthenticatedUser();
                        policy.Requirements.Add(new DynamicPermissionRequirement());
                    });
            });
        }
ابتدا باید DynamicPermissionsAuthorizationHandler تهیه شده را به سیستم تزریق وابستگی‌ها معرفی کنیم.
سپس یک Policy جدید را با نام دلخواه DynamicPermission تعریف کرده و نیازمندی علامتگذار خود را به عنوان یک policy.Requirements جدید، اضافه می‌کنیم. همانطور که ملاحظه می‌کنید یک وهله‌ی جدید از DynamicPermissionRequirement در اینجا ثبت شده‌است. همین وهله به متد HandleRequirementAsync نیز ارسال می‌شود. بنابراین اگر نیاز به ارسال پارامترهای بیشتری به این متد وجود داشت، می‌توان خواص مرتبطی را به کلاس DynamicPermissionRequirement نیز اضافه کرد.
همانطور که مشخص است، در اینجا یک نیازمندی را می‌توان ثبت کرد و نه Handler آن‌را. این Handler از سیستم تزریق وابستگی‌ها، بر اساس آرگومان جنریک AuthorizationHandler پیاده سازی شده، به صورت خودکار یافت شده و اجرا می‌شود (بنابراین اگر Handler شما اجرا نشد، مطمئن شوید که حتما آن‌را به سیستم تزریق وابستگی‌ها معرفی کرده‌اید).

پس از آن هر کنترلر یا اکشن متدی که از این سیاست دسترسی پویای تهیه شده استفاده کند:
[Authorize(Policy = ConstantPolicies.DynamicPermission)]
[DisplayName("کنترلر نمونه با سطح دسترسی پویا")]
public class DynamicPermissionsSampleController : Controller
به صورت خودکار توسط DynamicPermissionsAuthorizationHandler مدیریت می‌شود.


سرویس ISecurityTrimmingService چگونه کار می‌کند؟

کدهای کامل ISecurityTrimmingService را در کلاس SecurityTrimmingService می‌توانید مشاهده کنید.
پیشنیاز درک عملکرد آن، آشنایی با دو قابلیت زیر هستند:
الف) «روش یافتن لیست تمام کنترلرها و اکشن متدهای یک برنامه‌ی ASP.NET Core»
دقیقا از همین سرویس توسعه داده شده‌ی در مطلب فوق، در اینجا نیز استفاده شده‌است؛ با یک تفاوت تکمیلی:
public interface IMvcActionsDiscoveryService
{
    ICollection<MvcControllerViewModel> MvcControllers { get; }
    ICollection<MvcControllerViewModel> GetAllSecuredControllerActionsWithPolicy(string policyName);
}
از متد GetAllSecuredControllerActionsWithPolicy جهت یافتن تمام اکشن متدهایی که مزین به ویژگی Authorize هستند و دارای Policy مساوی DynamicPermission می‌باشند، در کنترلر DynamicRoleClaimsManagerController برای لیست کردن آن‌ها استفاده می‌شود. اگر این اکشن متد مزین به ویژگی DisplayName نیز بود (مانند مثال فوق و یا کنترلر نمونه DynamicPermissionsSampleController)، از مقدار آن برای نمایش نام این اکشن متد استفاده خواهد شد.
بنابراین همینقدر که تعریف ذیل یافت شود، این اکشن متد نیز در صفحه‌ی مدیریت سطوح دسترسی پویا لیست خواهد شد.
 [Authorize(Policy = ConstantPolicies.DynamicPermission)]

ابتدا به مدیریت نقش‌های ثابت سیستم می‌رسیم. سپس به هر نقش می‌توان یک ‍Claim جدید را با مقدار area:controller:action انتساب داد.
به این ترتیب می‌توان به یک نقش، تعدادی اکشن متد را نسبت داد و سطوح دسترسی به آن‌ها را پویا کرد. اما ذخیره سازی آن‌ها چگونه است و چگونه می‌توان به اطلاعات نهایی ذخیره شده دسترسی پیدا کرد؟


مفهوم جدید Role Claims در ASP.NET Core Identity

تا اینجا موفق شدیم تمام اکشن متدهای دارای سیاست دسترسی سفارشی سازی شده‌ی خود را لیست کنیم، تا بتوان آن‌ها را به صورت دلخواهی انتخاب کرد و سطوح دسترسی به آن‌ها را به صورت پویا تغییر داد. اما این اکشن متدهای انتخاب شده را در کجا و به چه صورتی ذخیره کنیم؟
برای ذخیره سازی این اطلاعات نیازی نیست تا جدول جدیدی را به سیستم اضافه کنیم. جدول جدید AppRoleClaims به همین منظور تدارک دیده شده‌است.



وقتی کاربری عضو یک نقش است، به صورت خودکار Role Claims آن نقش را نیز به ارث می‌برد. هدف از نقش‌ها، گروه بندی کاربران است. توسط Role Claims می‌توان مشخص کرد این نقش‌ها چه کارهایی را می‌توانند انجام دهند. اگر از قسمت قبل بخاطر داشته باشید، سرویس توکار UserClaimsPrincipalFactory دارای مرحله‌ی 5 ذیل است:
«5) اگر یک نقش منتسب به کاربر دارای Role Claim باشد، این موارد نیز واکشی شده و به کوکی او به عنوان یک Claim جدید اضافه می‌شوند. در ASP.NET Identity Core نقش‌ها نیز می‌توانند Claim داشته باشند (امکان پیاده سازی سطوح دسترسی پویا).»
به این معنا که با لاگین شخص به سیستم، تمام اطلاعات مرتبط به او که در جدول AppRoleClaims وجود دارند، به کوکی او به صورت خودکار اضافه خواهند شد و دسترسی به آن‌ها فوق العاده سریع است.

در کنترلر DynamicRoleClaimsManagerController، یک Role Claim Type جدید به نام DynamicPermissionClaimType اضافه شده‌است و سپس ID اکشن متدهای انتخابی را به نقش جاری، تحت Claim Type عنوان شده، اضافه می‌کند (تصویر فوق). این ID به صورت area:controller:action طراحی شده‌است. به همین جهت است که در  DynamicPermissionsAuthorizationHandler همین سه جزء از سیستم مسیریابی استخراج و در سرویس SecurityTrimmingService مورد بررسی قرار می‌گیرد:
 return user.HasClaim(claim => claim.Type == ConstantPolicies.DynamicPermissionClaimType &&
claim.Value == currentClaimValue);
در اینجا user همان کاربرجاری سیستم است. HasClaim جزو متدهای استاندارد آن است و Type انتخابی، همان نوع سفارشی مدنظر ما است. currentClaimValue دقیقا همان ID اکشن متد جاری است که توسط کنار هم قرار دادن area:controller:action تشکیل شده‌است.
متد HasClaim هیچگونه رفت و برگشتی را به بانک اطلاعاتی ندارد و اطلاعات خود را از کوکی شخص دریافت می‌کند. متد user.IsInRole نیز به همین نحو عمل می‌کند.


Tag Helper جدید SecurityTrimming

اکنون که سرویس ISecurityTrimmingService را پیاده سازی کرده‌ایم، از آن می‌توان جهت توسعه‌ی SecurityTrimmingTagHelper نیز استفاده کرد:
        public override void Process(TagHelperContext context, TagHelperOutput output)
        {
            context.CheckArgumentIsNull(nameof(context));
            output.CheckArgumentIsNull(nameof(output));

            // don't render the <security-trimming> tag.
            output.TagName = null;

            if(_securityTrimmingService.CanCurrentUserAccess(Area, Controller, Action))
            {
                // fine, do nothing.
                return;
            }

            // else, suppress the output and generate nothing.
            output.SuppressOutput();
        }
عملکرد آن نیز بسیار ساده است. اگر کاربر، به area:controller:action جاری دسترسی داشت، این Tag Helper کاری را انجام نمی‌دهد. اگر خیر، متد SuppressOutput را فراخوانی می‌کند. این متد سبب خواهد شد، هر آنچه که داخل تگ‌های این TagHelper قرار گرفته، در صفحه رندر نشوند و از خروجی آن حذف شوند. به این ترتیب، کاربر به اطلاعاتی که به آن دسترسی ندارد (مانند لینک به مدخلی خاص را) مشاهده نخواهد کرد. به این مفهوم security trimming می‌گویند.
نمونه‌ای از کاربرد آن‌را در ReportsMenu.cshtml_ می‌توانید مشاهده کنید:
            <security-trimming asp-area="" asp-controller="DynamicPermissionsTest" asp-action="Products">
                <li>
                    <a asp-controller="DynamicPermissionsTest" asp-action="Products" asp-area="">
                        <span class="left5 fa fa-user" aria-hidden="true"></span>
                        گزارش از لیست محصولات
                    </a>
                </li>
            </security-trimming>
در اینجا اگر کاربر جاری به کنترلر DynamicPermissionsTest و اکشن متد Products آن دسترسی پویا نداشته باشد، محتوای قرارگرفته‌ی داخل تگ security-trimming را مشاهده نخواهد کرد.

برای آزمایش آن یک کاربر جدید را به سیستم DNT Identity اضافه کنید. سپس آن‌را در گروه نقشی مشخص قرار دهید (منوی مدیریتی،‌گزینه‌ی مدیریت نقش‌های سیستم). سپس به این گروه دسترسی به تعدادی از آیتم‌های پویا را بدهید (گزینه‌ی مشاهده و تغییر لیست دسترسی‌های پویا). سپس با این اکانت جدید به سیستم وارد شده و بررسی کنید که چه تعدادی از آیتم‌های منوی «گزارشات نمونه» را می‌توانید مشاهده کنید (تامین شده‌ی توسط ReportsMenu.cshtml_).


مدیریت اندازه‌ی حجم کوکی‌های ASP.NET Core Identity

همانطور که ملاحظه کردید، جهت بالابردن سرعت دسترسی به اطلاعات User Claims و Role Claims، تمام اطلاعات مرتبط با آن‌ها، به کوکی کاربر وارد شده‌ی به سیستم، اضافه می‌شوند. همین مساله در یک سیستم بزرگ با تعداد صفحات بالا، سبب خواهد شد تا حجم کوکی کاربر از 5 کیلوبایت بیشتر شده و توسط مرورگرها مورد قبول واقع نشوند و عملا سیستم از کار خواهد افتاد.
برای مدیریت یک چنین مساله‌ای، امکان ذخیره سازی کوکی‌های شخص در داخل بانک اطلاعاتی نیز پیش بینی شده‌است. زیر ساخت آن‌را در مطلب «تنظیمات کش توزیع شده‌ی مبتنی بر SQL Server در ASP.NET Core» پیشتر در این سایت مطالعه کردید و در پروژه‌ی DNT Identity بکارگرفته شده‌است.
اگر به کلاس IdentityServicesRegistry مراجعه کنید، یک چنین تنظیمی در آن قابل مشاهده است:
 var ticketStore = provider.GetService<ITicketStore>();
identityOptionsCookies.ApplicationCookie.SessionStore = ticketStore; // To manage large identity cookies
در ASP.NET Identity Core، امکان تدارک SessionStore سفارشی برای کوکی‌ها نیز وجود دارد. این SessionStore  باید با پیاده سازی اینترفیس ITicketStore تامین شود. دو نمونه پیاده سازی ITicketStore را در لایه سرویس‌های پروژه می‌توانید مشاهده کنید:
الف) DistributedCacheTicketStore
ب) MemoryCacheTicketStore

اولی از همان زیرساخت «تنظیمات کش توزیع شده‌ی مبتنی بر SQL Server در ASP.NET Core» استفاده می‌کند و دومی از IMemoryCache توکار ASP.NET Core برای پیاده سازی مکان ذخیره سازی محتوای کوکی‌های سیستم، بهره خواهد برد.
باید دقت داشت که اگر حالت دوم را انتخاب کنید، با شروع مجدد برنامه، تمام اطلاعات کوکی‌های کاربران نیز حذف خواهند شد. بنابراین استفاده‌ی از حالت ذخیره سازی آن‌ها در بانک اطلاعاتی منطقی‌تر است.


نحوه‌ی تنظیم سرویس ITicketStore را نیز در متد setTicketStore می‌توانید مشاهده کنید و در آن، در صورت انتخاب حالت بانک اطلاعاتی، ابتدا تنظیمات کش توزیع شده، صورت گرفته و سپس کلاس DistributedCacheTicketStore به عنوان تامین کننده‌ی ITicketStore به سیستم تزریق وابستگی‌ها معرفی می‌شود.
همین اندازه برای انتقال محتوای کوکی‌های کاربران به سرور کافی است و از این پس تنها اطلاعاتی که به سمت کلاینت ارسال می‌شود، ID رمزنگاری شده‌ی این کوکی است، جهت بازیابی آن از بانک اطلاعاتی و استفاده‌ی خودکار از آن در برنامه.


کدهای کامل این سری را در مخزن کد DNT Identity می‌توانید ملاحظه کنید.
نظرات مطالب
آموزش ساخت و کار با subdomain در حالت لوکال هاست
مراحل کلی کار به این شکل است:
1- باز کردن فایل hosts و نوشتن کدهای زیر (البته شما می‌توانید هر اسم دلخواهی وارد نمایید)
 127.0.0.1 localhost
127.0.0.1 mysite.com
127.0.0.1 master.mysite.com
127.0.0.1 store1.mysite.com
127.0.0.1 store2.mysite.com
127.0.0.1 store3.mysite.com
127.0.0.1 store4.mysite.com
127.0.0.1 store5.mysite.com
127.0.0.1 store6.mysite.com
127.0.0.1 store7.mysite.com
127.0.0.1 store8.mysite.com
127.0.0.1 store9.mysite.com
2- اضافه کردن کد‌های زیر به قسمت binding در فایل applicationHosts.config از مسیر نصب IISExpress
 <bindings>
<!--<binding protocol="http" bindingInformation="*:80:localhost" />-->
<binding protocol="http" bindingInformation="*:80:mysite.com" />
<binding protocol="http" bindingInformation="*:80:master.mysite.com" />
<binding protocol="http" bindingInformation="*:80:store1.mysite.com" />
<binding protocol="http" bindingInformation="*:80:store2.mysite.com" />
<binding protocol="http" bindingInformation="*:80:store3.mysite.com" />
<binding protocol="http" bindingInformation="*:80:store4.mysite.com" />
<binding protocol="http" bindingInformation="*:80:store5.mysite.com" />
<binding protocol="http" bindingInformation="*:80:store6.mysite.com" />
<binding protocol="http" bindingInformation="*:80:store7.mysite.com" />
<binding protocol="http" bindingInformation="*:80:store8.mysite.com" />
<binding protocol="http" bindingInformation="*:80:store9.mysite.com" />
  </bindings>
3- باید برای IIS این آدرس‌ها را رجیستر کرد در غیر این صورت در زمان اجرا با خطا مواجه می‌شویم
برای این کار ابتدا cmd را با دسترسی مدیریتی اجرا کنید و با استفاده از دستور زیر می‌توان تک تک دامنه‌ها را رجیستر کرد:
 netsh http add urlacl url=http://mysite.com:80/ user=everyone
netsh http add urlacl url=http://master.mysite.com:80/ user=everyone
.
.
.
netsh http add urlacl url=http://store9.mysite.com:80/ user=everyone
در این صورت با اجرای پروژه سایت شما با آدرس‌های mysite.com و زیر دامنه‌های آن در دسترس خواهد بود.
پ.ن: در صورتی که نمی‌خواهید از پورت 80 استفاده کنید میتوانید به جای 80 پورت دلخواه خود را وارد نمایید. در این صورت از طریق آدرس mysite.com:mportnumber در دسترس خواهد بود