اشتراکها
بسیار پیش میآید که یک کنترلر را به یک اکشن فیلتر خاص مزین کنیم. در این صورت تمامی اکشنهای موجود در کنترلر مربوطه مجاب به اجرای اکشن فیلتر مورد نظر میشوند. اما بسیار پیش میآید که نخواهیم یک اکشن خاص در کنترلر مذکور اکشن فیلتر مورد نظر را اجرا کند.
یک راهکار ساده اما (به نظر شخصی من) غیر منطقی این است که تک تک اکشن هایی را که میخواهیم اکشن فیلتر مورد نظر را اجرا کنند، مزین کنیم و اکشنهایی که نمیخواهیم اکشن فیلتر مورد نظر را اجرا کنند به اکشن فیلتر مورد نظر مزین نمیکنیم. اما فرض کنید تعداد اکشنهای ما زیاد باشند؛ به نظر این روش غیر منطقی و غیر بهینه است.
یکی از مشکلاتی که در یکی از پروژهها حدود سه روز وقت من را گرفت همین کار بود. زمانی که از یک تصویر امنیتی جهت مقابله با ربوتهای استفاده میکردم به این مشکل برخورد کردم. کنترلر من به یک اکشن فیلتر فشرده سازی محتوا مزین بود. در نتیجه اکشنی که تصویر امنیتی را تولید میکرد نیز اکشن فیلتر فشرده سازی را اجرا میکرد و پس از فشرده سازی باعث میشد که تصویر امنیتی نشان داده نشود، چون فرمت تصویری آن بهم ریخته بود. یک راهکار را که پس از جستجو به آن رسیدم، جهت استفادهی دوستان مطرح میکنم.
برای اینکه از اجرای چنین اکشن فیلتر هایی جلوگیری کنیم نیاز است کمی اکشن فیلتر مورد نظر را دستکاری کنیم.
برای این کار باید مراحل زیر را انجام داد:
1- ابتدا یک Attribute خالی را تعریف میکنیم.
2- سپس اکشن فیلتر دلخواهی را تعریف کرده و در زمان اجرا بررسی میکنیم اگر متد (اکشن) مورد نظر با Attribute تعریفی در مرحله یک مزین شده بود، در نتیجه اکشن فیلتر را اجرا نمیکنیم.
3- هر اکشنی را که نمیخواهیم اکشن فیلتر تعریفی مرحله 2 را اجرا کند، آن را به Attribute مرحله یک مزین میکنیم.
به این ترتیب میتوانیم از اجرای اکشن فیلتر دلخواه روی متدها یا اکشنهای دلخواه جلوگیری کنیم. در ادامه نحوهی تعریف آنها را در زیر مشاهده میکنید.
1- تعریف یک Attribute دلخواه مثلا با نام DisableCompression
یک راهکار ساده اما (به نظر شخصی من) غیر منطقی این است که تک تک اکشن هایی را که میخواهیم اکشن فیلتر مورد نظر را اجرا کنند، مزین کنیم و اکشنهایی که نمیخواهیم اکشن فیلتر مورد نظر را اجرا کنند به اکشن فیلتر مورد نظر مزین نمیکنیم. اما فرض کنید تعداد اکشنهای ما زیاد باشند؛ به نظر این روش غیر منطقی و غیر بهینه است.
یکی از مشکلاتی که در یکی از پروژهها حدود سه روز وقت من را گرفت همین کار بود. زمانی که از یک تصویر امنیتی جهت مقابله با ربوتهای استفاده میکردم به این مشکل برخورد کردم. کنترلر من به یک اکشن فیلتر فشرده سازی محتوا مزین بود. در نتیجه اکشنی که تصویر امنیتی را تولید میکرد نیز اکشن فیلتر فشرده سازی را اجرا میکرد و پس از فشرده سازی باعث میشد که تصویر امنیتی نشان داده نشود، چون فرمت تصویری آن بهم ریخته بود. یک راهکار را که پس از جستجو به آن رسیدم، جهت استفادهی دوستان مطرح میکنم.
برای اینکه از اجرای چنین اکشن فیلتر هایی جلوگیری کنیم نیاز است کمی اکشن فیلتر مورد نظر را دستکاری کنیم.
برای این کار باید مراحل زیر را انجام داد:
1- ابتدا یک Attribute خالی را تعریف میکنیم.
2- سپس اکشن فیلتر دلخواهی را تعریف کرده و در زمان اجرا بررسی میکنیم اگر متد (اکشن) مورد نظر با Attribute تعریفی در مرحله یک مزین شده بود، در نتیجه اکشن فیلتر را اجرا نمیکنیم.
3- هر اکشنی را که نمیخواهیم اکشن فیلتر تعریفی مرحله 2 را اجرا کند، آن را به Attribute مرحله یک مزین میکنیم.
به این ترتیب میتوانیم از اجرای اکشن فیلتر دلخواه روی متدها یا اکشنهای دلخواه جلوگیری کنیم. در ادامه نحوهی تعریف آنها را در زیر مشاهده میکنید.
1- تعریف یک Attribute دلخواه مثلا با نام DisableCompression
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = true)] public sealed class DisableCompression : Attribute { }
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, Inherited = true, AllowMultiple = false)] public class CompressionFilter : ActionFilterAttribute { public override void OnActionExecuting(ActionExecutingContext filterContext) { bool disabled = filterContext.ActionDescriptor.IsDefined(typeof(DisableCompression), true) || filterContext.ActionDescriptor.ControllerDescriptor.IsDefined(typeof(DisableCompression), true); if (disabled) return; // کدهای دلخواه اکشن فیلتر مورد نظر } }
3- در این مرحله هر اکشنی را که نمیخواهیم اکشن فیلتر CompressionFilter را اجرا کند به Attribute با نام DisableCompression مزین میکنیم.
[CompressionFilter] public abstract class BaseController : Controller { } public class SomeController : BaseController { public ActionResult WantThisActionCompressed() { // code } [DisableCompression] public ActionResult DontWantThisActionCompressed() { // code } }
با این کار اکشن WantThisActionCompressed اکشن فیلتر CompressionFilter را اجرا میکند اما اکشن DontWantThisActionCompressed چون مزین به DisableCompression شدهاست، پس در نتیجه اکشن فیلتر CompressionFilter بر روی آن اجرا نخواهد شد.
فرض کنید در پروژهی جاری خودتون قصد دارید یک سیستم مدیریت سطوح دسترسی کاربران رو با انعطاف بالا پیاده سازی کنید . مثلا سیستم شما دارای صفحات مختلفی هستش که هر گروه کاربری اجازه دسترسی به هر صفحه رو نداره ... هدف اینه که شما این گروههای کاربری و سطوح دسترسی برای هر گروه رو برای سیستم مشخص کنید .
مثلا فقط کاربرانی که دسترسی admin دارن بتونن به صفحات مدیریتی دسترسی داشته باشن و ....
برای این منظور در دات نت کلاسی با نام Role Provider وجود داره که در ادامهی این مبحث به کار با اون میپردازیم.
مثلا فرض کنید قرار بر اینه که سطوح دسترسی رو از بانک اطلاعاتی استخراج کنیم . کلاس مذکور در فضای نام System.Web.Security قرار گرفته . برای شروع ما نیاز داریم یک نمونه از این کلاس رو پیاده سازی کرده و چند تابع از اون رو بازنویسی کنیم .
پیاده سازی کلاس به این صورته :
using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Security; namespace Myproject.Security { public class CustomRoleProvider : RoleProvider { } }
خب در مرحلهی بعد دو تابع از این کلاس رو بازنویسی میکنیم . اول تابع GetRolesForUser که در این مقاله وظیفهی استخراج لیست مجوزها برای هر کاربر رو از بانک اطلاعاتی داره که به شکل زیر پیاده سازی میشه .
public override string[] GetRolesForUser(string username) { using (DatabaseEntities db = new DatabaseEntities()) { User user = db.Users.FirstOrDefault(u => u.UserName.Equals(username, StringComparison.CurrentCultureIgnoreCase)); var roles = from ur in user.UserRoles from r in db.Roles where ur.RoleId == r.Id select r.Name; if (roles != null) return roles.ToArray(); else return new string[] {}; ; } }
همونطور که میبینید در تابع بالا از کلاس CustomRoleProvider ما عملیات استخراج لیست مجوزهای دسترسی مربوط به هر کاربر رو از بانک اطلاعاتی انجام دادیم . توجه داشته باشین که شما میتونید این واکشی رو از هر محیط دیگه ای جز بانک اطلاعاتی هم بسته به نوع کارتون انجام بدید .
تابع بعد IsUserInRole نام داره که با بازنویسی اون مشخص میکنیم که ایا یک کاربر دارای مجوز لازم برای دسترسی هست یا نه . اون رو به شکل زیر بازنویسی میکنیم.
public override bool IsUserInRole(string username, string roleName) { using (DatabaseEntities db = new DatabaseEntities()) { User user = db.Users.FirstOrDefault(u => u.UserName.Equals(username, StringComparison.CurrentCultureIgnoreCase)); var roles = from ur in user.UserRoles from r in db.Roles where ur.RoleId == r.Id select r.Name; if (user != null) return roles.Any(r => r.Equals(roleName, StringComparison.CurrentCultureIgnoreCase)); else return false; } }
همونطور که شاهد هستید در تابع بالا بعد از واکشی لیست مجوزهای ثبت شده برای هر کاربر بررسی انجام میشه که ایا اولا کاربر یک کاربر ثبت شده هست و ثانیا اینکه ایا درخواستی که ارسال کرده برای دسترسی به یک بخش مجوز اون رو داره یا خیر ...
این نکته رو یاد آور بشم که این توابع رو میشه به شکلهای مختلفی پیاده سازی کرد و اونچه که در اینجا نوشته شده فقط جهت مثال هستش . مثلا تابع IsUserInRole رو میشه به شکل زیر هم نوشت و این بسته به شرایط کاری داره که قصد انجام اون رو دارید ....
public override bool IsUserInRole(string username, string roleName) { return this.GetRolesForUser(username).Contains(roleName); }
خب میرسیم به بخش معرفی این Provider در web.config که به صورت زیر انجام میشه ...
<system.web> ... <rolemanager cacherolesincookie="true" defaultprovider="CustomRoleProvider" enabled="true"> <providers> <clear /> <add name="CustomRoleProvider" type="Myproject.Security.CustomRoleProvider" /> </providers> </rolemanager> ... </system.web>
توجه داشته باشید که مجوزهای هر کاربر با معرفی بالا بعد از یک بار واکشی در کوکی ذخیره میشه و دیگه هر بار، بار اضافه برای واکشی از بانک اطلاعاتی به برنامه تحمیل نمیشه ...
حالا به این صورت میتونیم مثلا یک Controller رو محافظت کنیم در برابر درخواست از جانب کاربرانی که سطح دسترسی به اون رو ندارند .
using System; using System.Web.Mvc; namespace MyProject.Areas.Admin.Controllers { [Authorize(Roles = "Administrators")] public class HomeController : Controller { // // GET: /Admin/Home/ public ActionResult Index() { return View(); } } }
توجه داشته باشید که کنترل مجوز برای بررسی وجود مجوز در بخشهای کوچکتر هم مانند اکشنها و ... میتونه در نظر گرفته بشه .
نگارش بعدی ASP.NET و تمام اجزای آن سورس باز هستند. به عنوان مخزن کد و هاست، GitHub را انتخاب کردهاند.
اشتراکها
سایت ASP.NET Monsters
اشتراکها