استيثاق المستخدمين (User Authentication) في Laravel 5


محمد أحمد العيل

يأتي Laravel، وهذه إحدى ميزاته الكثيرة، مضمنّا بآليات استيثاق Authentication جاهزة للاستخدام. سنطبق في هذا الدرس آلية الاستيثاق على صفحة الدفع checkout بحيث يُسمح بالدخول للمستخدمين المسجلين فقط.

هذا الدرس جزء من سلسلة تعلم Laravel والتي تنتهج مبدأ "أفضل وسيلة للتعلم هي الممارسة"، حيث ستكون ممارستنا عبارة عن إنشاء تطبيق ويب للتسوق مع ميزة سلة المشتريات. يتكون فهرس السلسلة من التالي:

laravel5-user-auth.png

يغطي الدرس المواضيع التالية:

  • إعداد الاستيثاق في Laravel 5.
  • أساسيات الاستيثاق في Laravel 5.
  • تغيير رابط تسجيل الدخول المبدئي.

إعدادات الاستيثاق في Laravel 5

يوجد ملف إعداد الاستيثاق على المسار config/auth.php. في ما يلي جزء من الملف:

'model' => App\User::class,
'table' => 'users',
'password' => [
        'email' => 'emails.password',
        'table' => 'password_resets',
        'expire' => 60,
    ],

يحدّد الملف:

  • اسم نموذج الاستيثاق.
  • اسم جدول المستخدمين.
  • خيارات إعادة تعيين كلمة السر.

سنستخدم النموذج User لاستيثاق المستخدمين. افتح الملف User.php وعدّله على النحو التالي:

<?php

namespace App;

use Illuminate\Auth\Authenticatable;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Contracts\Auth\Authenticatable as AuthenticatableContract;

class User extends Model implements AuthenticatableContract

{
    use Authenticatable;

    /**
    * The database table used by the model.
    *
    * @var string
    */
    protected $table = 'users';

    /**
    * The attributes that are mass assignable.
    *
    * @var array
    */
    protected $fillable = ['name', 'email', 'password'];

    /**
    * The attributes excluded from the model's JSON form.
    *
    * @var array
    */
    protected $hidden = ['password', 'remember_token'];
}

فلنعرّج قليلا على الأصناف الجديدة علينا:

use Illuminate\Contracts\Auth\Authenticatable as AuthenticatableContract;

توجد في Laravel أصناف يطلق عليها اسم العقود Contracts. العقود هي مجموعة واجهات Interfaces تعرّف خدمات يوفّرها إطار العمل. تعرّف العقود دوال يحتاجها إطار العمل لتأدية الخدمة. يستورد السطر أعلاه العقد Authenticatable وهي واجهة تعرّف الدوال الضرورية لاستيثاق المستخدمين. عند استيراد الواجهة أعطيناها الاسم AuthenticatableContract حتى لا تختلط مع السمة Trait الذي يحمل نفس الاسم (نستعمله في نموذج المستخدم كما سنرى لاحقا).

يجب على الكائن الذي يستوثق من المستخدمين (نموذج المستخدم في حالتنا) أن ينجز Implement هذه الواجهة:

class User extends Model implements AuthenticatableContract

لإنجاز دوال الواجهة (تعرّف واجهة Authenticatable الدوال دون أن تنجزها) نستخدم السمة Illuminate\Auth\Authenticatable.

نستورده أولا:

use Illuminate\Auth\Authenticatable;

ثم نستخدمه:

use Authenticatable;
  • يحدّد النموذج جدول البيانات المستخدم لتخزين بيانات المسجَّلين، ويفعّل الإسناد الشامل لبعض الحقول.
  • تعيّن مصفوفة hidden$ بيانات المستخدم التي لا نودّ عرضها عند الإجابة على طلب عبر واجهة تطبيقات برمجية API (يجب إخفاء كلمة سر المستخدم password ورمز الأمان remember_token عن الخارج).

يأتي مع Laravel مبدئيا ملف تهجير لإنشاء جدول المستخدمين. افتح ملف التهجير 2014_10_12_000000_create_users_table.php (قد يختلف الختم الزمني في اسم الملف حسب إصدار Laravel):

<?php

use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;

class CreateUsersTable extends Migration
{
    /**
    * Run the migrations.
    *
    * @return void
    */
    public function up()
    {
        Schema::create('users', function (Blueprint $table) {
            $table->increments('id');
            $table->string('name');
            $table->string('email')->unique();
            $table->string('password', 60);
            $table->rememberToken();
            $table->timestamps();
        });
    }

    /**
    * Reverse the migrations.
    *
    * @return void
    */
    public function down()
    {
        Schema::drop('users');
    }
}

يعرّف ملف التهجير حقول جدول المستخدمين. تعيّن الدالة rememberToken رمز حماية لتأمين المستخدمين ضدّ هجمات تزوير الطلب عبر الموقع CSRF.

استمارات التسجيل والولوج Login

يوجد في عرض الدخول login.blade.php استمارتان، واحدة لتسجيل المستخدمين Registration والأخرى لدخولهم Login. افتح ملف العرض login وحدّثه على النحو التالي:

@extends('layouts.layout')

@section('content')       
    <section id="form"><!--form-->
            <div class="container">
                <div class="row">
                    <div class="col-sm-4 col-sm-offset-1">
                        <div class="login-form"><!--login form-->
                            <h2>Login to your account</h2>
                            <form method="POST" action="{{url('auth/login')}}">
                                {!! csrf_field() !!}
                                <input type="email" name="email" id="email" placeholder="Email Address" />
                                <input type="password" name="password" id="password" placeholder="Password" />
                                <span>
                                    <input name="remember" id="remember" type="checkbox" class="checkbox"> 
                                    Keep me signed in
                                </span>
                                <button type="submit" class="btn btn-default">Login</button>
                            </form>
                        </div><!--/login form-->
                    </div>
                    <div class="col-sm-1">
                        <h2 class="or">OR</h2>
                    </div>
                    <div class="col-sm-4">
                        <div class="signup-form"><!--sign up form-->
                            <h2>New User Signup!</h2>
                            <form method="POST" action="{{url('register')}}">
                                {!! csrf_field() !!}
                                <input type="text" name="name" id="name"  placeholder="Name">
                                <input type="email" name="email" placeholder="Email Address"/>
                                <input type="password" name="password" placeholder="Password">
                                <button type="submit" class="btn btn-default">Signup</button>
                            </form>
                        </div><!--/sign up form-->
                    </div>
                </div>
            </div>
        </section><!--/form-->
@endsection
التغييرات الأساسية هنا هي:
  • تعريف رابط مناسب لاستمارتي التسجيل والدخول مع تحديد أن البيانات تُرسَل بإجراء POST:
// دخول المستخدمين

<form method="POST" action="{{url('auth/login')}}"> 

// تسجيل المستخدمين 
<form method="POST" action="{{url('register')}}">

تُرسل بيانات الدخول إلى الرابط larashop.dev/auth/login وبيانات التسجيل إلى الرابط larashop.dev/register.

  • تضيف التعليمة ()csrf_field حقلا أمنيا في الاستمارتين للحيلولة دون هجمات CSRF.

مسارات الدخول، الخروج والتسجيل

نضيف الآن المسارات الخاصة بالاستيثاق. افتح ملف المسارات routes.php وأضف المسارات التالية:

// مسارات الدخول والخروج
Route::get('auth/login', 'Front@login');
Route::post('auth/login', 'Front@authenticate');
Route::get('auth/logout', 'Front@logout');

// مسار التسجيل
Route::post('/register', 'Front@register');

نعرّف المسار الذي يعرض استمارتي التسجيل والدخول:

Route::get('auth/login', 'Front@login');

نعرّف إجراء HTTP POST الذي يوثّق المستخدمين:

Route::post('auth/login', 'Front@authenticate');

نعرّف مسار خروج المستخدم:

Route::get('auth/logout', 'Front@logout');

نعرف مسار تسجيل المستخدمين الجدد (إجراء HTTP POST):

Route::post('/register', 'Front@register'); 

المسارات المَحمِيّة

مسار محمي هو مسار يطلُب من الزائر الدخول قبل الوصول إليه. سنحمي في هذه الفقرة الرابط http://larashop.dev/checkout. يعني هذا أن المستخدمين المسجّلين فقط يمكنهم رؤية صفحة الدفع.

عدّل مسار checkout/ في ملف المسارات ليصبح على النحو التالي:

Route::get('/checkout', [
    'middleware' => 'auth',
    'uses' => 'Front@checkout'
]);

تُنفّذ التعليمة

'middleware' => 'auth', 

قبل دالة Front@checkout؛ وتتحقق من دخول الزائر. فإن لم يكن سجّل دخوله توجّهه إلى الرابط auth/login/، وتعرض له صفحة الدفع بعد الدخول.

دوال التسجيل والاستيثاق

نعدّل ملف المتحكّم لإضافة دوال تجيب على الطلبات القادمة من المسارات السابقة. افتح ملف المتحكم Front.php لتعديله.

نبدأ باستيراد فضاءات الأسماء التي نحتاجها:

use App\User;
use Illuminate\Support\Facades\Auth;

استوردنا نموذج المستخدم User وفضاء الأسماء الخاص بالاستيثاق Auth. توجد في فضاء الأسماء Auth الدوال والكائنات التي نحتاجها للاستيثاق من المستخدمين.

تسجيل مستخدم جديد

public function register() {
    if (Request::isMethod('post')) {
        User::create([
                    'name' => Request::get('name'),
                    'email' => Request::get('email'),
                    'password' => bcrypt(Request::get('password')),
        ]);
    } 

    return Redirect::away('login');
}

تستخدم دالة التسجيل المعلومات المذكورة في استمارة التسجيل New User Signup! لإنشاء مستخدم جديد عبر طلب الدالة User::create (تعرّفنا على هذه الدالة خلال درس Eloquent ORM). ثم نوجّه الزائر بعد إلى صفحة الدخول. لاحظ استخدام الدالة bcrypt أثناء تسجيل المستخدم. تعمّي هذه الدالة كلمة السر قبل تخزينها في جدول البيانات.

الاستيثاق من المستخدمين

تستوثق الدالة التالية من المستخدمين:

public function authenticate() {
    if (Auth::attempt(['email' => Request::get('email'), 'password' => Request::get('password')])) {
        return redirect()->intended('checkout');
    } else {
        return view('login', array('title' => 'Welcome', 'description' => '', 'page' => 'home'));
    }
}

ستوثق الدالة Auth::attempt من المستخدم بناء على عنوان البريد email وكلمة السر password الذين أرسلتهما استمارة الدخول. في حال نجاح دخول المستخدم يُعاد توجيهه إلى صفحة الدفع checkout عبر الدالة ()redirect.

تسجيل خروج المستخدمين

يؤدي طلب الدالة Auth::logout إلى تسجيل خروج المستخدمين بعد دخولهم عبر الدالة السابقة. عدّل دالة logout الموجودة في ملف المتحكم لتصبح كالتالي:

public function logout() {
    Auth::logout();

    return Redirect::away('auth/login');
}

تخرج الدالة المستخدم وتوجهه إلى صفحة الدخول.

إظهار بيانات الدخول في العرض

نحتاج، قبل اختبار التسجيل والدخول، إجراء تغيير أخير. تُظهر الصورة التالية الشريط العلوي الأيمن من الموقع:

01_before_login.png

نريد أن يظهر الشريط على النحو التالي بعد دخول المستخدم:

02_after_login.png

نريد أن يظهر اسم المستخدم بعد الدخول، وإبدال رابط Login بـLogout.

افتح ملف العرض layout.blade.php للتعديل عليه. تذكر أن هذا هو العرض الرئيس الذي تمدّده بقية العروض. نغيّر جزئية الترويسة (header) من ملف العرض لتصبح كالتالي:

<header id="header"><!--header-->
        <div class="header_top"><!--header_top-->
            <div class="container">
                <div class="row">
                    <div class="col-sm-6">
                        <div class="contactinfo">
                            <ul class="nav nav-pills">
                                <li><a href="#"><i class="fa fa-phone"></i> +2 95 01 88 821</a></li>
                                <li><a href="#"><i class="fa fa-envelope"></i> info@domain.com</a></li>
                            </ul>
                        </div>
                    </div>
                    <div class="col-sm-6">
                        <div class="social-icons pull-right">
                            <ul class="nav navbar-nav">
                                <li><a href="#"><i class="fa fa-facebook"></i></a></li>
                                <li><a href="#"><i class="fa fa-twitter"></i></a></li>
                                <li><a href="#"><i class="fa fa-linkedin"></i></a></li>
                                <li><a href="#"><i class="fa fa-dribbble"></i></a></li>
                                <li><a href="#"><i class="fa fa-google-plus"></i></a></li>
                            </ul>
                        </div>
                    </div>
                </div>
            </div>
        </div><!--/header_top-->

        <div class="header-middle"><!--header-middle-->
            <div class="container">
                <div class="row">
                    <div class="col-sm-4">
                        <div class="logo pull-left">
                            <a href="{{url('')}}"><img src="{{asset('images/home/logo.png')}}" alt="" /></a>
                        </div>
                    </div>
                    <div class="col-sm-8">
                        <div class="shop-menu pull-right">
                            <ul class="nav navbar-nav">
                                <li><a href="#"><i class="fa fa-user"></i> {{Auth::check() ? Auth::user()->name : 'Account'}}</a></li>
                                <li><a href="{{url('checkout')}}"><i class="fa fa-crosshairs"></i> Checkout</a></li>
                                <li><a href="{{url('cart')}}"><i class="fa fa-shopping-cart"></i> Cart</a></li>
                                <li><a href="{{Auth::check() ? url('auth/logout') : url('auth/login')}}"><i class="fa fa-lock"></i> {{Auth::check() ? 'Logout' : 'Login'}}</a></li>
                            </ul>
                        </div>
                    </div>
                </div>
            </div>
        </div><!--/header-middle-->

        <div class="header-bottom"><!--header-bottom-->
            <div class="container">
                <div class="row">
                    <div class="col-sm-9">
                        <div class="navbar-header">
                            <button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse">
                                <span class="sr-only">Toggle navigation</span>
                                <span class="icon-bar"></span>
                                <span class="icon-bar"></span>
                                <span class="icon-bar"></span>
                            </button>
                        </div>
                        <div class="mainmenu pull-left">
                            <ul class="nav navbar-nav collapse navbar-collapse">
                                <li><a href="{{url('')}}" {{$page == 'home' ? 'class=active' : ''}}>Home</a></li>
                                <li><a href="{{url('products')}}" {{$page == 'products' ? 'class=active' : ''}}>Products</a></li>
                                <li><a href="{{url('blog')}}" {{$page == 'blog' ? 'class=active' : ''}}>Blog</a></li>
                                <li><a href="{{url('contact-us')}}" {{$page == 'contact_us' ? 'class=active' : ''}}>Contact Us</a></li>
                            </ul>
                        </div>
                    </div>
                    <div class="col-sm-3">
                        <div class="search_box pull-right">
                            <input type="text" placeholder="Search"/>
                        </div>
                    </div>
                </div>
            </div>
        </div><!--/header-bottom-->
    </header><!--/header-->

أضفنا التغييرين التاليين على ترويسة العرض:

  1. إبدال Account بالتعليمة:

    {{Auth::check() ? Auth::user()->name : ‘Account’}}

    نستعمل الدالة Auth::check للتحقق من دخول المستخدم؛ في حال الإيجاب نظهر اسمه بالدالة Auth::user وإلا نبقي على عبارة Account.

  2. نفس المبدأ مع عبارة Login التي تصبح بعد الدخول Logout، وتغيير الرابط بما يُناسب (larashop.dev/auth/login للدخول وlarashop.dev/logout للخروج).

افتح الرابط http://larashop.dev/auth/login وسجل مستخدما جديد عبر الاستمارة New User Signup.

03_register_user.png

بالضغط على زر Signup تستدعي المسار http://laravel.dev/register مع الإجراء POST. تتولى دالة register في المتحكم تلقي الطلب واستدعاء النموذج الذي ينشئ تسجيلة جديدة في جدول البيانات ثم تعيد الدالة توجيه المستخدم إلى صفحة الدخول. يمكنك التأكد من إنشاء المستخدم في الجدول users ضمن قاعدة البيانات.

نجرّب الدخول بالمستخدم الجديد بإدخال عنوانه البريدي وكلمة السر في استمارة التسجيل.

04_login_user.png

إن أدخل بيانات مستخدم صالحة فستُنقل إلى صفحة الدفع. لاحظ أن اسم المستخدم وعبارة Logout يظهران في الشريط العلوي.

02_after_login.png

ترجمة -وبتصرّف- للمقال Laravel 5 Authentication لصاحبه Rodrick Kazembe.



1 شخص أعجب بهذا


تفاعل الأعضاء


لا توجد أيّة تعليقات بعد



يجب أن تكون عضوًا لدينا لتتمكّن من التعليق

انشاء حساب جديد

يستغرق التسجيل بضع ثوان فقط


سجّل حسابًا جديدًا

تسجيل الدخول

تملك حسابا مسجّلا بالفعل؟


سجّل دخولك الآن