پیاده‌سازی سیستم احراز هویت Sanctum در بستر API با روش توسعه TDD

در این مقاله در ادامه مبحث توسعه به روش TDD می‌خواهیم سیستم احراز هویت با Sanctum روی بستر API در لاراول پیاده‌سازی کنیم. در این آموزش تصور شده شما با مباحث پایه‌ای برای کار با لاراول و PHPUnit آشنا هستید در غیر اینصورت پیشنهاد می‌کنم مطالب پیشین در این دروه رو مطالعه کنید!


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

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

Sanctum یک پکیج سبک و ساده برای احراز هویته که توسط لاراول توسعه داده شده و برای پروژه‌های اصطلاحا تک‌ صفحه‌ای SPA، اپ‌های موبایل و سرویس‌های که در بستر Rest API کار می‌کنند، استفاده میشه! 

این بسته در سرویس‌های SPA، از سیستم‌ احراز هویت مبتنی بر cookie استفاده میکنه. به این معنی که حین کار با فناوری‌های فرانت‌اند نظیر angular یا react سرویس sanctum یک کوکی برای کاربر احراز هویت شده در مرورگر ذخیره می‌کند. این بسته همچنین در بستر‌های Rest API مبتنی بر توکن، قابلیت دارد و توکن ایجاد شده رو برای مدیریت ایمن‌تر در یک جدول به نام personal_access_tokens ذخیره می‌کند. در اینجا اگر عملیات احراز هویت نتواند با جدول یاد شده مطابقت داشته باشد احراز هویت شکست می‌خورد.

Image for post
Image for post

خب حالا بیایید در ابتدا پکیج sanctum رو روی پروژه‌‌ای که در این دوره روی آن کار می‌کردیم نصب کنیم (می‌توانید کدهای آن رو در گیت‌هاب مشاهده کنید):

composer require laravel/sanctum

سپس با استفاده از دستور زیر فایل‌های مربوط به تنظیمات و migration رو به پروژه اضافه کنید:

php artisan vendor:publish --provider="Laravel\Sanctum\SanctumServiceProvider"

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

Image for post
Image for post

سپس به مسیر  config/auth.php بروید و تنظیمات API رو همانند زیر اعمال کنید:

'api' => [
    'driver' => 'token',
    'provider' => 'users',
    'hash' => false,
],

خب حالا پس از نصب و تنظیمات اولیه پکیج Sanctum بصورت قدم به قدم فرآیندهای مربوط به احراز هویت کاربران رو با روش توسعه TDD پیاده می‌کنیم!

قدم اول - ثبت نام

ابتدا یک کنترلر ایجاد کنید:

 php artisan make:controller AuthController

حالا وارد کنترلر بشید و تابع مورد نیاز برای ثبت‌نام رو همانند زیر تعریف کنید:

Image for post
Image for post

سپس به route/api.php بروید و مسیر ارسال درخواست ثبت‌نام رو مانند زیر تعریف کنید:

Route::post('register', [AuthController::class, 'register']);

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

php artisan make:test AuthTest

و یک تابع برای تست ایجاد کاربر جدید مه بصورت زیر در نظر گرفته شده:

Image for post
Image for post

همانطور که مشاهده می‌کنید من در اینجا برای اطمینان از اینکه پس از دریافت پاسخ درست از سمت سرور درون response توکنی موجود هست یا نه! دستور دیگری نوشتم که به دنبال کلید token درون آرایه response می‌گرده و اگر این کلید وجود داشته باشه تست پاس میشه.

من مجددا تست رو اجرا کردم:

Image for post
Image for post

و همانطور که مشاهده می‌کنید خطای عدم نبود جدول رو دریافت کردیم. این مشکل هم با اضافه کردن خاصیت ‍RefreshDatabase همانطور که در مقاله قبلی اشاره شد، حل میشه! پس از افزودن این خاصیت به کلاس AuthTest و اجرای دوباره دستور تست خروجی نشان میده که کاربر با موفقیت ثبت‌نام شده و token هم برای کاربر ایجاد شده و در دسترسه:

Image for post
Image for post

در ادامه می‌تونید سناریوهای دیگه هم برای تست هنگام ثبت‌نام کار رو هم بنویسید من اینکار رو برای ایمیل تکراری، پسورد کوتاه و تایید پسورد اشتباه هم نوشتم:‌

Image for post
Image for post

و با تست کلی نتیجه بصورت زیر خواهد بود:

Image for post
Image for post

 

قدم دوم - ورود به حساب کاربری

در گام بعدی و پس از ثبت نام، عملیات ورود به حساب کاربری رو پیاده‌سازی می‌کنیم. برای این منظور وارد AuthController میشیم و تابع login مانند زیر تعریف می‌کنیم:

Image for post
Image for post

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

Image for post
Image for post

در ادامه من تست دیگری رو برای بررسی ورود کاربر با اطلاعات غلط هم نوشتم که بصورت زیر هست:

Image for post
Image for post

همانطور که مشاهده می‌کنید. در اینجا ما می‌خواهیم مطمئین بشیم که زمانی که اطلاعات اشتباه باشند خطای 401 برگرده پس تست ما زمانی پاس میشه که درخواست ما به سرور با پاسخ 401 همراه بشه!


قدم آخر بهینه‌سازی کدها

تا اینجا ما یک سیستم ثبت نام و احراز هویت با Sanctum و در بستر API ایجاد کردیم و سناریو‌هایی رو برای تست تعریف کردیم. حالا بیایید کمی کدهامون رو داخل کنترلر AuthController تمیزتر کنیم. 

اول از همه میریم سراغ تعریف یک request برای عملیات ثبت‌نام و قوانین اعتبارسنجی رو به اونجا منتقل می‌کنیم:

php artisan make:request RegisterRequest

وارد مسیر app/Http/Request بشید و RegisterRequest رو مانند زیر ویرایش کنید:

Image for post
Image for post

حالا به کنترلر و متد ثبت‌نام برمی‌گردیم و مانند زیر اون رو ویرایش ‌می‌کنیم:

Image for post
Image for post

همانطور که مشاهده می‌کنید اینبار کد بسیار کوتاه‌تر و خواناتر شد حالا یک تست کلی رو اجرا می‌کنیم تا مشکلی بوجود نیومده باشه!

Image for post
Image for post

خطا !!؟

یکی از مزیت‌های استفاده از روش TDD این هست که در هر مرحله‌ای که تغییری در سیستم داشتیم می‌تونیم با یک دستور ساده از درستی تغییرات اطمینان پیدا کنیم و این خیلی خیلی به روند توسعه کمک می‌کنه!

در اینجا ما در register کلمه عبور کاربر رو هنگام ذخیره هش می‌کردیم که با تغییری که اعمال کردیم این قسمت رو فراموش شد به همین خاطر در مرحله ورود کاربر اطلاعات ورودی درست نبودند. یکی از راه‌حل‌ها استفاده از متد validate مانند زیر هست که پسورد رو بعد از بررسی هش میکنه، برای این منظور مجددا وارد مسیر app/Http/Request بشید و RegisterRequest رو مانند زیر ویرایش کنید:

Image for post
Image for post

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

Image for post
Image for post

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

حالا کافیه به مسیر app/Http/Controllers/Controller.php‍ برید و این خاصیت رو در اونجا اعمال کنید. حالا به کنترلر AuthController برمیگردیم و اون رو پس از اعمال تغییراتی که انجام دادیم، بازنویسی می‌کنیم نتیجه کار بصورت زیر شد:

Image for post
Image for post

این کد قطعا بسیار کوتاه‌تره و خوانایی بهتری هم داره. اما صبر کنید یک تست دیگه هم بگیریم:

Image for post
Image for post

انتظار این یکیو داشتم!!!

توجه داشته باشید! هر وقت خروجی رو تغییر بدید باید انتظار عدم دسترسی رو داشته باشید. خیلی سادس ولی همین موارد کلی وقت از شما می‌گیره در اینجا هم به برای برطرف کردن این دو ایراد باید به تست‌های login و register که نوشتیم بریم و تغییر زیر رو اعمال کنیم:

$this->assertArrayHasKey('token', $response['data']);

و خب همانطور که خروجی زیر داره نشون میده ما هیچ ایرادی در فرآیند ثبت نام نداریم:

Image for post
Image for post

تبریک می‌گم شما در این آموزش با نحوه پیاده‌سازی فرآیند احراز هویت با پکیج Sanctum لارول رو یادگرفتید. در اینجا ما در بستر Api و با روش توسعه TDD اینکار رو انجام دادیم و همانطور که مشاهده با این روش کا بسیار راحت‌تر هست و با اطمینان بیشتری کدنویسی نیازمندی‌های انجام میشه و قادر هستید به سرعت صحت تغییراتتون رو بررسی کنید. 

 

امیدوارم از آموزش لذت برده باشید. کد مربوط به این دوره هم می‌تونید در گیت‌هاب مشاهده کنید.