زمانی که در حال توسعه یک API با لاراول هستید، یکی از قسمتهای مهم احراز هویت (Authentication) است. احراز هویت در API به طور خلاصه باید به صورتی توسعه داده شود، که در زمان درخواست از سمت Client به Server، یک مشخصه برای سرور ارسال شود، تا بتواند تشخیص دهد، که آیا کاربر مورد نظر احراز شده است، یا خیر که بتواند، جوابی را به Client ارسال کند. در این مقاله به بررسی احراز هویت با API در لاراول میپردازیم.
پکیج Sanctum
لاراول در نسخه جدید خود پکیجی به نام Sanctum را منتشر کرد. هدف این پکیج استفاده از راهی مناسب برای احراز هویت در نرم افزارهایی، به نام SPA یا Single Page Application، است. که از طریق فریم ورکهای جاوا اسکریپت توسعه داده میشوند.برای احراز هویت با API در لاراول، کار Sanctum در اینجا به پایان نمیرسد، و این پکیج را در تمامی اپلیکیشنها و سیستمهای Token Base میتوان استفاده کرد. این پکیج همچنین قابلیت ساختن چند Token را برای هر کاربر در اپلیکیشن شما دارد، که حتی میتوانید برای هر توکن چندین قابلیت (Ability) قرار دهید، و از هر توکن به منظور دسترسی به قسمت خاصی از سیستم خود استفاده کنید. به زبان سادهتر به هر توکن میتوانید دسترسیهایی در سیستم خود دهید، که در ادامه بیشتر در مورد آن صحبت خواهیم کرد.
نصب پکیج Sanctum
برای نصب پکیج Sanctum از طریق خط فرمان دستور زیر را در پروژه خود اجرا کنید.
composer require laravel/sanctum
بعد از نصب پکیج با وارد کردن دستور زیر فایلهای پکیج را به پروژه خود منتقل کنید.
با اجرای دستور بالا فایلهای پکیج از جمله، فایل sanctum.php در مسیر config، که تنظیمات کلی پکیج پروژهی شما در آن قرار دارد و همچنین فایلهای Migration مربوط به جداول، در پروژه شما قرار داده میشود. بعد از اجرای دستور بالا نیاز است، در اولین مرحله جداول مربوط به Sanctum را به دیتابیس خود اضافه کنیم. دستور زیر را در خط فرمان پروژه خود اجرا کنید.
php artisan migrate
جدولی که از طریق کد بالا ساخته میشود، در فایل migration مربوط به sanctum در مسیر database/migrations و با نام personal_access_tokens قرار دارد. در صورتی که میخواهید، فایلهای مربوط به دیتابیس اجرا نشود، در فایل AppServiceProvider.php متد Sanctum::ignoreMigrations را در متد register صدا بزنید.
تنظیمات Sanctum
در فایل config/sanctum.php تنظیمات کلی مربوط به پکیج Sanctum وجود دارد، که در زیر به شما توضیح خواهیم داد.
stateful: اینجا میتوانید دامنههایی که اجازه درخواست به سرور را دارند، تعریف کنید.
expiration: مربوط به انقضای توکن است، که بر حسب دقیقه محاسبه میشود، و زمانی که null قرار بگیرد، توکن منقضی نخواهد شد.
middleware: زمانی که از احراز هویت در نرم افزار SPA استفاده میکنید، باید csrf-token هایی که از طریق پکیج Sanctum به نرم افزار داده میشود، اعتبار سنجی شوند. میتوانید Middlewareهای مربوط به این کار را اینجا قرار دهید.
استفاده از پکیج Sanctum در پروژه
میخواهیم در پروژهی خودمان از طریق پکیج Sanctum یک API بنویسیم، که کاربر را از طریق توکن احراز هویت میکند، و بعد از ورود کاربر یک توکن به آن میدهد، و در درخواستهای بعدی با قرار دادن آن توکن در Header درخواست به سرور میگوییم، که درخواست از طریق کدام کاربر به سرور ارسال شده است. در اولین مرحله یک پروژه جدید لاراول با دستور زیر بسازید.
بعد از انجام مراحل Config پروژه و نصب پکیج Sanctum که در بالا توضیح دادیم، یک مدل User تعریف میکنیم ( که لاراول به طور پیشفرض، مدل User را دارد. ) برای تولید توکن توسط Sanctum باید از Trait به نام HasTokenApi در مدل خود استفاده کنیم، تا متدهای ایجاد و بررسی توکن برای مدل مربوطه، به مدل اضافه شود. در مدل User کد زیر را قرار دهید.
<?php
namespace App;
use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Laravel\Sanctum\HasApiTokens;
class User extends Authenticatable
{
use HasApiTokens, Notifiable;
/**
* The attributes that are mass assignable.
*
* @var array
*/
protected $fillable = [
'name', 'email', 'password','username'
];
/**
* The attributes that should be hidden for arrays.
*
* @var array
*/
protected $hidden = [
'password', 'remember_token',
];
/**
* The attributes that should be cast to native types.
*
* @var array
*/
protected $casts = [
'email_verified_at' => 'datetime',
];
}
در قسمت اول تعریف کلاس User از Trait مربوط به پکیج Sanctum با نام HasApiTokens استفاده کردیم، که همچنین در قسمت اول فایل آن را وارد (Import) کردیم.
ورود با Api
یک متد برای ورود و دریافت توکن از طریق Api میخواهیم بسازیم. به این منظور یک Controller ایجاد میکنیم. با استفاده از دستور زیر میتوانید یک Controller ایجاد کنید.
php artisan make:controller Auth/UserController
با اجرای دستور بالا یک Controller در مسیر app/Http/Controller/Auth ساخته میشود. حال باید متد login را به صورت زیر بنویسیم.
<?php
namespace App\Http\Controllers\Auth;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use Illuminate\Validation\ValidationException;
use Illuminate\Support\Facades\Hash;
class UserController extends Controller
{
public function login()
{
\request()->validate([
'email' => 'required|email',
'password' => 'required'
]);
$user = User::where('email', \request('email'))->first();
if (! $user || ! Hash::check(\request('password'), $user->password)) {
throw ValidationException::withMessages([
'email' => ['The provided credentials are incorrect.'],
]);
}
return $user->createToken('token_base_name')->plainTextToken;
}
}
در متد لاگین از طریق ایمیل و رمزعبور اول صحت اطلاعات وارد شده را بررسی میکنیم و در صورت صحیح بودن اطلاعات از طریق متد createToken یک توکن برای کاربر مورد نظر در دیتابیس ایجاد میکنیم و توکن خام را به کاربر نشان میدهیم. حال باید یک مسیر برای ورود در فایل routes/api.php به شکل زیر ایجاد میکنیم.
مسیر بالا را به متد login در فایل UserController.php مرتبط کردیم و زمانی که آدرس localhost:8000/api/login را وارد کنید به متد مربوطه متصل خواهد شد. زمان درخواست مسیر بالا با متد POST باید در body درخواست دو پارامتر با نام email و password قرار دهیم، که اطلاعات ما خواهند بود. خروجی در نرم افزار Postman به صورت زیر است. خروجی را برای درخواستهای بعد باید در Header درخواست خود، وارد کنید.
محافظت از مسیر ها
میخواهیم از طریق پکیج Sanctum از مسیرها محافظت کنیم، و یک مسیری بسازیم، که فقط در زمانی که کاربر وارد شده بود، و درخواست دارای توکن بود آن مسیر اجرا شود. برای انجام این کار باید یک guard با درایور token ایجاد کنیم، و ما guard پیشفرض api را تغییر میدهیم. در فایل config/auth.php قسمت guards را به صورت زیر تغییر میدهیم. توجه کنید به صورت پیشفرض نیز لاراول درایور api را token قرار داده است. در غیر این صورت، به صورت زیر تغییر دهید.
تنظیمات بالا مربوط به پروژه آموزش احراز هویت در لاراول است. حال در فایل routes/api.php یک مسیر به صورت زیر ایجاد میکنیم.
Route::get('/user', function (){
return \request()->user();
})->middleware('auth:sanctum');
مسیر بالا اطلاعات کاربری که به توکن ارسالی در Header، مرتبط است را برمیگرداند. در Postman به صورت زیر باید در تب Auth نوع احراز هویت را Bearer انتخاب کنید، و در فیلد بعدی توکن را به صورت زیر وارد کنید. در صورتی که توکن را وارد نکنید با خطای 401 (Unautheticated) برمیخورید. و در صورت وارد کردن اطلاعات به شما نمایش داده میشود. برای اضافه کردن توکن در Header درخواست، در فریم ورکهای جاوا اسکریپت یا اجرای درخواست بدون استفاده از Postman باید توکن را با کلید Authorization و به صورت {Bearer {token در Header قرار دهید.
مسیر Logout
زمانی که قرار است از نرم افزار خارج شوید باید عملیات Logout را انجام دهید و در این میان نیاز است توکن ساخته شده را حذف (Revoke) کنید. مسیر Logout را به صورت زیر در فایل routes/api.php قرار دهید.
Route::get('/user/logout', function (){
\request()->user()->tokens()->delete();
return response([], 204);
})->middleware('auth:sanctum');
در ارسال درخواست به آدرس بالا نیز باید توکن را بفرستید، زیرا که نیاز است کاربر وارد شده را پیدا کند، و با متد tokens تمامی توکنهای آن را بگیرد، و بعد از آن با delete تمامی توکنهای مربوط به کاربر وارد شده را حذف میکند. دقت کنید که در کد بالا تمامی توکنهای کاربر حذف میشود، ولی از طریق کد زیر میتوانید توکن مربوطه را پیدا کنید، و فقط آن را حذف کنید.
متغیر id همان id مربوط به توکن خواهد بود که میتوانید از راههای مختلف آن را به دست آورید.
دسترسیها در Sanctum
پکیج Sanctum یک ویژگی با نام Ability دارد، که میتوانید در هنگام ایجاد توکن در قسمت ورود برای هر توکن که ایجاد میکنید، یک سری Ability نیز به آن دهید. در این مقاله یک روش نیز برای دسترسی به قسمتهای مختلف پروژه از طریق قابلیتی به نام Policy در لاراول را به شما خواهیم گفت.
ایجاد Ability برای کاربر
در فایل UserController.php که در بالاتر ایجاد کردیم، متد login را به صورت زیر تغییر دهید.
public function login()
{
$this->validate(\request(),[
'email' => 'required|email',
'password' => 'required'
]);
$user = User::where('email', \request('email'))->first();
if (! $user || ! Hash::check((int)\request('password'), $user->password)) {
throw ValidationException::withMessages([
'email' => ['The provided credentials are incorrect.'],
]);
}
return $user->createToken('token_base_name', ['user:view'])->plainTextToken;
}
در کد بالا در ورودی دوم متد createToken یک آرایه قرار میگیرد، که شامل قابلیتهایی (Ability) است، که توکن میتواند آن را انجام دهد. ما یک آرایه با یک خانه به صورت user:view به آن دادیم، که کاربر در صورت ورود میتواند، در قسمتی از نرم افزار که نیاز به این دسترسی دارد، دسترسی داشته باشد.
ایجاد قوانین دسترسی
میتوانیم از قابلیت Policy در لاراول استفاده کنیم. Policy در لاراول به ما این امکان را میدهد، تا برای دسترسی به مدلهای خود یک قاعده و قانون بنویسیم. برای مثال میخواهیم در یک Controller پنج قسمت لیست، نمایش، اضافه، ویرایش و حذف را برای یک مدل بنویسیم. میتوانیم برای آن مدل یک Policy تعریف کنیم، تا برای هر کدام از عملیات بالا یک قانون بنویسیم. برای ایجاد یک Policy از طریق خط فرمان دستور زیر را اجرا کنید.
php artisan make:policy UserPolicy -m User
کلید m- یک مدل را انتخاب میکند. فایل Policy در مسیر app/Http/Policies قرار میگیرد. فایل UserPolicy را باز کنید. متدهای مختلفی در آن تعریف شده است، که همچنین شما میتوانید، متد سفارشی خود را نیز بنویسید. ما در این پروژه میخواهیم، برای نمایش اطلاعات کاربر این روش را بررسی کنیم. متد view مربوط به نمایش یک کاربر میباشد. دو ورودی میگیرد، که اولی مربوط به کاربر وارد شده است، و دومی مربوط به مدلی است، که قرار است عملیاتی بر روی آن اجرا شود. متد view را به صورت زیر تغییر دهید.
public function view(User $user, User $model)
{
return $user->tokenCan('user:view') && $user->id === $model->id;
}
دو شرط قرار دادهایم. یکی اینکه کاربر وارد شده دسترسی user:view را داشته باشد، و دوم اینکه مدلی که درخواست میکند دقیقا همان مدل خودش باشد، و نتواند اطلاعات دیگر کاربران را مشاهده کند. تا اینجا ما قانون مربوط به نمایش اطلاعات کاربر را نوشتهایم. حال در فایل UserController.php که در قسمت اول آموزش ساختهایم، یک متد با نام getUser به صورت زیر مینویسیم.
public function getUser()
{
$this->authorize('view', \request()->user());
return \request()->user();
}
در قسمت اول با استفاده از متد authorize برقرار بودن قانون مربوط به مدل درخواستی که همان مدل کاربر وارد شده است، را بررسی میکنیم. ورودی اول این متد، همان متدهای مربوط در فایل policy است، و ورودی دوم، مدل درخواستی که قصد انجام عملیاتی (در اینجا نمایش) بر روی آن را داریم، است. در آخرین مرحله باید مسیر مربوط به نمایش اطلاعات کاربر را تغییر دهیم. مسیری که در بالاتر ساختیم را به صورت زیر تغییر دهید.
حال اگر دوباره یک توکن جدید بگیریم، و به آدرس localhost:8000/api/user به همراه توکن درخواست بزنیم، اطلاعات به ما نمایش داده میشود. ولی اگر توکن ما اجازه دسترسی به آن قسمت را نداشته باشد، (طبق قانون نوشته شده در Policy نباشد) با خطای عدم دسترسی 403 مواجه میشوید. جمعبندی در این مقاله نحوهی پیادهسازی احراز هویت با API در لاراول از طریق پکیج Sanctum که توسط خود لاراول توسعه داده شده است، را مورد بررسی قرار دادهایم. این پکیج به صورت Token Base کار میکند. به این معنی که یک توکن به شما میدهد. و در درخواستهای بعدی که نیاز به احراز هویت دارد، آن توکن را باید در Header درخواست خود وارد کنیم. همچنین ویژگی Ability در پکیج را توضیح دادیم. و با یک مثال نشان دادیم، که چطور میتوانید یک سیستم دسترسی در پروژه خود ایجاد کنید.
اگر به یادگیری بیشتر لاراول علاقه داری میتوانی در دوره آموزش پروژه محور لاراول کاربردی (بسته پروژه محور) شرکت کنی، این دوره شامل ۱۲ پروژه کاربردی و پر استفاده در دنیای واقعی است، که تمامی پروژهها به صورت کامل برنامه نویسی خواهند شد، تا دانشجو بتواند با روند ایجاد و تکمیل پروژه به صورت کامل آشنا شود.
۱۶ دیدگاه
مهران۱۳ مهر ۱۴۰۲، ۱۶:۳۶
توی جدول users نیازه که ستون api_tokens باشه ؟ به من خطا میده میگه این ستون وجود ندارد ! لاراول من 10 هست اگه این ستون نیازه پس چرا از اول نیست ؟
Column not found: 1054 Unknown column 'api_token' in 'where clause' (Connection: mysql, SQL: select * from users where api_token = ld1bmhdVFj6RksdEInavxFvSmVzGfAZD7GGm9mnW12591d0f limit 1)
نازنین کریمی مقدم۲۴ مهر ۱۴۰۲، ۰۵:۵۳
درود
این تاپیک رو بررسی کنید:
https://stackoverflow.com/questions/58644716/laravel-and-passport-getting-sqlstate42s22-column-not-found-1054-unknown-col
۲۹ فروردین ۱۴۰۲، ۱۲:۵۴
سلام وقت شما بخیر.
برای لاگین از طریق وب هم باید مجددا کد لاگین و رجیستر بنویسیم؟
یا وقتی api لاگین و رجیستر رو نوشتیم میشه برای حالت WEB هم استفاده کرد؟
ممنون
نازنین کریمی مقدم۰۴ اردیبهشت ۱۴۰۲، ۱۷:۳۷
درود
افزونه معرفی شده برای spa به کار میره اما با کمی تغییر در کد لاگین بله میتونید در وب ازش استفاده کنید چون منطق کار یکسان هست.
۰۸ شهریور ۱۴۰۱، ۱۵:۰۴
واسه postman من چیزی به اسم bearer token ,نداره ؟!
نازنین کریمی مقدم۰۹ شهریور ۱۴۰۱، ۰۴:۳۳
درود
<a href="https://stackoverflow.com/questions/49785592/bearer-token-in-postman" target="_blank" rel="noopener nofollow ugc">این لینک</a> رو ببینید به صورت تصویری گفته که کجاست و چجوری دیده میشه.
۱۸ فروردین ۱۴۰۱، ۰۰:۵۰
اخه به چه درد میخوره وقتی ایمیل وریفای نشده باشه . اینی ک گفتید همجای نت هست
احمد۱۲ اردیبهشت ۱۴۰۰، ۱۸:۰۳
خیلی عالی بود.
خسته نباشید.
Hossein Ashouri۱۶ آذر ۱۳۹۹، ۰۸:۰۴
سلام.من دقیقا همون کار هایی که گفته شده رو انجام دادم و پس از debug فهمیدم تابع login مشکل داره.لطفا راهنمایی بفرمایید
رضا زیدی۲۱ آذر ۱۳۹۹، ۱۸:۲۶
سلام
تابع login مربوط به trait با نام AuthenticatesUsers هستش که در خود پکیج laravel/ui وجود داره و در این مقاله تابع login جدیدی تعریف نشده
اگر مایلید پیام اخطار رو برامون بفرستید تا بهتر بتونیم راهنماییتون کنیم
حسین۱۳ آبان ۱۳۹۹، ۰۸:۱۱
سلام در متد لاگین بعد از اینکه صحت اطلاعات ارسال شده برسی شد برای لاگین کردن از دستور auth()->login() یا دستورات مشابه این برای لاگین استفاده نکردین!
آیا این دستور زیر هم توکن ایجاد میکنه و هم عمل لاگینو مثل فساد Auth انجام میده؟
رضا زیدی۲۸ آبان ۱۳۹۹، ۱۰:۱۶
سلام
ما در اینجا فقط به توکن احتیاج داریم. از auth()->login زمانی استفاده میکنیم که بخوایم از سشن استفاده کنیم، چون auth()->login یا متدهای مشابه باعث ذخیرهی سشن برای کاربر لاگین شده میشن. در حالی که ما اینجا فقط به توکن ارسالی از سمت کاربر برای احراز هویت نیاز داریم.
مهدیه پارسا۱۸ مهر ۱۳۹۹، ۱۹:۱۱
سلام ممنون از مقاله خوبتون من یه سوال دارم تفاوت احراز هویت با توکن یا کوکی رو نمیفهمم. که لاراول گفته با استفاده از کوکی این کار انجام میشه و مزایای خودش رو داره اصلا کوکی چیه و با اون jwt چه فرقی داره ؟
رضا زیدی۲۸ آبان ۱۳۹۹، ۱۰:۳۰
سلام
کوکی معمولا برای احراز هویت با استفاده از سشن استفاده میشه و زمانی کاربرد داره که کاربر از مرورگر استفاده کنه. اما توکن عموما برای زمانی هست که مرورگری وجود نداره، مثل اپلیکیشنهای موبایل؛ هرچند که میتونه در مرورگر هم استفاده بشه.
JWT هم یک الگوریتم برای ایجاد توکن هست.
mahdi۲۱ مرداد ۱۳۹۹، ۲۱:۲۶
سلام وقت بخیر ممنون از مقاله ی خوبتون
من الان یکم گیج شدم تفاوت Sanctum با passport و اون middleware api:token که داخل خود لاراول هست و پکیج jwt چیه ؟ همشون token base هستن ؟ چه موقعی از هر کدوم باید استفاده کرد؟ پیشاپیش تشکر بابت پاسختون :)
رضا زیدی۲۸ آبان ۱۳۹۹، ۱۰:۴۷
سلام
Sanctum یک روش ساده رو برای ایجاد توکن و احراز هویت به وسیلهی اون فراهم میکنه، اما Passport از OAuth2 استفاده میکنه و پیچیدگیهای اون رو در خودش داره و البته امکانات بیشتری هم داره. middleware با نام auth:api درست هست و زمانی برای وب سرویسها استفاده میشه که نیاز به احراز هویت کاربر با توکن وجود داشته باشه. پکیج JWT هم از الگوریتم JWT برای ایجاد توکن استفاده میکنه. از این الگوریتم در Passport هم برای ایجاد توکن استفاده میشه.
بسته به نیازی که دارید میتونید از هرکدوم استفاده کنید و قاعدهی کلی وجود نداره. مطالعهی بیشتر در این زمینه رو بهتون توصیه میکنم.
شروع رایگان یادگیری برنامه نویسی
کلیک کنید 👇
دوره الفبای برنامه نویسی با هدف انتخاب زبان برنامه نویسی مناسب برای شما و پاسخگویی به سوالات متداول در شروع یادگیری موقتا رایگان شد: