سیستم احراز‌ هویت کاربران در جنگو

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

مدیریت کاربران حتی در یک سطح عادی – فراموشی رمز عبور، فراموشی نام کاربری، بروزرسانی اطلاعات کاربری و…- نیز دشوار است، حال نوشتن چنین سیستمی از ابتدا می‌تواند واقعا دردسرساز و سخت باشد.

خوشبختانه برای ما توسعه‌دهندگان پایتون، جنگو از یک پیاده‌سازی پیشفرض استفاده می‌کند که در آن همه چیز از قابلیت مدیریت کاربران، گروه‌ها، مجوزهای دسترسی و کوکی‌ها به سادگی انجام پذیر است.

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

بررسی کلی

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

سیستم احراز هویت به صورت کلی شامل موارد زیر است:

  • کاربران
  • دسترسی‌ها: از طریق باینری (True/False) مدیریت می‌شود.
  • گروه‌ها: راهی بسیار مناسب برای دادن دسترسی به حجمی بالا از کاربران.
  • سیستم هشینگ پسورد
  • فرم‌هایی برای مدیریت اعتبارسنجی و احرازهویت کاربران
  • ابزارهای View در مدل MVT برای مدیریت ورود کاربران
  • سیستم قابل توسعه در بک‌-اند

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

استفاده از سیستم احراز هویت جنگو

User Objects

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

به صورت کلی هر کاربری که در User Objects تعریف می‌شود دارای خصوصیات زیر است:

  • username
  • password
  • email
  • first_name
  • last_name

ایجاد یک Superuser

ایجاد superuser در جنگو با استفاده از دستور createsuperuser انجام می‌شود:

البته اطلاعات اضافی مورد نیاز برای ورود بعد از وارد کردن دستور بالا به صورت prompt از شما پرسیده می‌شود.

ایجاد کاربر جدید

راحت‌ترین و کم خطاترین راهکار برای اضافه کردن کاربر جدید به وبسایت استفاده کردن از ادمین جنگو است. جنگو همچنین Viewها و Formهایی را ایجاد کرده که به سادگی شما را قادر می‌سازند تا قابلیت‌هایی مانند ورود، فراموشی رمز عبور، ثبت‌نام کاربران و… را در اپلیکیشن‌تان پیاده‌سازی می‌کنید.

در این قسمت برای ایجاد کاربری جدید قصد داریم از Shell مربوط به جنگو استفاده کنیم. در این محیط ما قابلیت استفاده از دستورات پایتون را داریم، از این رو به سادگی می‌توانیم با مدل شئ‌گرای ORM جنگو نیز کار بکنیم:

حال همانطور که می‌توانید مشاهد بکنید، ما کاربری جدید با نام کاربری john، ایمیل lennon@thebeatles.com و رمز عبور johnpassword ایجاد کرده‌ایم. حال اگر بخواهیم ویژگی‌های دیگر آن را تغییر دهیم می‌توانیم به سادگی از حالت زیر استفاده نماییم:

برای تغییر دادن رمز عبور کاربران ما دو راهکار بسیار ساده را داریم که می‌شود از آن‌ها استفاده کرد. یکی از طریق فایل manage.py و دیگری از طریق Shell.

۱- با وارد کردن دستور python manage.py changepassword username می‌توانیم رمز عبور کاربر username را به سادگی تغییر دهیم.

۲- برای استفاده کردن از Shell برای تغییر پسورد، شما ابتدا باید کاربر مورد نظرتان را با متد get دریافت کنید:

بعد از آن می‌توانید با استفاده از متد set_password() پسورد جدیدی را برای کاربر تعیین نمایید:

اگر در بخش middleware در قسمت settings جنگو SessionAuthenticationMiddleware را فعال کرده باشید، این تغییر پسورد به صورت یک لاگ ظاهر می‌شود.

مجوز‌ها و اعتبار سنجی

در پنل ادمین جنگو، ما قابلیت ایجاد مجوزهای دسترسی برای کاربران را داریم. هر کدام از این مجوزها نیز نامی دارند که می‌شود تنها با assign کردن آن‌ها به یک کاربر و یا یک گروه کاربری آن‌ها را محدود کرد. سه مورد موجود از این مجوزها را می‌توان در زیر مشاهده کرد:

  • استفاده کردن از view «add» برای افزودن از طریق فرم‌های موجود تنها برای کاربرانی مقدور است که مجوز add را دارند.
  • استفاده کردن از view «change» برای تغییر از طریق فرم‌ها تنها برای کاربرانی مقدور است که مجوز change را دارند.
  • استفاده کردن از view «delete» برای تغییر از طریق فرم‌ها تنها برای کاربرانی مقدور است که مجوز delete را دارند.

شما جدای از پنل جنگو، می‌توانید این مجوزها را از طریق متدهای زیر نیز به User Object اعمال کنید:

has_add_permission()

has_change_permission()

has_delete_permission()

مجوزهای پیشفرض

در قسمت تنظیمات پروژه برای لیست INSTALLED_APPS می‌توانید django.contrib.auth را مشاهده کنید. این اپلیکیشن به صورت پیشفرض در تمام پروژه‌های جنگو وجود دارد. شما هر بار که عملیات migrate را روی مدل‌های دیتابیس خود اعمال کنید، این اپلیکیشن مجوز‌های پیشفرض add،  changeو delete را برای تمام مدل‌ها ایجاد می‌کند.

گروه‌ها

مدل‌ django.contrib.auth.models.Group یک راه منطقی و بسیار ساده برای دسته‌بندی کردن کاربران و قرار دادن آن‌ها در دسته‌بندی مشخصی است. شما می‌توانید از این طریق مجوز‌های کاربری را به صورت گروهی اعمال نمایید. شما می‌توانید یک کاربر را در هر چند گروه که بخواهید قرار دهید. مجوزهای دسترسی در این حالت override شده و چند مجوز به یک کاربر در این شرایط اعمال می‌شود.

ایجاد مجوز به صورت دستی

در حالیکه می‌شود از طریق class Meta در مدل‌های ساخته شده، مجوزهای دسترسی را به صورت دستی ایجاد کنید، اما یک روش مستقیم برای اینکار استفاده از خط فرمان است. برای مثال توسط بکنید که ما می‌خواهیم یک دسترسی can_publish را برای مدل BookReview در مدل books ایجاد کنیم. برای اینکار به صورت زیر عمل می‌کنیم:

بعد از آن می‌شود از طریق خاصیت user_permissions مجوز مربوطه را به یک گروه و یا یک کاربر اعمال کرد.

بررسی وجود یک مجوز

اگر بخواهید به صورت سریع بعد از ارائه یک مجوز و یا ایجاد یک کاربر مجوزهای موجود برای آن کاربر را بررسی کنید، یکی از راحت‌ترین راه‌حل‌ها برای انجام چنین کاری استفاده کردن از رویکرد زیر است:

در پایان

در این مطلب سعی کردم تا شما را با سیستم احراز هویت و اعتبار سنجی در جنگو آشنا کنم. البته در اینجا ما به صورت کلی با این موضوع آشنا شدیم، زمانی که خودتان وارد عمل شوید و بخواهید از این قابلیت‌ها در یک اپلیکیشن واقعی استفاده بکنید، چالش‌های شیرین بسیاری را باید پشت سر بگذارید. این مطلب از کتاب Build your first website with Django 2.1 نوشته Nigel George گرفته شده است.

منبع

حل مشکل Race Condition توی جنگو

داشتم یه اپلیکیشن ساده جَنگو رو می‌ساختم که با یه مشکل به اسم Race Condition برخوردم. خیلی چیز جالب و در عین حال ساده‌ایه که باید حتما حتما حل بشه. خب منبع این آموزش کوچولو موچولو وبسایت اصلی Django هستش و توی آخر همین پست لینکش رو گذاشتم.

قضیه از چه قراره؟! تصور کنید که یه وبسایت فروشگاهی دارید که توش میخواید یه مداد بفروشید. تعداد مدادهای موجودتون ۱۰ تا هستش. منطق برنامه شما به این صورته که به ازای هر فروش یک عدد از مقادیر موجودتون کم میشه. پس من اگه بیام یه مداد بخرم میشه ۹ تا.

ولی وقتی من میام یه مداد می‌خرم دقیقا چه چیزی اون پشت مشتا اتفاق می‌افته. خیلی ساده است: بانک اطلاعاتی میاد اول مقدار موجود رو می‌گیره و اون رو میبره تو رَم، براساس منطق برنامه میاد یه دونه کم میکنه پس مقدار ۱۰ توی رم میشه ۹ و در نهایت object.save() … مقدار توی بانک اطلاعاتی ذخیره میشه.

حالا یه مشکلی که پیش میاد توی این حالت چیه؟ اینکه اگه دو نفر همزمان داشتن یه مداد میخریدن چه اتفاقی می‌افته.

نفر ۱: بانک اطلاعاتی ۱۰ رو گذاشته توی رم و یکی ازش کم میکنه و مقدار میشه ۹ و ذخیره می‌کنه.

نفر ۲: بانک اطلاعاتی ۱۰ رو گذاشته توی رم و یکی ازش کم میکنه و مقدار میشه ۹ و ذخیره می‌کنه.

وقتی انبار رو نگاه می‌کنی می‌بینی ۸ تا مداد داری ولی بانک اطلاعاتی میگه ۹ تا داری! اینجوریه که برنامه شما خطا میره  ☺😉🙃

راه‌حلش چیه؟! خب این مطلب می‌خواد همین رو بگه. برای حل این مشکل کافیه از یه F() expression هستش:

اول بیایید اون رو import کنید:

F() به ما این قابلیت رو میده که قبل از ذخیره داده یا همون object.save() توی بانک اطلاعاتی، اون فیلد یا آبجکتی که می‌خوایم ذخیره بشه رو بروزرسانی کنیم. اینطوری دیگه مشکل Race Condition رو نداریم.

برای استفاده از این حالت می‌تونید توی تعریف View به صورت زیر کار بکنید:

خیلی ساده و خوشمزه 😉

موسیقی این مطلب هم یک کار از نوازنده جَز Django Reinhardt: