توکن JWT (JSON Web Token) یک استاندارد امن و فشرده برای تبادل اطلاعات بین کلاینت و سرور است. دلیل اصلی استفاده از آن، احراز هویت بدون وضعیت (Stateless) است. یعنی سرور نیازی به ذخیره سشن و بررسی کوکیها ندارد و کاربر با ارائه این توکن در هر درخواست، هویت خود را ثابت میکند.
لاراول بهصورت پیشفرض از سشنها استفاده میکند، اما در توسعه API برای موبایل یا فریمورکهای مدرنی مثل React و Vue یا اپلیکیشنهای موبایل با بکاند لاراول، به احراز هویت بدون وضعیت نیاز داریم که JWT این مشکل را بهخوبی حل میکند.
یکی از بهترین ابزارها برای پیادهسازی JWT در لاراول، jwt-auth است که در ادامه بیشتر با روش استفاده از آن آشنا میشویم.
راهنمای نصب jwt-auth
ابتدا با کامپوزر بسته را روی لاراول نصب میکنیم:
composer require php-open-source-saver/jwt-auth
سپس تنظیمات بسته را منتشر میکنیم تا بتوانیم به دلخواه آن را تغییر دهیم
php artisan vendor:publish --provider="PHPOpenSourceSaver\JWTAuth\Providers\LaravelServiceProvider"
با این کار فایل config/jwt.php ساخته میشود.
مرحلهی بعدی ساخت کلید امنیتی است که با فراخوانی دستور زیر انجام میشود:
php artisan jwt:secret
مرحلهی بعدی ویرایش مدل کاربر است تا بتوانیم از این بسته به درستی استفاده کنیم. لازم است تا یک اینترفیس و دو تابع به آن اضافه کنیم:
use PHPOpenSourceSaver\JWTAuth\Contracts\JWTSubject;
class User extends Authenticatable implements JWTSubject {
public function getJWTIdentifier() {
return $this->getKey();
}
public function getJWTCustomClaims() {
return [];
}
}
اگر میخواهید احراز هویت پیشفرض روی JWT باشد، در فایل config/auth.php، گارد api را به این شکل تنظیم کنید:
'guards' => [
'api' => [
'driver' => 'jwt',
'provider' => 'users',
],
],
ابزارهای کتابخانه jwt-auth
وقتی کتابخانه jwt-auth را روی لاراول نصب میکنیم، چند تابع بسیار مهم در اختیار ما قرار میگیرد که ابزارهای ورود و خروج آسان را در اختیار ما قرار میدهند.
- تابع
attempt: این تابع اطلاعات ورود (مثل ایمیل و رمز عبور) را دریافت میکند. اگر اطلاعات درست باشد، یک رشته متنی طولانی (همان توکن) تولید میکند و اگر غلط باشد، مقدارfalseبرمیگرداند. - تابع
user: هر زمان که بخواهید بدانید توکن ارسالی متعلق به کدام کاربر است، این تابع اطلاعات کامل کاربر (مدل User) را از دیتابیس استخراج میکند. - تابع
refresh: توکنها برای امنیت بیشتر تاریخ انقضا دارند. این تابع توکن قدیمی را میگیرد و یک توکن جدید با تاریخ انقضای تمدیدشده تحویل میدهد. - تابع
logout: با فراخوانی این تابع، توکن فعلی وارد لیست سیاه میشود و دیگر در هیچ درخواستی اعتبار نخواهد داشت.
پیادهسازی در لاراول
برای اینکه درک بهتری از نحوه کارکرد این توابع، یک کنترلر احراز هویت (AuthController) را در ادامه مشاهده میکنید. در این مثال، عملیات ورود و دریافت اطلاعات کاربر را در کنار هم قرار دادهایم:
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
class AuthController extends Controller {
public function login(Request $request) {
$credentials = $request->only('email', 'password');
// مرحله اول: بررسی اطلاعات و صدور توکن
if( ! $token = auth( 'api' )->attempt( $credentials ) ) {
return response()->json( [ 'error' => 'ایمیل یا رمز عبور اشتباه است' ], 401 );
}
// مرحله دوم: دریافت اطلاعات کاربری که با موفقیت لاگین کرده است
$user = auth( 'api' )->user();
// بازگرداندن توکن و اطلاعات کاربر به سمت کلاینت
return response()->json( [
'access_token' => $token,
'user_info' => $user,
'token_type' => 'bearer'
] );
}
public function logout() {
auth( 'api' )->logout();
return response()->json( [ 'message' => 'با موفقیت خارج شدید' ] );
}
}
ارتباط فرانتاند با لاراول
در سمت کلاینت (یک سایت طراحی شده با جاوااسکریپت، ریاکت یا ویو)، ما باید توکنی که از لاراول دریافت کردهایم را در مرورگر ذخیره کنیم و در تمام درخواستهای بعدی، آن را به لاراول نشان دهیم. یک محل مناسب برای ذخیرهسازی توکن، localStorage است.
// قدم اول وارد شدن به سیستم و دریافت توکن است
async function loginUser() {
const response = await fetch( '/api/login', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ email: 'user@test.com', password: 'password123' })
} );
const data = await response.json();
// اگر توکن با موفقیت دریافت شد، آن را در مرورگر ذخیره میکنیم
if( data.access_token ) {
localStorage.setItem( 'my_jwt_token', data.access_token );
console.log( 'شما با موفقیت وارد شدید!' );
}
}
// زمانی که توکن را در اختیار داشته باشیم میتوانیم
// درخواستهای خصوصی را ارسال کنیم
async function fetchPrivateArticles() {
const token = localStorage.getItem( 'my_jwt_token' );
const response = await fetch( '/api/private-articles', {
method: 'GET',
headers: {
// توکن باید دقیقا به همین شکل ارسال شود
'Authorization': `Bearer ${token}`,
'Accept': 'application/json'
}
} );
const articles = await response.json();
console.log( 'مقالات دریافت شده:', articles );
}
با استفاده از JWT، شما میتوانید یک سیستم تعیین هویت مستقل از سشن بسازید که بهراحتی میتواند به هزاران کاربر روی پلتفرمهای مختلف (وب، اندروید، iOS) پاسخ دهد. کافی است کلاینت، توکن را دریافت کرده و در هدر Authorization برای سرور ارسال کند. این روش ایمنی و کارایی خوبی دارد و یک روش استاندارد و قابل توسعه به شمار میرود.
