30 نوفمبر 2025·4 دقيقة قراءة

حراس توجيه Vue 3 للوصول حسب الأدوار: أنماط عملية

شرح حراس التوجيه في Vue 3 للوصول بناءً على الأدوار بأساليب عملية: قواعد meta في المسارات، إعادة توجيه آمنة، صفحات بديلة ودية 401/403، وتجنّب تسريب البيانات.

حراس توجيه Vue 3 للوصول حسب الأدوار: أنماط عملية

ما الذي تحله حراس المسار فعلاً (وماذا لا تفعل)\n\nحراس المسار تقوم بمهمة واحدة بشكل جيد: التحكم في التنقل. هم يقررون ما إذا كان يمكن لشخص ما دخول مسار، وأين ترسله إذا لم يستطع. هذا يحسن تجربة المستخدم، لكنه ليس بديلاً عن الأمان.\n\nإخفاء عنصر في القائمة هو مجرد تلميح، وليس تفويضاً. لا يزال بإمكان الأشخاص كتابة عنوان URL، أو تحديث صفحة عميقة، أو فتح إشارة مرجعية. إذا كان الحماية الوحيدة لديك هي "الزر غير مرئي"، فلا حماية حقيقية لديك.\n\nتلمع الحراس عندما تريد من التطبيق أن يتصرف بشكل متسق أثناء حظر صفحات لا ينبغي عرضها، مثل مناطق المشرف، الأدوات الداخلية، أو بوابات العملاء القائمة على الأدوار.\n\nتساعدك الحراس على:\n\n- حظر الصفحات قبل عرضها\n- إعادة التوجيه إلى تسجيل الدخول أو لقيمة افتراضية آمنة\n- إظهار شاشة 401/403 واضحة بدلًا من عرض مكسور\n- تجنب حلقات التنقل العرضية\n\nما لا تستطيع الحراس فعله بمفردها هو حماية البيانات. إذا أعاد API بيانات حساسة إلى المتصفح، فلا يزال المستخدم قادراً على استدعاء تلك النقطة النهائية مباشرة (أو تفحص الاستجابات في أدوات المطور). التفويض الحقيقي يجب أن يحدث على الخادم أيضاً.\n\nالهدف الجيد هو تغطية الجانبَين: حظر الصفحات وحظر البيانات. إذا فتح وكيل دعم مسارًا خاصًا بالمسؤول فقط، يجب أن يمنع الحارس التنقل ويعرض "تم رفض الوصول". وبشكل منفصل، يجب على الواجهة الخلفية رفض نداءات API الخاصة بالمسؤولين فقط، حتى لا يتم إرجاع بيانات مقيدة.\n\n## اختر نموذج أدوار وصلاحيات بسيط\n\nيتعقد التحكم في الوصول عندما تبدأ بقائمة طويلة من الأدوار. ابدأ بمجموعة صغيرة يفهمها الناس فعلاً، ثم أضف صلاحيات أدق فقط عندما تشعر بألم حقيقي.\n\nتقسيم عملي هو:\n\n- الأدوار تصف من هو الشخص في تطبيقك.\n- الصلاحيات تصف ما يمكنه فعله.\n\nبالنسبة لمعظم الأدوات الداخلية، ثلاثة أدوار تغطي الكثير:\n\n- admin: إدارة المستخدمين والإعدادات، رؤية كل البيانات\n- support: التعامل مع سجلات العملاء والردود، لكن ليس إعدادات النظام\n- viewer: وصول للقراءة فقط إلى الشاشات المصرح بها\n\nقرر مبكراً من أين تأتي الأدوار. مطالبات التوكن (JWT) سريعة للحراس لكنها قد تصبح قديمة حتى يتم تحديثها. جلب ملف تعريف المستخدم عند بدء التطبيق دائمًا ما يكون حديثاً، لكن يجب أن تنتظر الحراس حتى ينتهي ذلك الطلب.\n\nافصل أيضاً أنواع المسارات بوضوح: مسارات عامة (مفتوحة للجميع)، مسارات تتطلب جلسة (authenticated)، ومسارات مقيدة (تتطلب دورًا أو صلاحية).\n\n## عرّف قواعد الوصول باستخدام meta في المسار\n\nأوضح طريقة للتعبير عن الوصول هي إعلانها على المسار نفسه. يسمح لك Vue Router بإرفاق كائن meta لكل سجل مسار حتى يتمكن الحراس من قراءته لاحقاً. هذا يبقي القواعد قريبة من الصفحات التي تحميها.\n\nاختر شكل meta بسيط والتزم به عبر التطبيق.\n\njs\nconst routes = [\n {\n path: \"/admin\",\n component: () =\u003e import(\"@/pages/AdminLayout.vue\"),\n meta: { requiresAuth: true, roles: [\"admin\"] },\n children: [\n {\n path: \"users\",\n component: () =\u003e import(\"@/pages/AdminUsers.vue\"),\n // inherits requiresAuth + roles from parent\n },\n {\n path: \"audit\",\n component: () =\u003e import(\"@/pages/AdminAudit.vue\"),\n meta: { permissions: [\"audit:read\"] },\n },\n ],\n },\n {\n path: \"/tickets\",\n component: () =\u003e import(\"@/pages/Tickets.vue\"),\n meta: { requiresAuth: true, permissions: [\"tickets:read\"], readOnly: true },\n },\n]\n\n\nبالنسبة للمسارات المتداخلة، قرر كيف تُدمَج القواعد. في معظم التطبيقات، يجب أن ترث الأطفال متطلبات الأب. في حارسك، افحص كل سجل مسار مطابق (وليس فقط to.meta) حتى لا تُهمل متطلبات الأب.\n\nتفصيل واحد يوفر الوقت لاحقاً: ميز بين "يمكن العرض" و"يمكن التحرير". قد تكون الصفحة مرئية للـ support و admin، لكن يجب تعطيل التحرير للدعم. علامة readOnly: true في meta يمكن أن تُوجّه سلوك الواجهة (تعطيل إجراءات، إخفاء أزرار تدميرية) دون التظاهر بأنها أمان.\n\n## جهّز حالة المصادقة لكي تعمل الحراس بأمان\n\nمعظم أخطاء الحراس تأتي من مشكلة واحدة: يعمل الحارس قبل أن يعرف التطبيق من هو المستخدم.\n\nعامل المصادقة مثل آلة حالات صغيرة واجعلها مصدر الحقيقة الوحيد. تريد ثلاث حالات واضحة:\n\n- unknown: التطبيق بدأ للتو، لم يتم التحقق من الجلسة بعد\n- logged out: فحص الجلسة انتهى، لا يوجد مستخدم صالح\n- logged in: تم تحميل المستخدم، الأدوار/الصلاحيات متاحة\n\nالقاعدة: لا تقرأ الأدوار بينما المصادقة هي unknown. هذا هو سبب ظهور لمحات من الشاشات المحمية أو إعادة توجيهات مفاجئة إلى تسجيل الدخول.\n\n### قرر كيف يعمل تحديث الجلسة\n\nاختر استراتيجية تحديث واحدة وحافظ على توقعها (مثلاً: قراءة توكن، استدعاء نقطة نهاية "من أنا"، تعيين المستخدم).\n\nنمط مستقر يبدو هكذا:\n\n- عند تحميل التطبيق، ضع auth في حالة unknown وابدأ طلب تحديث واحد\n- حل الحراس فقط بعد انتهاء التحديث (أو انتهاء المهلة)\n- خزّن المستخدم في الذاكرة، لا في route meta\n- عند الفشل، ضع auth في حالة logged out\n- اكشف عن وعد ready (أو ما شابه) يمكن للحراس الانتظار عليه\n\nبمجرد وجود هذا، تبقى منطق الحارس بسيطاً: انتظر حتى تصبح auth جاهزة، ثم قرر الوصول.\n\n## خطوة بخطوة: تنفيذ التفويض على مستوى المسار\n\nنهج نظيف هو إبقاء معظم القواعد في حارس عام واحد، واستخدام حراس لكل مسار فقط عندما يحتاج المسار فعلاً إلى منطق خاص.\n\n### 1) أضف حارس beforeEach عام\n\njs\n// router/index.js\nrouter.beforeEach(async (to) =\u003e {\n const auth = useAuthStore()\n\n // Step 2: wait for auth initialization when needed\n if (!auth.ready) await auth.init()\n\n // Step 3: check authentication, then roles/permissions\n if (to.meta.requiresAuth \u0026\u0026 !auth.isAuthenticated) {\n return { name: 'login', query: { redirect: to.fullPath } }\n }\n\n const roles = to.meta.roles\n if (roles \u0026\u0026 roles.length \u003e 0 \u0026\u0026 !roles.includes(auth.userRole)) {\n return { name: 'forbidden' } // 403\n }\n\n // Step 4: allow navigation\n return true\n})\n\n\nهذا يغطي معظم الحالات دون نشر الفحوصات عبر المكونات.\n\n### متى يكون beforeEnter الأنسب\n\nاستخدم beforeEnter عندما تكون القاعدة خاصة فعلاً بالمسار، مثل "فقط صاحب التذكرة يمكنه فتح هذه الصفحة" وتعتمد على to.params.id. اجعلها قصيرة وأعد استخدام نفس مخزن المصادقة حتى يبقى السلوك متسقاً.\n\n## إعادة توجيهات آمنة دون فتح ثغرات\n\nيمكن أن تقوّض عمليات إعادة التوجيه ضوابط الوصول بهدوء إذا عاملتها كمُدخل موثوق.\n\nالنمط الشائع هو: عندما يكون المستخدم غير مسجل الدخول، أرسله إلى تسجيل الدخول وضع استعلام returnTo. بعد تسجيل الدخول، اقرأه وتنقل هناك. الخطر هو إعادة التوجيه المفتوحة (إرسال المستخدمين إلى مكان غير مقصود) والحلقات.\n\nحافظ على سلوك بسيط:\n\n- المستخدمون غير المسجلين يذهبون إلى Login مع returnTo مضبوط على المسار الحالي.\n- المستخدمون المسجلون لكن غير المصرح لهم يذهبون إلى صفحة Forbidden مخصصة (وليس Login).\n- فقط اسمح بقيم returnTo داخلية تعرفها.\n- أضف فحص حلقة واحد حتى لا تعيد التوجيه إلى نفس المكان.\n\njs\nconst allowedReturnTo = (to) =\u003e {\n if (!to || typeof to !== 'string') return null\n if (!to.startsWith('/')) return null\n // optional: only allow known prefixes\n if (!['/app', '/admin', '/tickets'].some(p =\u003e to.startsWith(p))) return null\n return to\n}\n\nrouter.beforeEach((to) =\u003e {\n if (!auth.isReady) return false\n\n if (!auth.isLoggedIn \u0026\u0026 to.name !== 'Login') {\n return { name: 'Login', query: { returnTo: to.fullPath } }\n }\n\n if (auth.isLoggedIn \u0026\u0026 !canAccess(to, auth.user) \u0026\u0026 to.name !== 'Forbidden') {\n return { name: 'Forbidden' }\n }\n})\n\n\n## تجنب تسريب بيانات مقيدة أثناء التنقل\n\nأسهل تسريب هو تحميل البيانات قبل أن تعرف ما إذا كان المستخدم مسموحًا له برؤيتها.\n\nفي Vue، يحدث هذا غالبًا عندما تجلب الصفحة بيانات في setup() ويعمل حارس الراوتر بعد لحظة. حتى لو تم إعادة توجيه المستخدم، قد تصل الاستجابة إلى مخزن مشترك أو تومض على الشاشة.\n\nقاعدة أكثر أمانًا: فوض أولاً، ثم احمِ البيانات.\n\njs\n// router guard: authorize before entering the route\nrouter.beforeEach(async (to) =\u003e {\n await auth.ready() // ensure roles are known\n const required = to.meta.requiredRole\n if (required \u0026\u0026 !auth.hasRole(required)) {\n return { name: 'forbidden' }\n }\n})\n\n\nراقب أيضاً الطلبات المتأخرة عندما يتغير التنقل بسرعة. ألغِ الطلبات (مثلاً باستخدام AbortController) أو تجاهل الاستجابات المتأخرة بفحص معرف الطلب.\n\nالتخزين المؤقت (caching) فخ آخر شائع. إذا خزّنت "آخر سجل عميل مُحمّل" عالمياً، فقد تُعرض استجابة خاصة بالمشرف لاحقًا لمستخدم غير مشرف يزور نفس هيكل الشاشة. قم بتمييز الكاش حسب معرف المستخدم والدور، وامسح الوحدات الحساسة عند تسجيل الخروج (أو عند تغيير الأدوار).\n\nبعض العادات تمنع معظم التسريبات:\n\n- لا تجلب بيانات حساسة حتى يؤكد التفويض.\n- مفتاح البيانات المخزنة ذاياً بالمستخدم والدور، أو احتفظ بها محلياً في الصفحة.\n- ألغِ أو تجاهل الطلبات الجارية عند تغيير المسار.\n\n## بدائل ودية: 401 و403 و404\n\nمسارات الـ "لا" لا تقل أهمية عن مسارات الـ "نعم". صفحات البدائل الجيدة تبقي المستخدمين موجهين وتقلل طلبات الدعم.\n\n### 401: تسجيل الدخول مطلوب (غير مصدق)\n\nاستخدم 401 عندما لا يكون المستخدم مسجلاً. اجعل الرسالة بسيطة: يحتاج لتسجيل الدخول للمتابعة. إذا تدعم العودة إلى الصفحة الأصلية بعد تسجيل الدخول، فتأكد من التحقق من مسار العودة حتى لا يشير خارج تطبيقك.\n\n### 403: مرفوض الوصول (مصدق لكن غير مصرح)\n\nاستخدم 403 عندما يكون المستخدم مسجلاً لكنه يفتقر للصلاحية. اجعل الرسالة محايدة وتجنب تلميح تفاصيل حساسة.\n\nصفحة 403 جيدة عادةً تحتوي على عنوان واضح ("تم رفض الوصول"), جملة شرح واحدة، وخطوة آمنة تالية (العودة إلى لوحة التحكم، الاتصال بمشرف، أو تغيير الحساب إن وُجد).\n\n### 404: غير موجود\n\nتعامل مع 404 بشكل منفصل عن 401/403. وإلا سيفترض الناس أنهم يفتقرون إلى الأذن بينما الصفحة ببساطة غير موجودة.\n\n## أخطاء شائعة تكسر ضوابط الوصول\n\nمعظم أخطاء التحكم في الوصول هي أخطاء منطق بسيطة تظهر كحلقات إعادة توجيه، لمحات من الصفحة الخاطئة، أو وقف المستخدمين.\n\nالمذنبات المعتادة:\n\n- اعتبار UI المخفي كـ"أمان". نفِّذ دائمًا الأدوار في الراوتر وعلى الـ API.\n- قراءة الأدوار من حالة قديمة بعد تسجيل الخروج/الدخول.\n- إعادة توجيه المستخدمين غير المصرح لهم إلى مسار محمي آخر (حلقة فورية).\n- تجاهل لحظة "المصادقة لا تزال جارِ تحميلها" عند التحديث.\n- الخلط بين 401 و403، مما يربك المستخدمين.\n\nمثال واقعي: وكيل دعم يقوم بتسجيل الخروج ومسؤول يسجل الدخول على نفس الحاسوب المشترك. إذا قرأ الحارس دورًا مخزنًا مؤقتًا قبل تأكيد الجلسة الجديدة، يمكنك حظر المسؤول بطريق الخطأ أو، والأسوأ، السماح بالوصول لفترة وجيزة لا يجب أن يسمح بها.\n\n## قائمة فحص سريعة قبل الإطلاق\n\nقم بمرور قصير يركز على اللحظات التي عادةً ما تنهار فيها ضوابط الوصول: الشبكات البطيئة، الجلسات منتهية الصلاحية، وعناوين URL المحفوظة.\n\n- لكل مسار محمي يوجد متطلبات meta صريحة.\n- الحراس يتعاملون مع حالة تحميل المصادقة دون إظهار واجهات محمية مؤقتة.\n- المستخدمون غير المصرح لهم يصلون إلى صفحة 403 واضحة (وليس ارتداد محير إلى الصفحة الرئيسية).\n- أي إعادة توجيه "العودة إلى" تم التحقق منها ولا يمكنها خلق حلقات.\n- نداءات API الحساسة تعمل فقط بعد تأكيد التفويض.\n\nثم اختبر سيناريو واحد من البداية للنهاية: افتح عنوان URL محمي في تبويب جديد أثناء تسجيل الخروج، سجل الدخول كمستخدم عادي، وتأكد إما أن تصل إلى الصفحة المقصودة (إن كانت مصرح بها) أو إلى 403 نظيفة مع خطوة تالية.\n\n## مثال: وصول الدعم مقابل المشرف في تطبيق ويب صغير\n\nتخيل تطبيق دعم فني بدورين: support و admin. يمكن للدعم قراءة والرد على التذاكر. المشرف يمكنه ذلك أيضاً، بالإضافة إلى إدارة الفوترة وإعدادات الشركة.\n\n- /tickets/:id مسموح لـ support و admin\n- /settings/billing مسموح فقط لـ admin\n\nلحظة شائعة: يفتح وكيل دعم رابطًا عميقًا إلى /settings/billing من إشارة مرجعية قديمة. يجب على الحارس فحص meta قبل تحميل الصفحة وحظر التنقل. لأن المستخدم مسجل ولكن يفتقر للدور، يجب أن يصل إلى بديل آمن (403).\n\nرسالتان مهمتان:\n\n- تسجيل الدخول مطلوب (401): «يرجى تسجيل الدخول للمتابعة.»\n- تم رفض الوصول (403): «ليس لديك صلاحية الوصول إلى إعدادات الفوترة.»\n\nما يجب ألا يحدث: مكون الفوترة يركب، أو تُجلب بيانات الفوترة، ولو لحظة وجيزة.\n\nتغيير الدور في منتصف الجلسة هو حالة حافة أخرى. إذا تمت ترقية شخص أو خفضه، لا تعتمد على القائمة. أعد فحص الأدوار عند التنقل وقرر كيف ستتعامل مع الصفحات النشطة: حدّث حالة المصادقة عندما يتغير الملف الشخصي، أو اكتشف تغيّر الأدوار وأعِد التوجيه بعيدًا عن الصفحات التي لم تعد مسموحًا بها.\n\n## الخطوات التالية: حافظ على قواعد الوصول قابلة للصيانة\n\nبمجرد أن تعمل الحراس، الخطر الأكبر هو الانجراف: مسار جديد يُنشر بدون meta، دور يُعاد تسميته، وتصبح القواعد غير متسقة.\n\nحوّل قواعدك إلى خطة اختبار صغيرة يمكنك تشغيلها عند إضافة مسار:\n\n- كزائر: افتح المسارات المحمية وتأكد من وصولك إلى تسجيل الدخول دون رؤية محتوى جزئي.\n- كمستخدم: افتح صفحة لا يجب أن تصل إليها وتأكد من حصولك على 403 واضحة.\n- كمشرف: جرب الروابط العميقة المنسوخة من شريط العنوان.\n- لكل دور: حدث الصفحة على مسار محمي وتأكد من استقرار النتيجة.\n\nإذا أردت طبقة أمان إضافية، أضف عرضًا اختباريًا في بيئة التطوير أو إخراجًا في وحدة التحكم يسرد المسارات ومتطلبات meta الخاصة بها، حتى تبرز القواعد المفقودة فوراً.\n\nإذا كنت تبني أدوات داخلية أو بوابات باستخدام AppMaster (appmaster.io)، يمكنك تطبيق نفس النهج: اجعل حراس المسار مركّزة على التنقل في واجهة Vue3، ونفّذ الصلاحيات حيث تعيش منطق البيانات والواجهة الخلفية.\n\nاختر تحسناً واحداً وطبّقه من البداية للنهاية: تشديد بوابات جلب البيانات، تحسين صفحة 403، أو تشديد منطق إعادة التوجيه. الإصلاحات الصغيرة هي التي توقف معظم أخطاء التحكم في العالم الحقيقي.

الأسئلة الشائعة

هل حراس المسار أمان فعلي أم مجرد تجربة للمستخدم؟

Route guards control navigation, not data access. They help you block a page, redirect, and show a clean 401/403 state, but they cannot stop someone from calling your API directly. Always enforce the same permissions on the backend so restricted data is never returned.

لماذا إخفاء عنصر في القائمة لا يكفي للوصول حسب الدور؟

Because hiding UI only changes what someone sees, not what they can request. Users can still type a URL, open bookmarks, or hit deep links. You need router checks to block the page, and server-side authorization to block the data.

ما نموذج الأدوار والصلاحيات البسيط الذي أبدأ به؟

Start with a small set that people understand, then add permissions when you feel real pain. A common baseline is admin, support, and viewer, then add permissions like tickets:read or audit:read for specific actions. Keep “who you are” (role) separate from “what you can do” (permission).

كيف أستخدم `meta` في Vue Router للتحكم في الوصول؟

Put access rules in meta on the route records, like requiresAuth, roles, and permissions. This keeps rules next to the pages they protect and makes your global guard predictable. For nested routes, check every matched record so parent requirements aren’t skipped.

كيف أتعامل مع المسارات المتداخلة بحيث يرث الأطفال قيود الآباء؟

Read from to.matched and combine requirements across all matched route records. That way, a child route can’t accidentally bypass a parent’s requiresAuth or roles. Decide a clear merge rule up front (usually: parent requirements apply to children).

كيف أوقف حلقات إعادة التوجيه وظهور صفحات محمية عند التحديث؟

Because the guard may run before the app knows who the user is. Treat auth as three states—unknown, logged out, logged in—and never evaluate roles while auth is unknown. Make guards wait for one initialization step (like a single “who am I” request) before deciding.

متى أستخدم global `beforeEach` مقابل `beforeEnter`؟

Default to a global beforeEach for consistent rules like “requires login” and “requires role/permission.” Use beforeEnter only when the rule is truly route-specific and depends on params, like “only the ticket owner can open this page.” Keep both paths using the same auth source of truth.

كيف أتعامل مع إعادة التوجيه بعد تسجيل الدخول دون فتح ثغرات لإعادة توجيه خارجية؟

Treat returnTo as untrusted input. Only allow internal paths you recognize (for example, values that start with / and match known prefixes), and add one loop check so you don’t redirect back to the same blocked route. Logged-out users go to Login; logged-in but unauthorized users go to a dedicated 403 page.

كيف أتجنب تسريب بيانات مقيدة أثناء التنقل؟

Authorize before fetching. If a page fetches data in setup() and you redirect a moment later, the response can still land in a store or briefly show. Gate sensitive requests behind confirmed authorization, and cancel or ignore in-flight requests on navigation changes.

ما الفرق الصحيح بين 401 و403 و404 في تطبيق Vue؟

Use 401 when the user is not signed in, and 403 when they are signed in but not allowed. Keep 404 separate so users don’t think they lack permission for a route that simply doesn’t exist. Clear, consistent fallbacks reduce confusion and support tickets.

من السهل أن تبدأ
أنشئ شيئًا رائعًا

تجربة مع AppMaster مع خطة مجانية.
عندما تكون جاهزًا ، يمكنك اختيار الاشتراك المناسب.

البدء
حراس توجيه Vue 3 للوصول حسب الأدوار: أنماط عملية | AppMaster