المصادقة في Vue.js
نشرت: 2022-03-10المصادقة هي ميزة ضرورية للغاية للتطبيقات التي تخزن بيانات المستخدم. إنها عملية للتحقق من هوية المستخدمين ، مما يضمن عدم تمكن المستخدمين غير المصرح لهم من الوصول إلى البيانات الخاصة - البيانات الخاصة بمستخدمين آخرين. هذا يؤدي إلى وجود مسارات مقيدة لا يمكن الوصول إليها إلا من قبل المستخدمين المصادق عليهم. يتم التحقق من هؤلاء المستخدمين المصادق عليهم باستخدام تفاصيل تسجيل الدخول الخاصة بهم (مثل اسم المستخدم / البريد الإلكتروني وكلمة المرور) وتعيين رمز مميز لهم لاستخدامه من أجل الوصول إلى الموارد المحمية للتطبيق.
في هذه المقالة ، ستتعرف على:
- تكوين Vuex مع Axios
- تحديد الطرق
- التعامل مع المستخدمين
- التعامل مع رمز منتهي الصلاحية
التبعيات
سنعمل مع التبعيات التالية التي تساعد في المصادقة:
- أكسيوس
لإرسال واسترجاع البيانات من API الخاص بنا - فويكس
لتخزين البيانات التي تم الحصول عليها من API الخاص بنا - جهاز التوجيه Vue-Router
للملاحة وحماية الطرق
سنعمل باستخدام هذه الأدوات ونرى كيف يمكنهم العمل معًا لتوفير وظائف مصادقة قوية لتطبيقنا.
واجهة برمجة تطبيقات Backend
سنقوم ببناء موقع مدونة بسيط ، والذي سيستفيد من واجهة برمجة التطبيقات هذه. يمكنك التحقق من المستندات لمعرفة نقاط النهاية وكيفية إرسال الطلبات.
من المستندات ، ستلاحظ إرفاق عدد قليل من نقاط النهاية بقفل. هذه طريقة لإظهار أن المستخدمين المصرح لهم فقط يمكنهم إرسال الطلبات إلى نقاط النهاية هذه. نقاط النهاية غير المقيدة هي نقاط نهاية /register
و /login
. يجب إرجاع خطأ برمز الحالة 401
عندما يحاول مستخدم غير مصادق الوصول إلى نقطة نهاية مقيدة.
بعد تسجيل دخول مستخدم بنجاح ، سيتم استلام رمز الوصول جنبًا إلى جنب مع بعض البيانات في تطبيق Vue ، والذي سيتم استخدامه في إعداد ملف تعريف الارتباط وإرفاقه في عنوان الطلب لاستخدامه في الطلبات المستقبلية. ستتحقق الواجهة الخلفية من عنوان الطلب في كل مرة يتم فيها تقديم طلب إلى نقطة نهاية مقيدة. لا تنجذب إلى تخزين رمز الوصول في التخزين المحلي.
مشروع سقالة
باستخدام Vue CLI ، قم بتشغيل الأمر أدناه لإنشاء التطبيق:
vue create auth-project
انتقل إلى مجلدك الجديد:
cd auth-project
أضف vue-router وقم بتثبيت المزيد من التبعيات - vuex و axios:
vue add router npm install vuex axios
قم الآن بتشغيل مشروعك وسترى ما لدي أدناه على متصفحك:
npm run serve
1. تكوين Vuex مع Axios
Axios هي مكتبة JavaScript تُستخدم لإرسال الطلبات من المتصفح إلى واجهات برمجة التطبيقات. وفقًا لوثائق Vuex ؛
"Vuex هو نمط إدارة حالة + مكتبة لتطبيقات Vue.js. إنه بمثابة متجر مركزي لجميع المكونات في التطبيق ، مع وجود قواعد تضمن أن الحالة لا يمكن أن تتغير إلا بطريقة يمكن التنبؤ بها ".
ماذا يعني ذلك؟ Vuex هو متجر يستخدم في تطبيق Vue يسمح لنا بحفظ البيانات التي ستكون متاحة لكل مكون وتوفير طرق لتغيير هذه البيانات. سنستخدم Axios في Vuex لإرسال طلباتنا وإجراء تغييرات على حالتنا (البيانات). سيتم استخدام Axios في actions
Vuex لإرسال GET
و POST
، وسيتم استخدام الاستجابة التي تم الحصول عليها في إرسال المعلومات إلى mutations
وأي تحديث لبيانات المتجر.
للتعامل مع إعادة تعيين Vuex بعد التحديث ، سنعمل مع vuex-persistedstate
، وهي مكتبة تحفظ بيانات Vuex الخاصة بنا بين عمليات إعادة تحميل الصفحة.
npm install --save vuex-persistedstate
لنقم الآن بإنشاء store
مجلدات جديد في src
لتكوين متجر Vuex. في مجلد store
، قم بإنشاء مجلد جديد ؛ modules
وملف index.js
. من المهم ملاحظة أنك تحتاج فقط إلى القيام بذلك إذا لم يتم إنشاء المجلد لك تلقائيًا.
import Vuex from 'vuex'; import Vue from 'vue'; import createPersistedState from "vuex-persistedstate"; import auth from './modules/auth'; // Load Vuex Vue.use(Vuex); // Create store export default new Vuex.Store({ modules: { auth }, plugins: [createPersistedState()] });
نحن هنا نستخدم Vuex
module
المصادقة من مجلد modules
إلى متجرنا.
الوحدات
الوحدات النمطية هي أقسام مختلفة من متجرنا تتعامل مع مهام متشابهة معًا ، بما في ذلك:
- حالة
- أجراءات
- الطفرات
- حاصل
قبل المتابعة ، دعنا نعدل ملف main.js
import Vue from 'vue' import App from './App.vue' import router from './router'; import store from './store'; import axios from 'axios'; axios.defaults.withCredentials = true axios.defaults.baseURL = 'https://gabbyblog.herokuapp.com/'; Vue.config.productionTip = false new Vue({ store, router, render: h => h(App) }).$mount('#app')
قمنا باستيراد كائن store
من مجلد. /store بالإضافة إلى حزمة ./store
.
كما ذكرنا سابقًا ، يجب تعيين ملف تعريف ارتباط رمز الوصول والبيانات الضرورية الأخرى التي تم الحصول عليها من واجهة برمجة التطبيقات في رؤوس الطلبات للطلبات المستقبلية. نظرًا لأننا سنستخدم Axios عند تقديم الطلبات ، نحتاج إلى تكوين Axios للاستفادة من ذلك. في المقتطف أعلاه ، نقوم بذلك باستخدام axios.defaults.withCredentials = true
، هذا مطلوب لأنه افتراضيًا لا يتم تمرير ملفات تعريف الارتباط بواسطة Axios.
aaxios.defaults.withCredentials = true
هو تعليمات إلى Axios لإرسال جميع الطلبات ببيانات اعتماد مثل ؛ رؤوس التفويض أو شهادات عميل TLS أو ملفات تعريف الارتباط (كما في حالتنا).
قمنا بتعيين axios.defaults.baseURL
لدينا لطلب Axios إلى API
الخاصة بنا بهذه الطريقة ، كلما أرسلنا عبر Axios ، فإنه يستخدم عنوان URL الأساسي هذا. باستخدام ذلك ، يمكننا إضافة نقاط النهاية الخاصة بنا فقط مثل /register
و /login
إلى إجراءاتنا دون ذكر عنوان URL الكامل في كل مرة.
الآن داخل مجلد modules
في store
، أنشئ ملفًا يسمى auth.js
//store/modules/auth.js import axios from 'axios'; const state = { }; const getters = { }; const actions = { }; const mutations = { }; export default { state, getters, actions, mutations };
state
في state
، سنحدد بياناتنا وقيمها الافتراضية:
const state = { user: null, posts: null, };
نقوم بتعيين القيمة الافتراضية state
، وهي كائن يحتوي على user
posts
بقيمها الأولية على أنها null
.
أجراءات
الإجراءات هي الوظائف التي يتم استخدامها commit
طفرة لتغيير الحالة أو يمكن استخدامها dispatch
أي استدعاء إجراء آخر. يمكن استدعاؤها في مكونات أو وجهات نظر مختلفة ثم ترتكب طفرات في حالتنا ؛
تسجيل الإجراء
يأخذ إجراء Register
الخاص بنا بيانات النموذج ، ويرسل البيانات إلى نقطة نهاية /register
الخاصة بنا ، ويخصص الاستجابة response
متغيرة. بعد ذلك ، سنقوم بإرسال username
password
الخاصين بنا إلى إجراء login
الخاص بنا. بهذه الطريقة ، نقوم بتسجيل دخول المستخدم بعد قيامه بالتسجيل ، حتى يتم إعادة توجيهه إلى صفحة /posts
.
async Register({dispatch}, form) { await axios.post('register', form) let UserForm = new FormData() UserForm.append('username', form.username) UserForm.append('password', form.password) await dispatch('LogIn', UserForm) },
إجراء تسجيل الدخول
هنا حيث تحدث المصادقة الرئيسية. عندما يملأ المستخدم اسم المستخدم وكلمة المرور الخاصة به ، يتم تمريره إلى User
وهو كائن FormData ، تأخذ وظيفة LogIn
كائن User
وتقوم بطلب POST
إلى نقطة نهاية /login
الدخول إلى المستخدم.
تقوم وظيفة Login
أخيرًا بإدخال username
إلى طفرة setUser
.
async LogIn({commit}, User) { await axios.post('login', User) await commit('setUser', User.get('username')) },
إنشاء إجراء ما بعد
إجراء CreatePost
الخاص بنا هو وظيفة ، تأخذ المنشور وترسله إلى نقطة النهاية /post
post
ثم ترسل إجراء GetPosts
. يتيح ذلك للمستخدم رؤية مشاركاته بعد الإنشاء.
async CreatePost({dispatch}, post) { await axios.post('post', post) await dispatch('GetPosts') },
الحصول على وظيفة المشاركات
يرسل إجراء GetPosts
الخاص بنا طلب GET
إلى نقطة النهاية /posts
لجلب المنشورات في واجهة برمجة التطبيقات لدينا ويلتزم setPosts
.
async GetPosts({ commit }){ let response = await axios.get('posts') commit('setPosts', response.data) },
إجراء تسجيل الخروج
async LogOut({commit}){ let user = null commit('logout', user) }
يؤدي إجراء LogOut
الخاص بنا إلى إزالة user
من ذاكرة التخزين المؤقت للمتصفح. يقوم بذلك عن طريق logout
:
الطفرات
const mutations = { setUser(state, username){ state.user = username }, setPosts(state, posts){ state.posts = posts }, LogOut(state){ state.user = null state.posts = null }, };
تأخذ كل طفرة في state
وقيمة من الإجراء الذي يرتكبه ، بغض النظر عن Logout
. تُستخدم القيمة التي تم الحصول عليها لتغيير أجزاء معينة أو جميعها أو ما شابه في LogOut
اضبط جميع المتغيرات مرة أخرى على قيمة خالية.
حاصل
الحاصلون هم وظائف للحصول على الدولة. يمكن استخدامه في مكونات متعددة للحصول على الحالة الحالية. تتحقق الدالة isAuthenticatated
إذا تم تعريف state.user
أم لاغية وإرجاع true
أو false
على التوالي. تقوم StatePosts
و StateUser
بإرجاع قيمة state.posts
و state.user
على التوالي.
const getters = { isAuthenticated: state => !!state.user, StatePosts: state => state.posts, StateUser: state => state.user, };
الآن يجب أن يشبه ملف auth.js
بالكامل الكود الخاص بي على GitHub.
إعداد المكونات
1. NavBar.vue
و App.vue
المكونات
في مجلد src/components
element الخاص بك ، احذف HelloWorld.vue
وملف جديد يسمى NavBar.vue
.
هذا هو المكون الخاص بشريط التنقل الخاص بنا ، فهو يرتبط بصفحات مختلفة من المكون الخاص بنا تم توجيهه هنا. يشير كل رابط جهاز توجيه إلى مسار / صفحة على تطبيقنا.
يعد v-if="isLoggedIn"
شرطًا لعرض ارتباط Logout
إذا قام المستخدم بتسجيل الدخول وإخفاء مسار Register
وتسجيل Login
. لدينا طريقة logout
التي لا يمكن الوصول إليها إلا للمستخدمين الذين قاموا بتسجيل الدخول ، وسيتم استدعاء هذا عند النقر فوق ارتباط Logout
. سيرسل إجراء LogOut
ثم يوجه المستخدم إلى صفحة تسجيل الدخول.
<template> <div> <router-link to="/">Home</router-link> | <router-link to="/posts">Posts</router-link> | <span v-if="isLoggedIn"> <a @click="logout">Logout</a> </span> <span v-else> <router-link to="/register">Register</router-link> | <router-link to="/login">Login</router-link> </span> </div> </template> <script> export default { name: 'NavBar', computed : { isLoggedIn : function(){ return this.$store.getters.isAuthenticated} }, methods: { async logout (){ await this.$store.dispatch('LogOut') this.$router.push('/login') } }, } </script> <style> #nav { padding: 30px; } #nav a { font-weight: bold; color: #2c3e50; } a:hover { cursor: pointer; } #nav a.router-link-exact-active { color: #42b983; } </style>
الآن قم بتحرير مكون App.vue
ليبدو كالتالي:
<template> <div> <NavBar /> <router-view/> </div> </template> <script> // @ is an alias to /src import NavBar from '@/components/NavBar.vue' export default { components: { NavBar } } </script> <style> #app { font-family: Avenir, Helvetica, Arial, sans-serif; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; text-align: center; color: #2c3e50; } </style>
هنا قمنا باستيراد مكون NavBar الذي أنشأناه أعلاه ووضعناه في قسم القالب قبل <router-view />
.
2. مكونات طرق العرض
مكونات طرق العرض عبارة عن صفحات مختلفة على التطبيق سيتم تحديدها ضمن مسار ويمكن الوصول إليها من شريط التنقل. للبدء ، انتقل إلى مجلد views
العرض ، واحذف المكون About.vue
، وأضف المكونات التالية:
-
Home.vue
-
Register.vue
-
Login.vue
-
Posts.vue
Home.vue
أعد كتابة Home.vue
لتبدو هكذا:
<template> <div class="home"> <p>Heyyyyyy welcome to our blog, check out our posts</p> </div> </template> <script> export default { name: 'Home', components: { } } </script>
سيعرض هذا نصًا ترحيبيًا للمستخدمين عند زيارتهم للصفحة الرئيسية.
Register.vue
هذه هي الصفحة التي نريد أن يتمكن المستخدمون لدينا من التسجيل في تطبيقنا. عندما يملأ المستخدمون النموذج ، يتم إرسال معلوماتهم إلى واجهة برمجة التطبيقات وإضافتها إلى قاعدة البيانات ثم تسجيل الدخول.
بالنظر إلى واجهة برمجة التطبيقات ، تتطلب نقطة نهاية /register
username
، واسم كامل password
full_name
. لنقم الآن بإنشاء صفحة ونموذج للحصول على هذه المعلومات:
<template> <div class="register"> <div> <form @submit.prevent="submit"> <div> <label for="username">Username:</label> <input type="text" name="username" v-model="form.username"> </div> <div> <label for="full_name">Full Name:</label> <input type="text" name="full_name" v-model="form.full_name"> </div> <div> <label for="password">Password:</label> <input type="password" name="password" v-model="form.password"> </div> <button type="submit"> Submit</button> </form> </div> <p v-if="showError">Username already exists</p> </div> </template>
في مكون Register
، سنحتاج إلى استدعاء إجراء Register
الذي سيتلقى بيانات النموذج.
<script> import { mapActions } from "vuex"; export default { name: "Register", components: {}, data() { return { form: { username: "", full_name: "", password: "", }, showError: false }; }, methods: { ...mapActions(["Register"]), async submit() { try { await this.Register(this.form); this.$router.push("/posts"); this.showError = false } catch (error) { this.showError = true } }, }, }; </script>
نبدأ باستيراد mapActions
من Vuex ، ما يفعله هذا هو استيراد الإجراءات من متجرنا إلى المكون. هذا يسمح لنا باستدعاء الإجراء من المكون.
تحتوي data()
على قيمة الحالة المحلية التي سيتم استخدامها في هذا المكون ، ولدينا كائن form
يحتوي على username
full_name
password
، مع تعيين قيمها الأولية على سلسلة فارغة. لدينا أيضًا showError
وهو منطقي ، لاستخدامه إما لإظهار خطأ أم لا.
في methods
، نقوم باستيراد إجراء Register
باستخدام Mapactions
في المكون ، لذلك يمكن استدعاء إجراء Register
this.Register
.
لدينا طريقة إرسال this.Register
إجراء Register
الذي يمكننا الوصول إليه this.form
هذا. إذا لم يتم العثور على error
فإننا نستفيد من this.$router
لإرسال المستخدم إلى صفحة تسجيل الدخول. وإلا قمنا بتعيين showError
على true.
بعد القيام بذلك ، يمكننا تضمين بعض التصميم.
<style scoped> * { box-sizing: border-box; } label { padding: 12px 12px 12px 0; display: inline-block; } button[type=submit] { background-color: #4CAF50; color: white; padding: 12px 20px; cursor: pointer; border-radius:30px; } button[type=submit]:hover { background-color: #45a049; } input { margin: 5px; box-shadow:0 0 15px 4px rgba(0,0,0,0.06); padding:10px; border-radius:30px; } #error { color: red; } </style>
Login.vue
صفحة تسجيل الدخول الخاصة بنا هي المكان الذي يقوم فيه المستخدمون المسجلون بإدخال username
password
الخاصة بهم للمصادقة عليها بواسطة واجهة برمجة التطبيقات وتسجيل الدخول إلى موقعنا.
<template> <div class="login"> <div> <form @submit.prevent="submit"> <div> <label for="username">Username:</label> <input type="text" name="username" v-model="form.username" /> </div> <div> <label for="password">Password:</label> <input type="password" name="password" v-model="form.password" /> </div> <button type="submit">Submit</button> </form> <p v-if="showError">Username or Password is incorrect</p> </div> </div> </template>
الآن سيتعين علينا تمرير بيانات النموذج الخاصة بنا إلى الإجراء الذي يرسل الطلب ثم دفعهم إلى Posts
الصفحة الآمنة
<script> import { mapActions } from "vuex"; export default { name: "Login", components: {}, data() { return { form: { username: "", password: "", }, showError: false }; }, methods: { ...mapActions(["LogIn"]), async submit() { const User = new FormData(); User.append("username", this.form.username); User.append("password", this.form.password); try { await this.LogIn(User); this.$router.push("/posts"); this.showError = false } catch (error) { this.showError = true } }, }, }; </script>
نقوم باستيراد Mapactions
في استيراد إجراء تسجيل LogIn
إلى المكون ، والذي سيتم استخدامه في وظيفة submit
الخاصة بنا.
بعد إجراء Login
، يتم إعادة توجيه المستخدم إلى صفحة /posts
. في حالة حدوث خطأ ، يتم اكتشاف الخطأ ويتم تعيين ShowError
على true.
الآن ، بعض التصميم:
<style scoped> * { box-sizing: border-box; } label { padding: 12px 12px 12px 0; display: inline-block; } button[type=submit] { background-color: #4CAF50; color: white; padding: 12px 20px; cursor: pointer; border-radius:30px; } button[type=submit]:hover { background-color: #45a049; } input { margin: 5px; box-shadow:0 0 15px 4px rgba(0,0,0,0.06); padding:10px; border-radius:30px; } #error { color: red; } </style>
Posts.vue
صفحة المنشورات الخاصة بنا هي الصفحة المؤمنة والمتاحة فقط للمستخدمين المصادق عليهم. في هذه الصفحة ، يمكنهم الوصول إلى المنشورات في قاعدة بيانات API. يسمح هذا للمستخدمين بالوصول إلى المنشورات ويمكنهم أيضًا من إنشاء منشورات على واجهة برمجة التطبيقات.
<template> <div class="posts"> <div v-if="User"> <p>Hi {{User}}</p> </div> <div> <form @submit.prevent="submit"> <div> <label for="title">Title:</label> <input type="text" name="title" v-model="form.title"> </div> <div> <textarea name="write_up" v-model="form.write_up" placeholder="Write up..."></textarea> </div> <button type="submit"> Submit</button> </form> </div> <div class="posts" v-if="Posts"> <ul> <li v-for="post in Posts" :key="post.id"> <div> <p>{{post.title}}</p> <p>{{post.write_up}}</p> <p>Written By: {{post.author.username}}</p> </div> </li> </ul> </div> <div v-else> Oh no!!! We have no posts </div> </div> </template>
في الكود أعلاه ، لدينا نموذج للمستخدم ليتمكن من إنشاء مشاركات جديدة. يجب أن يؤدي إرسال النموذج إلى إرسال المنشور إلى واجهة برمجة التطبيقات - سنضيف الطريقة التي تقوم بذلك قريبًا. لدينا أيضًا قسم يعرض المنشورات التي تم الحصول عليها من واجهة برمجة التطبيقات (في حال كان لدى المستخدم أي منها). إذا لم يكن لدى المستخدم أية منشورات ، فإننا ببساطة نعرض رسالة مفادها أنه لا توجد مشاركات.
يتم تعيين معلمات StateUser
و StatePosts
على سبيل المثال ، يتم استيرادها باستخدام mapGetters
إلى Posts.vue
ومن ثم يمكن استدعاؤها في القالب.
<script> import { mapGetters, mapActions } from "vuex"; export default { name: 'Posts', components: { }, data() { return { form: { title: '', write_up: '', } }; }, created: function () { // a function to call getposts action this.GetPosts() }, computed: { ...mapGetters({Posts: "StatePosts", User: "StateUser"}), }, methods: { ...mapActions(["CreatePost", "GetPosts"]), async submit() { try { await this.CreatePost(this.form); } catch (error) { throw "Sorry you can't make a post now!" } }, } }; </script>
لدينا حالة أولية form
، وهو كائن له title
و write_up
كمفاتيح ويتم تعيين القيم على سلسلة فارغة. ستتغير هذه القيم إلى كل ما يدخله المستخدم في النموذج في قسم القالب الخاص بالمكون الخاص بنا.
عندما يرسل المستخدم المنشور ، فإننا نسمي this.CreatePost
الذي يتلقى كائن النموذج.
كما ترى في دورة الحياة created
، لدينا هذا this.GetPosts
لجلب المشاركات عند إنشاء المكون.
بعض التصميم ،
<style scoped> * { box-sizing: border-box; } label { padding: 12px 12px 12px 0; display: inline-block; } button[type=submit] { background-color: #4CAF50; color: white; padding: 12px 20px; cursor: pointer; border-radius:30px; margin: 10px; } button[type=submit]:hover { background-color: #45a049; } input { width:60%; margin: 15px; border: 0; box-shadow:0 0 15px 4px rgba(0,0,0,0.06); padding:10px; border-radius:30px; } textarea { width:75%; resize: vertical; padding:15px; border-radius:15px; border:0; box-shadow:0 0 15px 4px rgba(0,0,0,0.06); height:150px; margin: 15px; } ul { list-style: none; } #post-div { border: 3px solid #000; width: 500px; margin: auto; margin-bottom: 5px;; } </style>
2. تحديد الطرق
في ملف router/index.js
الخاص بنا ، قم باستيراد طرق العرض الخاصة بنا وحدد المسارات لكل منها
import Vue from 'vue' import VueRouter from 'vue-router' import store from '../store'; import Home from '../views/Home.vue' import Register from '../views/Register' import Login from '../views/Login' import Posts from '../views/Posts' Vue.use(VueRouter) const routes = [ { path: '/', name: 'Home', component: Home }, { path: '/register', name: "Register", component: Register, meta: { guest: true }, }, { path: '/login', name: "Login", component: Login, meta: { guest: true }, }, { path: '/posts', name: Posts, component: Posts, meta: {requiresAuth: true}, } ] const router = new VueRouter({ mode: 'history', base: process.env.BASE_URL, routes }) export default router
3. التعامل مع المستخدمين
- المستخدمون غير المصرح لهم
إذا لاحظت أثناء تحديد مساراتrouter.BeforeEach
، فقدmeta
مفتاحًا تعريفًا للإشارة إلى وجوب مصادقة المستخدم ، فنحن بحاجة الآن إلى جهاز توجيه. قبل كل حارس تنقل يقوم بالتحقق مما إذا كان المسار يحتوي على مفتاح التعريفmeta: {requiresAuth: true}
. إذا كان المسار يحتوي على مفتاحmeta
، فإنه يتحقق من المتجر بحثًا عن رمز مميز ؛ إذا كان موجودًا ، فإنه يعيد توجيههم إلى مسارlogin
.
const router = new VueRouter({ mode: 'history', base: process.env.BASE_URL, routes }) router.beforeEach((to, from, next) => { if(to.matched.some(record => record.meta.requiresAuth)) { if (store.getters.isAuthenticated) { next() return } next('/login') } else { next() } }) export default router
- المستخدمون المعتمدون
لدينا أيضًاmeta
على/register
و/login
.meta: {guest: true}
يمنع المستخدمين الذين قاموا بتسجيل الدخول من الوصول إلى المسارات باستخدام metaguest
.
router.beforeEach((to, from, next) => { if (to.matched.some((record) => record.meta.guest)) { if (store.getters.isAuthenticated) { next("/posts"); return; } next(); } else { next(); } });
في النهاية ، يجب أن يكون ملفك كالتالي:
import Vue from "vue"; import VueRouter from "vue-router"; import store from "../store"; import Home from "../views/Home.vue"; import Register from "../views/Register"; import Login from "../views/Login"; import Posts from "../views/Posts"; Vue.use(VueRouter); const routes = [ { path: "/", name: "Home", component: Home, }, { path: "/register", name: "Register", component: Register, meta: { guest: true }, }, { path: "/login", name: "Login", component: Login, meta: { guest: true }, }, { path: "/posts", name: "Posts", component: Posts, meta: { requiresAuth: true }, }, ]; const router = new VueRouter({ mode: "history", base: process.env.BASE_URL, routes, }); router.beforeEach((to, from, next) => { if (to.matched.some((record) => record.meta.requiresAuth)) { if (store.getters.isAuthenticated) { next(); return; } next("/login"); } else { next(); } }); router.beforeEach((to, from, next) => { if (to.matched.some((record) => record.meta.guest)) { if (store.getters.isAuthenticated) { next("/posts"); return; } next(); } else { next(); } }); export default router;
4.التعامل مع الرمز منتهي الصلاحية (الطلبات المحظورة)
تم تعيين واجهة برمجة التطبيقات الخاصة بنا على الرموز المميزة التي تنتهي صلاحيتها بعد 30 دقيقة ، والآن إذا حاولنا الوصول إلى صفحة posts
بعد 30 دقيقة ، فسنحصل على خطأ 401
، مما يعني أنه يتعين علينا تسجيل الدخول مرة أخرى ، لذلك سنقوم بتعيين معترض يقرأ إذا حصلنا على خطأ 401
، ثم يعيد توجيهنا مرة أخرى إلى صفحة login
.
أضف المقتطف أدناه بعد إعلان عنوان URL الافتراضي لـ Axios في ملف main.js
axios.interceptors.response.use(undefined, function (error) { if (error) { const originalRequest = error.config; if (error.response.status === 401 && !originalRequest._retry) { originalRequest._retry = true; store.dispatch('LogOut') return router.push('/login') } } })
يجب أن يأخذ هذا الكود الخاص بك إلى نفس الحالة مثل المثال على GitHub.
خاتمة
إذا تمكنت من المتابعة حتى النهاية ، فيجب أن تكون قادرًا الآن على إنشاء تطبيق أمامي يعمل بكامل طاقته وآمن. لقد تعلمت الآن المزيد حول Vuex وكيفية دمجه مع Axios ، وكذلك كيفية حفظ بياناته بعد إعادة التحميل.
الكود متاح على GitHub →
الموقع المستضاف:
https://nifty-hopper-1e9895.netlify.app/
API:
https://gabbyblog.herokuapp.com
مستندات API:
https://gabbyblog.herokuapp.com/docs
موارد
- "التعامل مع ملفات تعريف الارتباط مع أكسيوس" ، Aditya Srivastava ، متوسط
- "Creating an Authentication Navigation Guard In Vue" ، لوري بارث ، مدونة Ten Mile Square
- "Getting Started with Vuex ،" الدليل الرسمي
- "مصادقة Vue.js JWT مع موجه Vuex و Vue ،" BezKoder