در این مقاله، می خواهیم به شما نحوه پیاده سازی احراز هویت در یک برنامه Nuxt.js با ماژول Auth را آموزش دهیم.
در این آموزش از jwt برای احراز هویت استفاده می کنم.
برای صرفه جویی در زمان از Api ایی که برای این مقاله آموزشی آماده کرده ام، استفاده می کنیم. پس برای کار با آن، ابتدا از آن یک clone می گیریم، مطابق زیر:
$ git clone https://github.com/ammezie/jwt-auth-api.git
سپس وابستگی های این api را با دستور زیر نصب می کنیم.
$ cd jwt-auth-api $ npm install
حال فایل .env.example را به .env تغییر نام بدهید و سپس APP_KEY را با دستور زیر تولید کنید.
// with the adonis CLI $ adonis key:generate // without the adonis CLI $ node ace key:generate
بعد از اینکه عملیات فوق را انجام دادید، باید migration تان را اجرا کنید. مطابق با دستور زیر:
// with the adonis CLI $ adonis migration:run // without the adonis CLI $ node ace migration:run
اجازه دهید قبل از اینکه ساخت برنامه Nuxt.js را شروع کنیم، کمی درباره این api صحبت کنیم. این api با استفاده از AdonisJs ساخته شده و از jwt Json Web Token برای احراز هویت استفاده می کند. همچنین برای پایگاه داده از SQLite بهره برده ایم.
این Api سه endpoint (یا آدرس) دارد:
این آدرس توسط یا middleware ایی به نام Auth محافظت می شود و برای دسترسی به این آدرس کاربر باید احراز هویت شده باشد.
و همچنین قابلیت CROS هم در این Api فعال شده است. حال می توانیم این Api را اجرا کنیم.
$ npm start
برای دسترسی به Api از آدرس http://127.0.0.1:3333/api استفاده کنید.
نکته مهم: اگر چه در این آموزش از AdonisJs برای ساخت Api استفاده کردیم، اما شما می توانید از هر فریم ورکی که مایل بودید برای اینکار بهره ببرید.
برای شروع کار، نیاز به استفاده از Vue Cli داریم. در صورتی که قبلاً Vue Cli را نصب نکرده اید، ترمینال را باز کرده و با دستور زیر آن را نصب کنید.
$ npm install -g vue-cli
سپس با دستور زیر یک برنامه جدید Nuxt.js ایجاد کنید.
$ vue init nuxt/starter nuxt-auth
حال باید وابستگی های مورد نیاز آن را نصب کنیم.
$ cd nuxt-auth $ npm install
سپس برنامه را اجرا کنید.
$ npm run dev
با اجرای دستور فوق، برنامه در آدرس http://localhost:3000 نمایش داده می شود.
حال ماژول های Nuxt.js یی که در برنامه به آن نیاز داریم را نصب خواهیم کرد. ما از ماژول های Nuxt Auth و Nuxt Axios استفاده می کنیم.
$ npm install @nuxtjs/auth @nuxtjs/axios --save
در مرحله بعد، کد زیر را به فایل nuxt.config.js اضافه کنید.
// nuxt.config.js modules: [ '@nuxtjs/axios', '@nuxtjs/auth' ],
در مرحله بعد باید این ماژول را پیکربندی کنیم، کد زیر را درون فایل nuxt.config.js قرار دهید.
// nuxt.config.js axios: { baseURL: 'http://127.0.0.1:3333/api' }, auth: { strategies: { local: { endpoints: { login: { url: 'login', method: 'post', propertyName: 'data.token' }, user: { url: 'me', method: 'get', propertyName: 'data' }, logout: false } } } }
در اینجا یک baseURL قرار دادیم که Axios هنگام ایجاد یک درخواست از آن استفاده می کند.
بطور مشابه درخواست ارسالی از آدرس /me داخل آبجکت data قرار می گیرد. در نهایت مقدار logout را برابر false می گذاریم.
چون api مان هیچ آدرسی برای logout ندارد. هنگامی که کاربر از سیستم logout کرد، توکن را از سیستم حذف می کنیم.
برای استایل دهی به برنامه از فریمورک Bulma استفاده می کنیم. فایل nuxt.config.js را باز کرده و کدهای زیر را داخل آبجکت Link که در آبجکت head قرار دارد کپی کنید.
// nuxt.config.js { rel: 'stylesheet', href: 'https://cdnjs.cloudflare.com/ajax/libs/bulma/0.7.1/css/bulma.min.css' }
حال کامپوننت Navbar (منو) را ایجاد می کنیم. فایل APPLogo.vue که در فولدر components قرار دارد را به Navbar.vue تغییر نام داده و کدهای زیر را در آن قرار دهید.
// components/Navbar.vue
<template>
<nav class="navbar is-light">
<div class="container">
<div class="navbar-brand">
<nuxt-link class="navbar-item" to="/">Nuxt Auth</nuxt-link>
<button class="button navbar-burger">
<span></span>
<span></span>
<span></span>
</button>
</div>
<div class="navbar-menu">
<div class="navbar-end">
<div class="navbar-item has-dropdown is-hoverable">
<a class="navbar-link">
My Account
</a>
<div class="navbar-dropdown">
<nuxt-link class="navbar-item" to="/profile">My Profile</nuxt-link>
<hr class="navbar-divider">
<a class="navbar-item">Logout</a>
</div>
</div>
<nuxt-link class="navbar-item" to="/register">Register</nuxt-link>
<nuxt-link class="navbar-item" to="/login">Log In</nuxt-link>
</div>
</div>
</div>
</nav>
</template>
این منو شامل لینک هایی برای لاگین، ثبت نام، نمایش پروفایل و logout است.
در قدم بعد برای استفاده از کامپوننت Navbar، قالب پیش فرض آن را بروزرسانی می کنیم.فایل layouts/default.vue را باز کرده و کدهای زیر را در آن قرار دهید.
// layouts/default.vue <template> <div> <Navbar/> <nuxt/> </div> </template> <script> import Navbar from '~/components/Navbar' export default { components: { Navbar } } </script>
همچنین صفحه خانه را بروزرسانی می کنیم. فایل pages/index.vue را باز کرده و کدهای زیر را در آن قرار دهید.
// pages/index.vue <template> <section class="section"> <div class="container"> <h1 class="title">Nuxt Auth</h1> </div> </section> </template>
حال ظاهر برنامه را باید مطابق شکل زیر باشد.
یک فایل با نام register.vue داخل فولدر pages ایجاد کنید، و کدهای زیر را در آن قرار دهید.
// pages/register.vue <template> <section class="section"> <div class="container"> <div class="columns"> <div class="column is-4 is-offset-4"> <h2 class="title has-text-centered">Register!</h2> <Notification :message="error" v-if="error"/> <form method="post" @submit.prevent="register"> <div class="field"> <label class="label">Username</label> <div class="control"> <input type="text" class="input" name="username" v-model="username" required > </div> </div> <div class="field"> <label class="label">Email</label> <div class="control"> <input type="email" class="input" name="email" v-model="email" required > </div> </div> <div class="field"> <label class="label">Password</label> <div class="control"> <input type="password" class="input" name="password" v-model="password" required > </div> </div> <div class="control"> <button type="submit" class="button is-dark is-fullwidth">Register</button> </div> </form> <div class="has-text-centered" style="margin-top: 20px"> Already got an account? <nuxt-link to="/login">Login</nuxt-link> </div> </div> </div> </div> </section> </template> <script> import Notification from '~/components/Notification' export default { components: { Notification, }, data() { return { username: '', email: '', password: '', error: null } }, methods: { async register() { try { await this.$axios.post('register', { username: this.username, email: this.email, password: this.password }) await this.$auth.loginWith('local', { data: { email: this.email, password: this.password }, }) this.$router.push('/') } catch (e) { this.error = e.response.data.message } } } } </script>
کدهای بالا شامل یک فرم همراه با سه فیلد ورودی است: نام کاربری، ایمیل و کلمه عبور.
هر کدام از این متغیرها به پروپرتی متناظرش که در کامپوننت قرار گرفته متصل شده است.
بعد از ارسال فرم، متد register فراخوانی می شود. با استفاده از ماژول Axios یک درخواست post به آدرس register ارسال می کنیم. در صورتی که ثبت نام موفقیت آمیز بود، با استفاده از استراتژی local از متد loginWith() که در ماژول Auth قرار دارد، استفاده می کنیم و اطلاعات کاربری که لاگین کرده را به آن ارسال خواهیم کرد، سپس کاربر را به صفحه پروفایل خودش یا صفحه اصلی هدایت می کنیم. اگر در حین ثبت نام خطایی اتفاق افتاد، مقدار پروپرتی error را برابر متن پیام خطا قرار می دهیم.
همچنین در صورت بروز یک خطا متن خطا توسط یک کامپوننت Notification که بعداً آن را ایجاد خواهیم کرد، نمایش داده می شود.
حال کامپوننت Notification را ایجاد می کنیم. برای اینکار یک فایل به نام Notification.vue داخل فولدر components ایجاد کنید و کدهای زیر را در آن قرار دهید.
// components/Notification.vue <template> <div class="notification is-danger"> {{ message }} </div> </template> <script> export default { name: 'Notification', props: ['message'] } </script>
کامپوننت Notification یک پروپرتی message را که حاوی متن پیام خطا است را دریافت می کند.
حال می توانیم عمل ثبت نام کاربران را تست کنیم.
هنگامی که کاربر وارد صفحه لاگین شود هیچ راهی وجود ندارد که بفهمیم آیا این کاربر قبلاً لاگین کرده یا خیر.
برای حل مسئله باید کامپوننت Navbar را بروزرسانی کرده و یک سری پروپرتی هایی را به آن اضافه کنیم. اما قبل از شروع، یک فایل با نام index.js داخل فولدر store ایجاد می کنیم.
ماژول Auth وضعیت احراز هویت کاربران به همراه اطلاعات آنها را داخل وضعیت vuex که در آبجکت auth قرار دارد، ذخیره می کند. به این ترتیب می توانیم بررسی کنیم که آیا کاربری قبلا به سیستم لاگین کرده یا خیر، و این کار را توسط this.$store.state.auth.loggedIn
که یک مقدار true یا false بر می گرداند، انجام می دهیم.
همچنین برای دریافت اطلاعات مربوط به کاربر لاگین کرده از this.$store.state.auth.user
استفاده می کنیم، و اگر کاربری به سیستم لاگین نکرده بود مقدار null را بر می گرداند.
نکته: برای دسترسی به وضعیت احراز هویت یک کاربر و همچنین اطلاعات مربوط به کاربر لاگین شده می توانیم با ماژول Auth و توسط this.$auth.loggedIn
و this.$auth.user
استفاده کنیم.
چون می خواهیم از پروپرتی هایی که تعریف کردیم در قسمت های مختلف برنامه استفاده کنیم، از یک getter برای ذخیره سازی اطلاعات بهره می بریم.
کدهای زیر را در فایل store/index.js قرار دهید.
// store/index.js export const getters = { isAuthenticated(state) { return state.auth.loggedIn }, loggedInUser(state) { return state.auth.user } }
در بالا ما دو getter ایجاد کردیم. اولی یعنی isAuthenticated وضعیت احراز هویت یک کاربر را بر می گرداند و دومی یعنی loggedInUser اطلاعات مربوط به کاربر لاگین شده را بر می گرداند.
در قدم بعد کامپوننت Navbar را برای استفاده از این getterها بروزرسانی می کنیم. کدهای زیر را داخل فایل components/Navbar.vue قرار دهید
// components/Navbar.vue <template> <nav class="navbar is-light"> <div class="container"> <div class="navbar-brand"> <nuxt-link class="navbar-item" to="/">Nuxt Auth</nuxt-link> <button class="button navbar-burger"> <span></span> <span></span> <span></span> </button> </div> <div class="navbar-menu"> <div class="navbar-end"> <div class="navbar-item has-dropdown is-hoverable" v-if="isAuthenticated"> <a class="navbar-link"> {{ loggedInUser.username }} </a> <div class="navbar-dropdown"> <nuxt-link class="navbar-item" to="/profile">My Profile</nuxt-link> <hr class="navbar-divider"> <a class="navbar-item">Logout</a> </div> </div> <template v-else> <nuxt-link class="navbar-item" to="/register">Register</nuxt-link> <nuxt-link class="navbar-item" to="/login">Log In</nuxt-link> </template> </div> </div> </div> </nav> </template> <script> import { mapGetters } from 'vuex' export default { computed: { ...mapGetters(['isAuthenticated', 'loggedInUser']) } } </script>
همان طور که در بالا می بینید از عملگر spread یعنی (...) برای استخراج getterها از داخل mapGetters استفاده کردیم، سپس با استفاده از isAuthenticated لینک های مربوط به ثبت نام، لاگین و یا اطلاعات مربوط به پروفایل کاربر، بسته به اینکه آیا کاربر قبلاً لاگین کرده یا خیر، ایجاد می کنیم.
همچنین از loggedInUser برای نمایش نام کاربری کاربر احراز هویت شده استفاده می کنیم.
حال اگر برنامه را رفرش کنیم، باید تصویر زیر را ببینیم.
در این قسمت می خواهیم قابلیت ورود به سایت (لاگین) را پیاده سازی کنیم.
برای اینکار در فولدر pages یک فایل با نام login.vue ایجاد کنید، و کدهای زیر را در آن قرار دهید.
// pages/login.vue <template> <section class="section"> <div class="container"> <div class="columns"> <div class="column is-4 is-offset-4"> <h2 class="title has-text-centered">Welcome back!</h2> <Notification :message="error" v-if="error"/> <form method="post" @submit.prevent="login"> <div class="field"> <label class="label">Email</label> <div class="control"> <input type="email" class="input" name="email" v-model="email" > </div> </div> <div class="field"> <label class="label">Password</label> <div class="control"> <input type="password" class="input" name="password" v-model="password" > </div> </div> <div class="control"> <button type="submit" class="button is-dark is-fullwidth">Log In</button> </div> </form> <div class="has-text-centered" style="margin-top: 20px"> <p> Don't have an account? <nuxt-link to="/register">Register</nuxt-link> </p> </div> </div> </div> </div> </section> </template> <script> import Notification from '~/components/Notification' export default { components: { Notification, }, data() { return { email: '', password: '', error: null } }, methods: { async login() { try { await this.$auth.loginWith('local', { data: { email: this.email, password: this.password } }) this.$router.push('/') } catch (e) { this.error = e.response.data.message } } } } </script>
کد بالا تقریباً مانند صفحه ثبت نام است.
این کد شامل یک فرم با دو فیلد ایمیل و کلمه عبور است.
هنگام ارسال فرم متد login فراخوانی می شود. با استفاده از متد loginWith() در ماژول Auth و ارسال اطلاعات کاربر به آن می توانیم کاربر را به سیستم وارد کنیم.
اگر احراز هویت موفقیت آمیز بود، کاربر را به صفحه خانگی هدایت می کنیم، در غیر اینصورت متن پیغام خطا را در پروپرتی error می ریزیم.
همچنین از کامپوننت Notification که قبلاً آن را ایجاد کردیم، پیام خطا را به کاربر نمایش می دهیم.
در این قسمت می خواهیم امکانی را ایجاد کنیم تا کاربرانی که به برنامه لاگین کرده اند بتوانند پروفایل شان را ببینند. برای اینکار داخل فولدر pages یک فایل با نام profile.vue ایجاد کنید و کدهای زیر را در این فایل قرار دهید.
// pages/profile.vue <template> <section class="section"> <div class="container"> <h2 class="title">My Profile</h2> <div class="content"> <p> <strong>Username:</strong> {{ loggedInUser.username }} </p> <p> <strong>Email:</strong> {{ loggedInUser.email }} </p> </div> </div> </section> </template> <script> import { mapGetters } from 'vuex' export default { computed: { ...mapGetters(['loggedInUser']) } } </script>
در بالا ما از loggedInUser که قبلاً برای نمایش اطلاعات مربوط به کاربر لاگین کرده بود، استفاده میکنیم.
با کلیک روی لینک my profile باید نتیجه ای مانند تصویر زیر را ببینید
لینک مربوط به logout که در کامپوننت Navbar تعریف کرده بودیم را مطابق زیر بروزرسانی کنید.
// components/Navbar.vue <a class="navbar-item" @click="logout">Logout</a>
هنگامی که لینک log out کلیک شد، متد logout فراخوانی می شود، سپس باید متد logout را به بخش script در کامپوننت Navbar اضافه کنیم.
// components/Navbar.vue methods: { async logout() { await this.$auth.logout(); }, },
همان طور که در بالا می بینید، ما متد logout که در ماژول Auth قرار دارد را فراخوانی می کنیم. این متد توکن را از سیستم کاربر حذف کرده و کاربر را به صفحه خانگی هدایت می کند.
تا به اینجا همه می توانند صفحه profile را مشاهده کنند و اگر کاربری هنوز لاگین نکرده باشد با خطای زیر مواجه می شود.
برای رفع این مشکل، باید صفحه پروفایل را محدود به کاربرانی که لاگین کرده اند بکنیم. برای اینکار می توانیم از ماژول Auth استفاده کنیم. ماژول Auth یک middleware به نام auth ارائه داده که برای این سناریو کاربرد دارد.
بنابراین باید middleware ،auth را به صفحه profile و در قسمت Script آن اضافه کنیم.
// pages/profile.vue <script> ... export default { middleware: 'auth', ... } </script>
حال هنگامی که یک کاربر لاگین نکرده بخواهد صفحه پروفایل را مشاهده کند به صفحه login هدایت می شود.
تا به اینجا کاربری که حتی به برنامه لاگین نکرده باشد، می تواند صفحات لاگین و ثبت نام را ببیند. یک روش برای حل این مسئله، محدود کردن صفحات لاگین و ثبت نام برای کاربرانی که لاگین نکرده اند است. برای اینکار باید یک middleware برای کاربران مهمان ایجاد کنیم.
یک فایل با نام guest.js در فولدر middleware ایجاد کنید و کدهای زیر را در آن قرار دهید.
// middleware/guest.js export default function ({ store, redirect }) { if (store.state.auth.loggedIn) { return redirect('/') } }
هر middleware یک context را به عنوان آرگومان اول می گیرد، سپس می توانیم مقادیر Store و redirect را از این context استخراج کنیم. همچنین بررسی می کنیم که اگر کاربر لاگین کرده بود، آنگاه به صفحه خانگی هدایت شود، در غیر اینصورت درخواست به شکل عادی اجرا می شود.
در قدم بعد باید از این middleware استفاده کنیم. برای اینکار قسمت script صفحات login و register را مطابق زیر بروزرسانی کنید.
حال همه چیز باید مطابق انتظار کار کند.
در این قسمت، به پرسشهای تخصصی شما دربارهی محتوای مقاله پاسخ داده نمیشود. سوالات خود را اینجا بپرسید.
در این قسمت، به پرسشهای تخصصی شما دربارهی محتوای مقاله پاسخ داده نمیشود. سوالات خود را اینجا بپرسید.
در این قسمت، به پرسشهای تخصصی شما دربارهی محتوای مقاله پاسخ داده نمیشود. سوالات خود را اینجا بپرسید.