Laravel المتقدم

المصادقة المتقدمة: الحراس المتعددة والدخول الموحد

20 دقيقة الدرس 7 من 40

المصادقة المتقدمة: الحراس المتعددة والدخول الموحد

نفذ أنظمة المصادقة المتقدمة بما في ذلك الحراس المتعددة ومزودي المستخدمين المخصصين والدخول الموحد (SSO) و OAuth2 ومصادقة API مع Laravel Passport و Sanctum.

فهم حراس المصادقة

تحدد الحراس كيفية مصادقة المستخدمين لكل طلب. يدعم Laravel حراس مصادقة متعددة في وقت واحد:

// config/auth.php return [ 'defaults' => [ 'guard' => 'web', 'passwords' => 'users', ], 'guards' => [ 'web' => [ 'driver' => 'session', 'provider' => 'users', ], 'api' => [ 'driver' => 'token', 'provider' => 'users', 'hash' => false, ], 'admin' => [ 'driver' => 'session', 'provider' => 'admins', ], 'customer' => [ 'driver' => 'session', 'provider' => 'customers', ], ], 'providers' => [ 'users' => [ 'driver' => 'eloquent', 'model' => App\Models\User::class, ], 'admins' => [ 'driver' => 'eloquent', 'model' => App\Models\Admin::class, ], 'customers' => [ 'driver' => 'eloquent', 'model' => App\Models\Customer::class, ], ], ];

استخدام الحراس المتعددة

الوصول إلى الحراس المختلفة في وحدات التحكم والوسيطة:

namespace App\Http\Controllers; class AuthController extends Controller { // تسجيل الدخول مع حارس محدد public function adminLogin(Request $request) { $credentials = $request->only('email', 'password'); if (Auth::guard('admin')->attempt($credentials)) { return redirect()->route('admin.dashboard'); } return back()->withErrors(['email' => 'بيانات اعتماد غير صحيحة']); } // التحقق من المصادقة لحارس محدد public function dashboard() { if (Auth::guard('admin')->check()) { $admin = Auth::guard('admin')->user(); return view('admin.dashboard', compact('admin')); } return redirect()->route('admin.login'); } // تسجيل الخروج من حارس محدد public function logout() { Auth::guard('admin')->logout(); return redirect()->route('admin.login'); } // فحص مصادقة الحراس المتعددة public function profile() { if (Auth::guard('admin')->check()) { $user = Auth::guard('admin')->user(); $userType = 'admin'; } elseif (Auth::guard('customer')->check()) { $user = Auth::guard('customer')->user(); $userType = 'customer'; } else { return redirect()->route('login'); } return view('profile', compact('user', 'userType')); } }
نصيحة احترافية: استخدم وسيطة المسار لحماية المسارات بحراس محددة: Route::middleware('auth:admin')->group(...)

مزودو المستخدمين المخصصين

إنشاء مزودي مصادقة مخصصة لمصادر المستخدمين غير القياسية:

// app/Providers/CustomUserProvider.php namespace App\Providers; use Illuminate\Contracts\Auth\UserProvider; use Illuminate\Contracts\Auth\Authenticatable; class LdapUserProvider implements UserProvider { protected $ldapConnection; public function __construct($ldapConfig) { $this->ldapConnection = ldap_connect($ldapConfig['host']); ldap_set_option($this->ldapConnection, LDAP_OPT_PROTOCOL_VERSION, 3); } public function retrieveById($identifier) { $filter = "(uid={$identifier})"; $result = ldap_search($this->ldapConnection, 'dc=example,dc=com', $filter); $entries = ldap_get_entries($this->ldapConnection, $result); if ($entries['count'] > 0) { return $this->createUserFromLdap($entries[0]); } return null; } public function retrieveByCredentials(array $credentials) { $username = $credentials['username']; $filter = "(uid={$username})"; $result = ldap_search($this->ldapConnection, 'dc=example,dc=com', $filter); $entries = ldap_get_entries($this->ldapConnection, $result); if ($entries['count'] > 0) { return $this->createUserFromLdap($entries[0]); } return null; } public function validateCredentials(Authenticatable $user, array $credentials) { $username = $credentials['username']; $password = $credentials['password']; $dn = "uid={$username},ou=users,dc=example,dc=com"; return @ldap_bind($this->ldapConnection, $dn, $password); } protected function createUserFromLdap($ldapUser) { return new \App\Models\LdapUser([ 'id' => $ldapUser['uid'][0], 'name' => $ldapUser['cn'][0], 'email' => $ldapUser['mail'][0], ]); } public function retrieveByToken($identifier, $token) { return null; } public function updateRememberToken(Authenticatable $user, $token) {} public function rehashPasswordIfRequired(Authenticatable $user, array $credentials, bool $force = false) {} }

تسجيل المزود المخصص

سجل المزود المخصص في AuthServiceProvider:

// app/Providers/AuthServiceProvider.php namespace App\Providers; use Illuminate\Support\Facades\Auth; use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider; class AuthServiceProvider extends ServiceProvider { public function boot() { $this->registerPolicies(); // تسجيل مزود LDAP المخصص Auth::provider('ldap', function ($app, array $config) { return new LdapUserProvider($config); }); } } // config/auth.php 'providers' => [ 'ldap_users' => [ 'driver' => 'ldap', 'host' => env('LDAP_HOST', 'ldap.example.com'), 'port' => env('LDAP_PORT', 389), ], ], 'guards' => [ 'ldap' => [ 'driver' => 'session', 'provider' => 'ldap_users', ], ],
ملاحظة: يجب أن تنفذ المزودات المخصصة واجهة Illuminate\Contracts\Auth\UserProvider. يجب أن ينفذ المستخدم المصادق عليه Illuminate\Contracts\Auth\Authenticatable.

الدخول الموحد (SSO) مع SAML

تنفيذ SSO المستند إلى SAML للمصادقة المؤسسية:

// composer require onelogin/php-saml // app/Http/Controllers/SamlController.php namespace App\Http\Controllers; use OneLogin\Saml2\Auth as Saml2Auth; class SamlController extends Controller { protected function getSamlAuth() { $settings = [ 'sp' => [ 'entityId' => config('saml.sp.entity_id'), 'assertionConsumerService' => [ 'url' => route('saml.acs'), ], 'singleLogoutService' => [ 'url' => route('saml.sls'), ], ], 'idp' => [ 'entityId' => config('saml.idp.entity_id'), 'singleSignOnService' => [ 'url' => config('saml.idp.sso_url'), ], 'x509cert' => config('saml.idp.x509cert'), ], ]; return new Saml2Auth($settings); } public function login() { $auth = $this->getSamlAuth(); $auth->login(route('dashboard')); } public function acs() { $auth = $this->getSamlAuth(); $auth->processResponse(); $errors = $auth->getErrors(); if (!empty($errors)) { return redirect()->route('login') ->withErrors(['saml' => implode(', ', $errors)]); } if (!$auth->isAuthenticated()) { return redirect()->route('login') ->withErrors(['saml' => 'غير مصادق عليه']); } $attributes = $auth->getAttributes(); $user = $this->findOrCreateUser($attributes); Auth::login($user); return redirect()->route('dashboard'); } protected function findOrCreateUser($attributes) { $email = $attributes['email'][0] ?? null; $user = User::where('email', $email)->first(); if (!$user) { $user = User::create([ 'name' => $attributes['name'][0] ?? '', 'email' => $email, 'saml_id' => $attributes['uid'][0] ?? null, ]); } return $user; } public function logout() { $auth = $this->getSamlAuth(); Auth::logout(); $auth->logout(route('login')); } }

OAuth2 مع Laravel Socialite

دمج مزودي تسجيل الدخول الاجتماعي باستخدام Socialite:

// composer require laravel/socialite // config/services.php return [ 'google' => [ 'client_id' => env('GOOGLE_CLIENT_ID'), 'client_secret' => env('GOOGLE_CLIENT_SECRET'), 'redirect' => env('GOOGLE_REDIRECT_URL'), ], 'github' => [ 'client_id' => env('GITHUB_CLIENT_ID'), 'client_secret' => env('GITHUB_CLIENT_SECRET'), 'redirect' => env('GITHUB_REDIRECT_URL'), ], ]; // app/Http/Controllers/SocialAuthController.php namespace App\Http\Controllers; use Laravel\Socialite\Facades\Socialite; class SocialAuthController extends Controller { public function redirectToProvider($provider) { return Socialite::driver($provider) ->scopes(['read:user', 'user:email']) ->redirect(); } public function handleProviderCallback($provider) { try { $socialUser = Socialite::driver($provider)->user(); } catch (\Exception $e) { return redirect()->route('login') ->withErrors(['oauth' => 'فشلت المصادقة']); } $user = $this->findOrCreateUser($socialUser, $provider); Auth::login($user, true); return redirect()->route('dashboard'); } protected function findOrCreateUser($socialUser, $provider) { // التحقق من وجود المستخدم بهذا الحساب الاجتماعي $account = SocialAccount::where('provider', $provider) ->where('provider_id', $socialUser->getId()) ->first(); if ($account) { return $account->user; } // التحقق من وجود المستخدم بهذا البريد الإلكتروني $user = User::where('email', $socialUser->getEmail())->first(); if (!$user) { $user = User::create([ 'name' => $socialUser->getName(), 'email' => $socialUser->getEmail(), 'avatar' => $socialUser->getAvatar(), 'email_verified_at' => now(), ]); } // إنشاء رابط الحساب الاجتماعي SocialAccount::create([ 'user_id' => $user->id, 'provider' => $provider, 'provider_id' => $socialUser->getId(), 'token' => $socialUser->token, 'refresh_token' => $socialUser->refreshToken, ]); return $user; } }
تحذير: تحقق دائماً من رموز OAuth وتعامل مع الحالات الحدية مثل تعارضات البريد الإلكتروني والرموز المسحوبة وتغييرات API المزود.

Laravel Passport لمصادقة API

تنفيذ خادم OAuth2 مع Laravel Passport:

// composer require laravel/passport // تشغيل الترحيلات والتثبيت php artisan migrate php artisan passport:install // app/Models/User.php use Laravel\Passport\HasApiTokens; class User extends Authenticatable { use HasApiTokens, Notifiable; } // app/Providers/AuthServiceProvider.php use Laravel\Passport\Passport; public function boot() { $this->registerPolicies(); // أعمار الرموز Passport::tokensExpireIn(now()->addDays(15)); Passport::refreshTokensExpireIn(now()->addDays(30)); Passport::personalAccessTokensExpireIn(now()->addMonths(6)); // تعريف النطاقات Passport::tokensCan([ 'read-posts' => 'قراءة المنشورات', 'create-posts' => 'إنشاء المنشورات', 'delete-posts' => 'حذف المنشورات', ]); Passport::setDefaultScope(['read-posts']); } // config/auth.php 'guards' => [ 'api' => [ 'driver' => 'passport', 'provider' => 'users', ], ], // حماية المسارات بالنطاقات Route::middleware(['auth:api', 'scopes:create-posts'])->group(function () { Route::post('/posts', [PostController::class, 'store']); }); // التحقق من النطاقات في وحدة التحكم public function store(Request $request) { if ($request->user()->tokenCan('create-posts')) { // إنشاء منشور } return response()->json(['error' => 'أذونات غير كافية'], 403); }

رموز منح كلمة المرور

إصدار الرموز عبر اسم المستخدم وكلمة المرور:

// app/Http/Controllers/Api/AuthController.php namespace App\Http\Controllers\Api; use Illuminate\Support\Facades\Http; class AuthController extends Controller { public function login(Request $request) { $request->validate([ 'email' => 'required|email', 'password' => 'required', ]); $response = Http::asForm()->post(config('app.url') . '/oauth/token', [ 'grant_type' => 'password', 'client_id' => config('passport.password_client_id'), 'client_secret' => config('passport.password_client_secret'), 'username' => $request->email, 'password' => $request->password, 'scope' => '*', ]); if ($response->failed()) { return response()->json([ 'message' => 'بيانات اعتماد غير صحيحة' ], 401); } return $response->json(); } public function refresh(Request $request) { $request->validate(['refresh_token' => 'required']); $response = Http::asForm()->post(config('app.url') . '/oauth/token', [ 'grant_type' => 'refresh_token', 'refresh_token' => $request->refresh_token, 'client_id' => config('passport.password_client_id'), 'client_secret' => config('passport.password_client_secret'), 'scope' => '*', ]); return $response->json(); } public function logout(Request $request) { $request->user()->token()->revoke(); return response()->json(['message' => 'تم تسجيل الخروج بنجاح']); } }
تمرين 1: نفذ تطبيق متعدد المستأجرين بمصادقة منفصلة لـ:
1. المسؤول الفائق (يدير جميع المستأجرين)
2. مسؤول المستأجر (يدير مؤسسته)
3. المستخدمين العاديين (الوصول إلى موارد مستأجرهم)
أنشئ ثلاثة حراس وثلاثة نماذج مستخدمين ووسيطة لفرض عزل المستأجرين. أضف صفحات تسجيل دخول لكل نوع مستخدم.
تمرين 2: ابن مزود مستخدم مخصص يصادق ضد قاعدة بيانات MongoDB. نفذ جميع الطرق المطلوبة وسجلها في AuthServiceProvider. أنشئ حارس باستخدام هذا المزود واختبر وظائف تسجيل الدخول/الخروج.
تمرين 3: ادمج تسجيل الدخول OAuth لـ Google و GitHub باستخدام Socialite. اسمح للمستخدمين بربط حسابات اجتماعية متعددة ببريد إلكتروني واحد. تعامل مع الحالات الحدية مثل تعارضات البريد الإلكتروني الموجودة والبريد الإلكتروني المفقود من المزود وإلغاء ربط الحساب. أضف صفحة ملف تعريف تعرض جميع الحسابات المرتبطة.