سلام 🙂
خیلی از مقالاتی که در مورد مدیریت دسترسی کاربران بحث می کنند،فقط به نحوه اعتبارسنجی کاربران اشاره دارند که صرفا دارای دو سطح امنیتی می باشد:یا کاربر وارد سیستم شده(logged in) و یا نشده(not logged in).برای بسیاری از وب سایت ها درجه بالاتری از کنترل نیاز است تا بتوانند مشخص کنند که کاربران شان به چه جاهایی دسترسی داشته باشند و چه کارهایی بتوانند انجام دهند.
ایجاد یک لیست کنترل دسترسی(ACL) می تواند با انعطاف پذیری خود،دست شما را در انجام این کار باز بگذارد.
مقدمه
تصور کنید یک وبسایت بزرگ دارید که به کاربران اجازه می دهد تا طیف وسیعی از تکنیک های طراحی و برنامه نویسی وب را یاد بگیرند.علاوه بر این کاربران معمولی،شما تعدادی کاربر ویژه(پولی) دارید و به اینها،نویسندگان سایت و مدیران را نیز اضافه کنید.
مشکل شما
شما می خواهید کاری کنید که کاربران فقط در حیطه نوع حساب کاربری خودشان،به محتویات سایت دسترسی داشته باشند.
راه حل
با ایجاد یک لیست کنترل دسترسی،شما قادر خواهید بود به صورت ریز به ریز تعیین کنید که چه کاربرانی به چه قسمت هایی از سایت دسترسی داشته باشند یا نه.
اگر نمونه نمایشی این مطلب را ببینید،با یک صفحه index مواجه می شوید که ACL را برای هر کاربر امتحان می کند.شما با انتخاب لینک های پایین این صفحه می توانید ACL مربوط به دیگر کاربران را نیز مشاهده کنید.همچنین اگر روی لینک ‘Admin Screen’ در بالای صفحه کلیک کنید،می توانید نمونه ای از رابط مدیریت را ببینید که به شما اجازه می دهد،کاربران،وظایف و دسترسی ها را مدیریت کنید.نکته:سیستم مدیریتی هر ۳۰ دقیقه همه چیز را از پایگاه داده به روز رسانی خواهد کرد تا از بروز بودنشان مطمئن شود.همچنین در فایل های قابل دانلود،مسئله امنیت بخش مدیریت نیز جاسازی شده است،بنابراین اگر کاربر شماره یک،اجازه ‘access admin’ را نداشته باشد،شما قادر نخواهید بود به بخش مدیریت دسترسی پیدا کنید.
این سیستم شما را قادر خواهد کرد که گروه های مختلف کاربری ایجاد کنید(مثل:مهمان،کاربران ویژه،نویسندگان یا مدیران).به علاوه ما قادر خواهیم بود برای هر گروه یا به صورت انفرادی برای هر کاربر،دسترسی های جداگانه ای را تعیین کنیم.اجازه دهید کارمان را با تنظیم پایگاه داده MySQL شروع کنیم.
قدم ۱:ایجاد پایگاه داده
ACL ما در ۶ جدول به هم مرتبط،ذخیره خواهد شد(شامل جدولی برای کاربران).شما باید قبلا یک پایگاه داده را در محیط میزبان خود(چه محلی و چه وب) ایجاد کرده باشید.ساختار جداول ما به این شکل خواهد بود:
کدی که برای ایجاد پایگاه داده(شامل جداول) لازم است،در کد منبع موجود است(install.sql).همچنین یک فایل دیگر(sampleData.sql) نیز موجود است که برای شما ۴ کاربر نمایشی با وظایف و دسترسی های مختلف ایجاد می کند تا با آنها سیستم را تست کنید.آسان ترین راه برای اجرای فایل های فوق این است که آنها را در ویرایشگر دلخواه خود باز کرده و کدهایشان را در پنل SQL برنامه phpMyAdmin کپی کنید.
قدم ۲:اتصال به پایگاه داده
ما نیاز به ایجاد یک فایل include داریم تا بتوانیم به پایگاه داده متصل شویم.یک فایل با نام assets/php/database.php ایجاد کرده و کدهای زیر را به آن اضافه کنید(مقادیر متغیر ها را با اطلاعات مربوط به سرور خودتان تغییر دهید)
<?php session_start(); ob_start(); $hasDB = false; $server = 'localhost'; $user = 'root'; $pass = 'mysql'; $db = 'acl_test'; $link = mysql_connect($server,$user,$pass); if (!is_resource($link)) { $hasDB = false; die("Could not connect to the MySQL server at localhost."); } else { $hasDB = true; mysql_select_db($db); } ?>
در خط اول ما،session_start() را فراخوانی کردیم.در حقیقت ما از متغیرهای جلسه استفاده نخواهیم کرد ولی شما به عنوان قسمتی از سیستم اعتبارسنجی کاربران،به آن نیاز خواهید داشت.بعد با فراخوانی ob_start()،یک بافر خروجی ایجاد کردیم.عموما،وقتی php صفحه ای را ایجاد کرد،آن را به عنوان صفحه ساخته شده،به مرورگر می فرستد.اگر از تابع ob_start استفاده کنیم،صفحه و هدرهای آن تا زمانی که کامل بارگذاری نشوند،یا تا زمانی که ما تابع ob_end_flush() را فراخوانی کنیم،به مرورگر فرستاده نخواهند شد.با استفاده از قابلیت بافر کردن صفحه،ما می توانیم علاوه بر بالای صفحه،در هر نقطه ای از برنامه که بخواهیم،بیننده را منتقل(redirect) کنیم.بعد از ارسال هدرها،تنها گزینه ی ما برای این کار،استفاده از جاوااسکریپت خواهد بود و یک هکر با تجربه می تواند با خاموش کردن جاوااسکریپت،برای ما دردسر ساز شود.این یک خط به ما اجازه می دهد اگر نیاز بود،بتوانیم در هر نقطه ای از صفحه،اجازه دسترسی را به کاربر ندهیم.
خط ۴ تا ۸ متغیر های ما را تنظیم می کنند.$hasDB که از نوع بولین می باشد،برای تشخیص متصل بودن به پایگاه داده بکار می رود.چهار متغیر بعدی نیز برای اتصال به پایگاه داده کاربرد دارند.در خط ۹ ه پایگاه داده متصل می شود و در خط ۱۰ چک می کند که آیا اتصال انجام شد یا خیر؟اگر همه چیز درست بود،در خط ۱۵ دیتابیس مان را انتخاب می کنیم وگرنه بوسیله تابع die،یک خطا نمایش داده و اجرای برنامه متوقف می شود.
قدم ۳:ایجاد کلاس ACL
انصافا این بخش مقداری طولانی است چون قرار است کلاس ACL را که پایه سیستم ما است،ایجاد کنیم.بدین خاطر،برای طولانی بودن آن،پوزش می خواهم.
سیستم ACL ما،شی گرا(Object Oriented Programming) می باشد،برای همین ابتدا فایل حاوی کلاس را ایجاد می کنیم.کارمان را با تعریف کلاس،خاصیت های کلاس و متد سازنده(constructor__) شروع می کنیم و در فایلی بنام assets/php/class.acl.php ذخیره می کنیم:
<?php class ACL { var $perms = array(); //Array : Stores the permissions for the user var $userID = 0; //Integer : Stores the ID of the current user var $userRoles = array(); //Array : Stores the roles of the current user function __constructor($userID = '') { if ($userID != '') { $this->userID = floatval($userID); } else { $this->userID = floatval($_SESSION['userID']); } $this->userRoles = $this->getUserRoles('ids'); $this->buildACL(); } function ACL($userID='') { $this->__constructor($userID); }
تحلیل کد
بعد از تعریف کلاس،سه متغیر یا خاصیت که اطلاعات مربوط به ایجاد ACL را در خود خواهند داشت،ایجاد می کردیم.
متد سازنده(Constructor)
تابع یا متد Constructor،اولین متدی است که به صورت اتوماتیک،هنگام ایجاد شی(Object) از روی کلاس(Class)،فراخوانی می شود.یعنی هر وقت که از ;new ACL استفاده کنیم.بعد یک آرگومنت اختیاری از ID کاربر به آن پاس داده شده است تا ACL یا (لیست کنترل دسترسی ها) را بر طبق آن بارگذاری کند.ما چک می کنیم که آیا ID کاربر به آن پاس داده شده یا نه؟اگر ID پاس داده نشده بود،فرض می کنیم که می خواهیم ACL را برای کاربری که در حال حاضر وارد شده(logged in) است،بارگذاری(Load) کنیم بنابراین مقدار موجود در متغیر جلسه (Session) آن را می خوانیم.در غیر این صورت،اگر این مقدار پاس داده شده بود،ما می توانیم ACL را برای کاربرانی غیر از کاربر جاری که وارد شده است نیز تغییر دهیم و این برای سیستم مدیریت لازم خواهد بود یعنی در جایی که شما به عنوان مدیر سیستم می خواهید لیست دسترسی های کاربری را تغییر دهید.
بعد از خواندن ID کاربر،می رویم سراغ متد getUserRoles تا آرایه ای از رفتارهای تخصیص داده شده به کاربر را در داخل متغیر کلاس یا همان خاصیت $userRoles ذخیره کنیم.در انتهای متد سازنده نیز با فراخوانی متد buildACL() می توانیم ACL واقعی و مورد نظرمان را ایجاد کنیم.متدی که در انتها می بینید،عصای دست php4 می باشد.هنگامی که ما در php5،کد new ACL() را برای ایجاد شی،فراخوانی می کنیم،مفسر php متد __constructor() را اجرا می کند اما اگر همین کد را در php4 اجرا کنید،مفسر متد ACL() را اجرا می کند.یعنی مفسر php4 اول همان متدی را فراخوانی و به جای متد سازنده در php5 می شناسد،که همنام کلاس باشد.بنابراین با نوشتن متدی همنام با کلاس مان می توانیم از سازگاری برنامه با php4 نیز مطمئن شویم.
از این به بعد هر موقع ما شی ACL را با پاس دادن ID کاربری به آن،ایجاد کنیم،این شی مجوزهای دسترسی همان کاربر را در خود نگه خواهد داشت.
متدهای کمکی(Helper Methods)
حالا اجازه دهید تعدادی متد کمک کننده دیگر را به مان کلاس اضافه کنیم.این متدها با انجام کارهای اختصاص یافته به خودشان،پشتیبان متدهای دیگر خواهند بود.
function getUserRoles() { $strSQL = "SELECT * FROM `user_roles` WHERE `userID` = " . floatval($this->userID) . " ORDER BY `addDate` ASC"; $data = mysql_query($strSQL); $resp = array(); while($row = mysql_fetch_array($data)) { $resp[] = $row['roleID']; } return $resp; } function getAllRoles($format='ids') { $format = strtolower($format); $strSQL = "SELECT * FROM `roles` ORDER BY `roleName` ASC"; $data = mysql_query($strSQL); $resp = array(); while($row = mysql_fetch_array($data)) { if ($format == 'full') { $resp[] = array("ID" => $row['ID'],"Name" => $row['roleName']); } else { $resp[] = $row['ID']; } } return $resp; } function buildACL() { //first, get the rules for the user's role if (count($this->userRoles) > 0) { $this->perms = array_merge($this->perms,$this->getRolePerms($this->userRoles)); } //then, get the individual user permissions $this->perms = array_merge($this->perms,$this->getUserPerms($this->userID)); } function getPermKeyFromID($permID) { $strSQL = "SELECT `permKey` FROM `permissions` WHERE `ID` = " . floatval($permID) . " LIMIT 1"; $data = mysql_query($strSQL); $row = mysql_fetch_array($data); return $row[0]; } function getPermNameFromID($permID) { $strSQL = "SELECT `permName` FROM `permissions` WHERE `ID` = " . floatval($permID) . " LIMIT 1"; $data = mysql_query($strSQL); $row = mysql_fetch_array($data); return $row[0]; } function getRoleNameFromID($roleID) { $strSQL = "SELECT `roleName` FROM `roles` WHERE `ID` = " . floatval($roleID) . " LIMIT 1"; $data = mysql_query($strSQL); $row = mysql_fetch_array($data); return $row[0]; } function getUsername($userID) { $strSQL = "SELECT `username` FROM `users` WHERE `ID` = " . floatval($userID) . " LIMIT 1"; $data = mysql_query($strSQL); $row = mysql_fetch_array($data); return $row[0]; }
متد getUserRoles
متد getUserRoles() وظایف کاربر جاری را در قالب یک آرایه بر خواهد گرداند.ابتدا،دستور SQL مناسب را نوشته و اجرا می کنیم.بوسیله حلقه while()،بین نتایج یافت شده،یک حلقه ایجاد می کنیم تا ID را یکی یکی به آن اضافه کنیم.در پایان آرایه ای از ID را در اختیار داریم.همچنین متد getAllRoles() تمام وظایف موجود را بر می گرداند(نه فقط یکی از انها را).بوسیله آرگومنت $format،این متد می تواند آرایه ای شامل ID تمام وظایف را برگردانذ یا بوسیله یک آرایه انجمنی(آرایه ای که ایندکس آن علاوه بر عدد می تواند هر چیزی باشد)،ID و به همراه عنوان وظابف را نیز برگشت دهد.به این ترتیب بوسیله یک متد،دو کار انجام می دهیم و متد بعدی تو جیب مان می ماند!اگر بخواهیم آرایه شرح وظایف کاربر را در MySQL استفاده کنیم،نیاز به آرایه ای داریم که فقط شامل ID وظایف(Roles) باشد ولی چون ما می خواهیم آنها را در صفحه ای نشان دهیم،راه دوم کار ما را بسیار آسان می کند.
متد getUserRoles() وظایف کاربر جاری را در قالب یک آرایه بر خواهد گرداند.ابتدا،دستور SQL مناسب را نوشته و اجرا می کنیم.بوسیله حلقه while()،بین نتایج یافت شده،یک حلقه ایجاد می کنیم تا ID را یکی یکی به آن اضافه کنیم.در پایان آرایه ای از ID را در اختیار داریم.همچنین متد getAllRoles() تمام وظایف موجود را بر می گرداند(نه فقط یکی از انها را).
buildACL
buildACL() متدی است که آرایه دسترسی های کاربر را ایجاد می کند و حکم قلب سیستم ما را دارد.اول چک می کنیم که آیا نقشیبه کاربر محول شده است یا نه.اگر این طور بود،با استفاده از array_merge()،آرایه قبلی حاوی دسترسی ها را با آرایه جدیدی که متد getRolePerms() بر می گرداند،ترکیب می کنیم(بدین ترتیب تمام مجوزهای دسترسی مربوط به تمام رفتارهای محوله به کاربر ار خواهیم داشت).بعد همین کار را با فراخوانی متد getUserPerms() برای دسترسی های مخصوص کاربر نیز انجام می دهیم.این مهم است که ما مجوزهای دسترسی کاربر را دوبار بخوانیم زیرا تابع array_merge() کلیدهای تکراری را بازنویسی می کند.با این کار مطمئن خواهیم شد دسترسی های مختص کاربر با دسترسی های ارثی مربوط به رفتارها جایگزین خواهند شد.
همه توابع getPermKeyFromID()،getPermNameFromID()،getRoleNameFromID() و getUsername() تقریبا مشابه هم هستند و به ما اجازه می دهند تا یک ID را به آنها پاس دهیم تا یک مقدار متنی مناسب به ما باز گردانند.شما می توانید ببینید که چگونه بعد از ایجاد کدهای SQL مناسب،آنها را اجرا کردیم و نتیجه بازگردانده شد.
در بخش بعدی دو متد ایجاد خواهیم کرد تا دسترسی های(permissions) کاربر را از پایگاه داده استخراج کنند…
سلام وقت بخیر
در قسمت راه حل فرمودین که” اگر نمونه نمایشی این مطلب را ببینید،با یک صفحه index مواجه می شوید که ACL را برای هر کاربر امتحان می کند.شما با انتخاب لینک های پایین این صفحه می توانید ACL مربوط به دیگر کاربران را نیز مشاهده کنید.همچنین اگر روی لینک ‘Admin Screen’ در بالای صفحه کلیک کنید،می توانید نمونه ای از رابط مدیریت را ببینید” اما لینکی من پیدا نکردم،تو بخش بعد اضافه میکنید؟
میشه ادامش بدین
با سلام و تشکر از سایت خوبتون
در قدم ۲ از لحاظ ترجمه مشکل وجود داره :
“تا زمانی که ما تابع ob_end_flush() را فراخوانی کنیم،به مرورگر فرستاده نخواهند شد.با استفاده از قابلیت بافر کردن صفحه،ما می توانیم(((((علاوه بر بالای صفحه))))،در هر نقطه ای از برنامه که بخواهیم،بیننده را منتقل(redirect) کنیم”
:منظور از header در متن اصلی تابع header() بوده نه بالای صفحه.
ممنون.
سلام
اون “علاوه بر بالای صفحه” که فرمودین مربوط به این بخش هست :”instead of just at the top”
ممنون از اینکه با دقت و حساسیت مطالب رو دنبال می کنید
موفق باشید
سپاس …
منتظر بقیشم !!! 🙂