28
تشکر

اتمام زمان نشست (Session timeout) در لاراول

Session timeout

Session timeout

حتما پیشخوان سایت هایی مثل Mailchimp یا بلاگفا یا phpMyAdmin را دیده اید، که اگر بعد از مدت زمانی مشخص هیچ فعالیتی انجام ندهید، از شما درخواست ورود مجدد به سایت را میکند.

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

دلیل مهمی که اینکار میتواند داشته باشد این است که اگر شما سیستمی که با آن وارد حساب کاربری خود شده‌اید را رها کنید، ممکن است فرد دیگری از سیستم شما سوء استفاده کرده و وارد حساب کاربری شما در سایت مورد نظر شود.

برای جلوگیری از این مسئله باید نشست یا Session کاربر را بعد از زمان مشخصی که فعالیت نداشته است غیر فعال کنیم، در این صورت احتمال سوء استفاده از حساب کاربری کمتر میشود. البته باز هم امکان سوء استفاده وجود دارد.

برای حل این مسئله ما از میان افزارها یا Middleware استفاده می کنیم.

پس ابتدا با دستور زیر یک میان افزار می سازیم:

php artisan make:middleware SessionTimeout

دو متغیر برای نگهداری زمان انقضا نشست و شئ کلاس Store میخواهیم که آن‌ها را تعریف میکنیم:

<?php namespace App\Http\Middleware;

use Closure;

class SessionTimeout
{
  protected $session;

  protected $timeout = 1200;

  /**
   * Handle an incoming request.
   *
   * @param  \Illuminate\Http\Request  $request
   * @param  \Closure  $next
   * @return mixed
   */
  public function handle($request, Closure $next)
  {
    return $next($request);
  }
}
&#91;/php&#93;</div>
در تابع سازنده، کلاس <span class="en-words">Store</span> که در لاراول مربوط به نشست می‌شود را تزریق می‌کنیم و متغیر <span class="en-words">session</span> را برابر با شئ کلاس <span class="en-words">Store</span> می‌کنیم.
<div class="mycode">[php]
public function __construct(Store $session)
{
  $this->session = $session;
}

در تابع handle اگر کاربر، نشست lastActivityTime را نداشته باشد (یعنی کاربر تازه وارد پیشخوان یا برای اولین بار وارد شده است، یا سشن قبلا از بین رفته است) برای کاربر یک نشست با کلید lastActivityTime و مقدار زمان کنونی را به صورت time stamp قرار می‌دهیم.

public function handle($request, Closure $next)
{
  if(!$this->session->has('lastActivityTime')) {
    $this->session->put('lastActivityTime', time());
  }
}

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

public function handle($request, Closure $next)
{
  if(!$this->session->has('lastActivityTime')) {
    $this->session->put('lastActivityTime', time());
  }
  elseif(time() - $this->session->get('lastActivityTime') > $this->timeout) {
    $this->session->forget('lastActivityTime');
    Auth::logout();
    return redirect('login')->with(['warning' => 'شما در '.$this->timeout/60 .'دقیقه گذشته فعالیتی نداشتهاید. لطفا دوباره وارد شوید.']);
  }
}

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

public function handle($request, Closure $next)
{
  if(!$this->session->has('lastActivityTime')) {
    $this->session->put('lastActivityTime', time());
  }
  elseif(time() - $this->session->get('lastActivityTime') > $this->timeout) {
    $this->session->forget('lastActivityTime');
    Auth::logout();
    return redirect('login')->with(['warning' => 'شما در '.$this->timeout/60 .'دقیقه گذشته فعالیتی نداشتهاید. لطفا دوباره وارد شوید.']);
  }

  $this->session->put('lastActivityTime', time());
  return $next($request);
}

کد نهایی ما به صورت زیر می‌شود.

<?php namespace App\Http\Middleware;

use Closure;
use Illuminate\Support\Facades\Auth;
use Illuminate\Session\Store;

class SessionTimeout
{
  protected $session;

  protected $timeout = 1200;

  public function __construct(Store $session)
  {
    $this->session = $session;
  }

  /**
   * Handle an incoming request.
   *
   * @param  \Illuminate\Http\Request  $request
   * @param  \Closure  $next
   * @return mixed
   */
  public function handle($request, Closure $next)
  {
    if(!$this->session->has('lastActivityTime')) {
      $this->session->put('lastActivityTime', time());
    }
    elseif(time() - $this->session->get('lastActivityTime') > $this->timeout) {
      $this->session->forget('lastActivityTime');
      Auth::logout();
      return redirect('login')->with(['warning' => 'شما در '.$this->timeout/60 .'دقیقه گذشته فعالیتی نداشتهاید. لطفا دوباره وارد شوید.']);
    }

    $this->session->put('lastActivityTime', time());
    return $next($request);
  }
}

اصولا باید این میان افزار در Route group مورد استفاده قرار بگیرد یعنی باید آن را به صورت زیر به یک Route group اعمال کنید.

Route::group(['prefix' => 'dashboard', 'middleware' => ['auth', 'timeout']], function() {

  Route::controller('profile/{id}', 'Dashboard\UserProfileController');

  Route::controller('/', 'UserDashboardController');
});

حال، هر زمانی که کاربر درخواست جدیدی را ارسال کند، ابتدا میان افزار اجرا خواهد شد.

همچنین فراموش نکنید که باید این میان افزار را در فایل app/Http/Kernel.php ثبت کنید، به صورت زیر:

'timeout'       =>  '\App\Http\Middleware\SessionTimeout',

نکات دیگری که باید مورد توجه قرار بگیرد:

1- بعد از خروج کاربر دوباره کاربر را مجبور نکنید که کل مسیر قبلی را پیمایش کند تا به جایی که قبلا بوده است برسد، یعنی باید کاربر را Redirect back کنیم که اصول کاربر پسند بودن را نیز حفظ کرده باشیم.

2- زمانی که کاربر را خارج می‌کنیم پیغامی را هم برای کاربر نمایش دهیم که دلیل خارج شدن خود را بداند. نمایش این پیغام که در Session قرار دارد. زمان انقضا را نیز می‌توانید به صورت دلخواه عوض کنید، اما زمان معمول آن 20 دقیقه است.

3- با Middleware Parameter که جدیدا در Laravel 5.1 آمده، امکانات بیشتری بدست می‌آوریم و می‌توان پیاده‌سازی بهتری با توجه به نیاز داشته باشیم.

امیدوارم لذت برده باشید. اگه  نظری در مورد کد یا پیشنهادی یا … دارید لطفا در قسمت نظرات بنویسید.

وحید منتظر

کلا یک ساله که بیشتر درگیر وب هستم(قبلا کمتر). اوایل خیلی دنبال مطالب سئو بودم. بعدش دنبال یادگیری CSS3, HTML5, Jquery و ... بودم. کمی بعدتر رفتم دنبال یادگیری PHP بعدش هم چیزی که خیلی خیلی مجذوبم کرد لاراول بود (در حال حاضر). به فوتبال خیلی علاقه دارم. (بازی کردن) و به نظرم فوتبال وسعت و پیچیدگی بیشتری نسبت به چیزایی که تاحالا یاد گرفتم داره و ذهن بهتری می خواد.

  • جواد نیازی می‌گه:

    خیلی جالب بود. زمان نشست خیلی مبحث مهمیه و من درگیر اونم هنوز.

  • حمید قائمی می‌گه:

    وا چرا هرجا پا میذارم از فریم ورک لاراول میشنوم؟ مثل اینکه این فریم ورک خیلی طرفدار داره!

  • hCzss می‌گه:

    سلام آقای شفیعی

    اگه میشه یه سری توضیحات درباره نحوه پست گذاشتن بدین

    • محسن شفیعی می‌گه:

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

  • بهزاد عزیزان می‌گه:

    سلام
    ممنون وحید جان
    وقتی انرژی مثبتی که بعد از شروع به کار مجدد آپارنت رو دیدم خیلی اشتیاق پیدا کردم که منم بتونم نقشی داشته داشته باشم و الان که ساعت 4 و 33 دقیقه صبحه بگم محسن جان اگه نظرت مثبته من هم بتونم نویسنده باشم..


  • نظرات این مطلب بسته است.