در جلسه قبل Backend سیستم احراز هویت SPA با سطح دسترسی را پیاده سازی کردیم. در این قسمت به بخش Frontend پروژه ساخت احراز هویت تک صفحه ای به کمک vueJs می پردازیم.
ابتدا جهت دریافت vueJs باید پکیج laravel/ui را از طریق ترمینال (CommandLine) به پروژه خود اضافه کنید، با استفاده از دستور زیر:
composer require laravel/ui
سپس در ترمینال دستور زیر را وارد کنید:
php artisan ui vue
حالا دستور نصب npm را در ترمینال وارد کنید:
npm install
هم اکنون که پیکربندی vue.js با دستورات بالا به پایان رسیده است، کار را ادامه می دهیم. فایل 'resources/views/welcome.blade.php' را باز کنید و به جای کدهایی که در فایل وجود دارند، کدهای زیر را قرار دهید:
<!DOCTYPE html> <html lang="{{ str_replace('_', '-', app()->getLocale()) }}"> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1"> <!-- CSRF Token --> <meta name="csrf-token" content="{{ csrf_token() }}"> <title>{{ config('app.name', 'Laravel') }}</title> <!-- Scripts --> <script src="{{ asset('js/app.js') }}" defer></script> <!-- Fonts --> <link rel="dns-prefetch" href="https://fonts.gstatic.com"> <link href="https://fonts.googleapis.com/css?family=Raleway:300,400,600" rel="stylesheet" type="text/css"> <!-- Styles --> <link href="{{ asset('css/app.css') }}" rel="stylesheet"> </head> <body> <div id="app"> <index></index> </div> </body> </html>
نکته: قراردادن تگ اسکریپت 'js/app.js'، تگ '<div id=”app”>' و تگ '<index>' دستورات مهم فایل بالا هستند.
سپس فایل 'resources/js/Index.vue' را ایجاد کنید و کدهای زیر را در آن قرار دهید:
<template> <div id="main"> <header id="header"> <h1> Laravel Vue SPA </h1> </header> <div id="content"> Bienvenue ! </div> </div> </template> <script> export default { data() { return { // } }, components: { // } } </script>
فایل هایی که با Vue ساخته شده اند (مانند فایل بالا) باید کامپایل شده و به کدهای جاوا اسکریپت تبدیل شوند، بدین منظور دستور زیر را در ترمینال اجرا کنید:
npm run watch
دستور بالا یک اسکریپتی را که به صورت مداوم آپدیت می شود و کدهای کامپایل شده جدید در آن قرار می گیرند، تولید می کند. این دستور باعث می شود به صورت خودکار هر نوع تغییر و کامپایلی در فایل های vue سریعا در کدهای نهایی جاوا اسکریپت اعمال شود.
فایل 'resources/js/app.js' را باز کنید و به جای کدهای کنونی، کدهای زیر را در آن وارد کنید:
import './bootstrap' import Vue from 'vue' import Index from './Index' // Set Vue globally window.Vue = Vue // Load Index Vue.component('index', Index) const app = new Vue({ el: '#app' });
بعد از کامپایل شدن با دسترسی به مرورگر، شما می توانید بخش 'Index.vue' را که ساخته شده بود مشاهده کنید (تصویر زیر):
جهت مدیریت احراز هویت و دسترسی صحیح front-side، ما از پکیج 'websanova/vue-auth' استفاده خواهیم کرد. همچنین چند پکیج مرتبط دیگر: vue-router، vue-axios، axios و es6-promise. برای نصب همه این پکیج ها دستور زیر را در ترمینال وارد کنید:
npm i @websanova/vue-auth vue-router vue-axios axios es6-promise
فایل 'resources/js/auth.js' را ایجاد کنید و کدهای زیر در آن قرار دهید:
import bearer from '@websanova/vue-auth/drivers/auth/bearer' import axios from '@websanova/vue-auth/drivers/http/axios.1.x' import router from '@websanova/vue-auth/drivers/router/vue-router.2.x' // Auth base configuration some of this options // can be override in method calls const config = { auth: bearer, http: axios, router: router, tokenDefaultName: 'laravel-vue-spa', tokenStore: ['localStorage'], rolesVar: 'role', registerData: {url: 'auth/register', method: 'POST', redirect: '/login'}, loginData: {url: 'auth/login', method: 'POST', redirect: '', fetchUser: true}, logoutData: {url: 'auth/logout', method: 'POST', redirect: '/', makeRequest: true}, fetchData: {url: 'auth/user', method: 'GET', enabled: true}, refreshData: {url: 'auth/refresh', method: 'GET', enabled: true, interval: 30} } export default config
فایل بالا جهت تنظیمات احراز هویت vue.js بوجود آمده است و برخی توضیحات در رابطه با آن به صورت زیر است:
فایل 'resources/js/router.js' را ایجاد کنید و کدهای زیر را در آن قرار دهید:
import VueRouter from 'vue-router' // Pages import Home from './pages/Home' import Register from './pages/Register' import Login from './pages/Login' import Dashboard from './pages/user/Dashboard' import AdminDashboard from './pages/admin/Dashboard' // Routes const routes = [ { path: '/', name: 'home', component: Home, meta: { auth: undefined } }, { path: '/register', name: 'register', component: Register, meta: { auth: false } }, { path: '/login', name: 'login', component: Login, meta: { auth: false } }, // USER ROUTES { path: '/dashboard', name: 'dashboard', component: Dashboard, meta: { auth: true } }, // ADMIN ROUTES { path: '/admin', name: 'admin.dashboard', component: AdminDashboard, meta: { auth: {roles: 2, redirect: {name: 'login'}, forbiddenRedirect: '/403'} } }, ] const router = new VueRouter({ history: true, mode: 'history', routes, }) export default router
فایل بالا شامل کدهایی است که مسیرهای مختلفی را در سمت frontend پروژه شما تعریف می کند. در واقع کارکردی که این فایل دارد شامل لود کردن بخش ها و تمپلت های مختلف پروژه است که براساس آدرس درخواستی به کاربر نشان داده می شوند. به بخش و تمپلت خاصی که با هر یک از آدرس های موجود در فایل بالا فراخوانده می شود، component می گوییم. پارامتر مهم دیگری که در کدهای بالا بکار رفته است، پارامتر meta می باشد که قوانین دسترسی به هریک از آدرس ها و یا مسیر های موجود در کدهای بالا را مشخص می کند. حالا می خواهیم حالات مختلف فیلد های meta و قاعده متناظر با آن ها را بررسی کنیم:
حالا کامپوننت ها (component) را ایجاد می کنیم. بخش "صفحه اصلی" پروژه یا همان HomePage را با کامپوننت home نشان می دهیم. پس فایل 'resources/js/pages/Home.vue' را می سازیم و کدهای زیر را در آن قرار می دهیم:
<template> <div class="container"> <div class="card card-default"> <div class="card-header">Bienvenue</div> <div class="card-body"> <p> American Main Barbary Coast scuttle hardtack spanker fire ship grapple jack code of conduct port. Port red ensign Shiver me timbers provost salmagundi bring a spring upon her cable pillage cog crow's nest lateen sail. Barbary Coast quarterdeck lass coffer keel hulk mizzen me square-rigged loot. </p> <p> Yardarm starboard keelhaul list schooner prow booty cackle fruit gabion topmast. Plunder shrouds Nelsons folly jack Arr parley warp grog blossom ballast pressgang. Knave crack Jennys tea cup flogging log man-of-war hearties killick long clothes six pounders hulk. </p> </div> </div> </div> </template>
اکنون فایل 'resources/js/pages/Register.vue' را که در واقع کامپوننت Register است، می سازیم:
<template> <div class="container"> <div class="card card-default"> <div class="card-header">Inscription</div> <div class="card-body"> <div class="alert alert-danger" v-if="has_error && !success"> <p v-if="error == 'registration_validation_error'">Erreur(s) de validation, veuillez consulter le(s) message(s) ci-dessous.</p> <p v-else>Erreur, impossible de s'inscrire pour le moment. Si le problème persiste, veuillez contacter un administrateur.</p> </div> <form autocomplete="off" @submit.prevent="register" v-if="!success" method="post"> <div class="form-group" v-bind:class="{ 'has-error': has_error && errors.email }"> <label for="email">E-mail</label> <input type="email" id="email" class="form-control" placeholder="user@example.com" v-model="email"> <span class="help-block" v-if="has_error && errors.email">{{ errors.email }}</span> </div> <div class="form-group" v-bind:class="{ 'has-error': has_error && errors.password }"> <label for="password">Mot de passe</label> <input type="password" id="password" class="form-control" v-model="password"> <span class="help-block" v-if="has_error && errors.password">{{ errors.password }}</span> </div> <div class="form-group" v-bind:class="{ 'has-error': has_error && errors.password }"> <label for="password_confirmation">Confirmation mot de passe</label> <input type="password" id="password_confirmation" class="form-control" v-model="password_confirmation"> </div> <button type="submit" class="btn btn-default">Inscription</button> </form> </div> </div> </div> </template> <script> export default { data() { return { name: '', email: '', password: '', password_confirmation: '', has_error: false, error: '', errors: {}, success: false } }, methods: { register() { var app = this this.$auth.register({ data: { email: app.email, password: app.password, password_confirmation: app.password_confirmation }, success: function () { app.success = true this.$router.push({name: 'login', params: {successRegistrationRedirect: true}}) }, error: function (res) { console.log(res.response.data.errors) app.has_error = true app.error = res.response.data.error app.errors = res.response.data.errors || {} } }) } } } </script>
نکته: من فقط خط ها را در همین کامپوننت بالا مدیریت کردم همان طور که در قسمت قبلی و سمت سرور هم اعتبارسنجی را فقط هنگام Register انجام دادم، اما در شرایط واقعی این اعتبارسنجی ها و مدیریت خطاها باید برای همه کامپوننت ها رخ دهد.
حالا فایل 'resources/js/pages/Login.vue' یا همان کامپوننت Login را ایجاد می کنیم و کدهای زیر را در آن قرار می دهیم:
<template> <div class="container"> <div class="card card-default"> <div class="card-header">Connexion</div> <div class="card-body"> <div class="alert alert-danger" v-if="has_error"> <p>Erreur, impossible de se connecter avec ces identifiants.</p> </div> <form autocomplete="off" @submit.prevent="login" method="post"> <div class="form-group"> <label for="email">E-mail</label> <input type="email" id="email" class="form-control" placeholder="user@example.com" v-model="email" required> </div> <div class="form-group"> <label for="password">Mot de passe</label> <input type="password" id="password" class="form-control" v-model="password" required> </div> <button type="submit" class="btn btn-default">Connexion</button> </form> </div> </div> </div> </template> <script> export default { data() { return { email: null, password: null, has_error: false } }, mounted() { // }, methods: { login() { // get the redirect object var redirect = this.$auth.redirect() var app = this this.$auth.login({ params: { email: app.email, password: app.password }, success: function() { // handle redirection const redirectTo = redirect ? redirect.from.name : this.$auth.user().role === 2 ? 'admin.dashboard' : 'dashboard' this.$router.push({name: redirectTo}) }, error: function() { app.has_error = true }, rememberMe: true, fetchUser: true }) } } } </script>
و سپس کامپوننت Dashboard را با فایل 'resources/js/pages/user/Dashboard.vue' ایجاد می کنیم و کدهای زیر را در آن قرار می دهیم:
<template> <div class="container"> <div class="card card-default"> <div class="card-header">Dashboard</div> <div class="card-body"> Bienvenue </div> </div> </div> </template> <script> export default { data() { return { // } }, components: { // } } </script>
کامپوننت بالا مربوط به کاربران معمولی بود حالا کامپوننت dashbaord ویژه ادمین ها را با فایل 'resources/js/pages/admin/Dashboard.vue' ایجاد می کنیم و کدهای زیر را در آن قرار می دهیم:
<template> <div class="container"> <div class="card card-default"> <div class="card-header">Admin Dashboard</div> <div class="card-body"> Bienvenue sur votre dashboard administrateur </div> </div> </div> </template> <script> export default { mounted() { // } } </script>
حالا بار دیگر به فایل 'resources/js/Index.vue' باز می گردیم و آن را تغییر می دهیم. به جای عبارت "Bienvenue" که یک رشته معمولی بود تگ <router-view> را وارد می کنیم. در واقع بنابر آدرس و مسیر درخواستی کاربر، کامپوننت مناسب با آن مسیر به جای تگ <router-view> نشان داده می شود. اگر کاربر مسیر '/' را وارد کند، در حقیقت صفحه اصلی یا صفحه خانه را درخواست کرده است که با کامپوننت Home نشان می دهیم. کدهای فایل index.vue باید مانند کدهای زیر باشد:
<template> <div id="main"> <header id="header"> <h1> Laravel Vue SPA </h1> </header> <div id="content"> <router-view></router-view> </div> </div> </template> <script> export default { data() { return { // } }, components: { // } } </script>
در نهایت باید فایل "resources/js/app.js" را با استفاده از دستورات مرتبط با استفاده کردن از پکیج های 'vue-auth' و 'vue-router' آپدیت کنیم. پس کدهای زیر را در این فایل خواهیم داشت:
import 'es6-promise/auto' import axios from 'axios' import './bootstrap' import Vue from 'vue' import VueAuth from '@websanova/vue-auth' import VueAxios from 'vue-axios' import VueRouter from 'vue-router' import Index from './Index' import auth from './auth' import router from './router' // Set Vue globally window.Vue = Vue // Set Vue router Vue.router = router Vue.use(VueRouter) // Set Vue authentication Vue.use(VueAxios, axios) axios.defaults.baseURL = `${process.env.MIX_APP_URL}/api` Vue.use(VueAuth, auth) // Load Index Vue.component('index', Index) const app = new Vue({ el: '#app', router });متغی
نکته: متغیر 'axios.defaults.baseURL' از مقدار متغیر 'MIX_APP_URL' در فایل env لاراول استفاده می کند که در فایل env به صورت زیر تعریف می کنیم:
MIX_APP_URL="${APP_URL}"
ضمنا مقدار 'APP_URL' که در بالا استفاده شده است، نیز در فایل env مقدار می گیرد. با اجرای دستور php artisan serve در ترمینال می توانید مقدار این متغیر یا آدرس اصلی پروژه را ببینید، که به صورت پیش فرض در فایل env با مقدار 'http://127.0.0.1:8000' تعریف شده است. نحوه تعریف مقدار این متغیر در فایل env مانند زیر است:
APP_URL=http://127.0.0.1:8000
اگر شما از یک هاست virtual استفاده می کنید، مقدار فیلد بالا تغییر خواهد کرد. به طور کلی وقتی فایل های js با دستورات 'npm run dev' و یا 'npm run watch' کامپایل شوند، مقدار همه متغیر های فایل env، که با '_MIX' شروع می شوند، در سمت frontend دستخوش تغییر خواهند شد.
اکنون صفحه اصلی را رفرش کنید تا محتوای کامپوننت 'Home.vue' را ببینید (تصویر زیر):
اما اگر بخواهید به صفحه '/login' دسترسی پیدا کنید، با یک خطا رو به رو خواهید شد. در واقع در حال حاضر پروژه لاراولی به گونه ای پیکربندی شده است که تنها یک مسیر بر می گرداند و آن همان قسمت index پروژه است که شامل قالب blade با عنوان 'welcome.blade.php' می شود. برای حل این خطا نیاز داریم تا فایل 'routes/web.php' ویرایش شود و خطوط زیر به آن اضافه شوند:
// Route to handle page reload in Vue except for api routes Route::get('/{any?}', function (){ return view('welcome'); })->where('any', '^(?!api\/)[\/\w\.-]*');
با کد بالا url های مختلف در هر صورتی قالب welcome را بر می گردانند و کامپوننت متناظر با درخواست ارائه شده توسط کاربر به کمک vue-router نشان داده می شود. آخرین خط کد بالا جهت مدیریت مسیر api هایی هستند که ممکن است وجود نداشته باشند و یا حالتی برای مدیریت آن ها تعریف نشده باشد، در این صورت با این خط از خطای احتمالی که ممکن است پروژه در این رابطه داشته باشد، جلوگیری می کنیم.
هم اکنون اگر می خواهید به 'login/' دسترسی داشته باشید، فرم ورود (login) نمایان می گردد، و اگر قصد دسترسی به 'dashboard/' و یا 'admin/dashboard/' را داشته باشید، به صفحه ورود بازگردانده می شوید (جهت احراز هویت). تصویر زیر فرم ورود را نشان می دهد:
اگر ورود کنید و احراز هویت انجام شود می توانید به داشبورد متناظر با سطح دسترسی خود بروید (کاربر مدیر یا معمولی). این رفتار در کدهای کامپوننت 'Login.vue' مدیریت می شود:
const redirectTo = redirect ? redirect.from.name : this.$auth.user().role === 2 ? 'admin.dashboard' : 'dashboard'
بر اساس دستور بالا یا کاربر به مسیر قبلی که از آن جا آمده است، redirect می شود یا بر اساس احراز هویت صورت گرفته و براساس نقش و سطح دسترسی که دارا خواهد بود به داشبورد متناسب با خودش منتقل یا redirect می شود.
حالا می خواهیم یک لیست جهت حرکت بین لینک ها و مسیرهای مختلف به پروژه اضافه کنیم. فایل 'resources/js/components/Menu.Vue' ایجاد کنید و کدهای زیر را در آن قرار دهید:
<template> <nav id="nav"> <ul> <!--UNLOGGED--> <li v-if="!$auth.check()" v-for="(route, key) in routes.unlogged" v-bind:key="route.path"> <router-link :to="{ name : route.path }" :key="key"> {{route.name}} </router-link> </li> <!--LOGGED USER--> <li v-if="$auth.check(1)" v-for="(route, key) in routes.user" v-bind:key="route.path"> <router-link :to="{ name : route.path }" :key="key"> {{route.name}} </router-link> </li> <!--LOGGED ADMIN--> <li v-if="$auth.check(2)" v-for="(route, key) in routes.admin" v-bind:key="route.path"> <router-link :to="{ name : route.path }" :key="key"> {{route.name}} </router-link> </li> <!--LOGOUT--> <li v-if="$auth.check()"> <a href="#" @click.prevent="$auth.logout()">Logout</a> </li> </ul> </nav> </template> <script> export default { data() { return { routes: { // UNLOGGED unlogged: [ { name: 'Inscription', path: 'register' }, { name: 'Connexion', path: 'login' } ], // LOGGED USER user: [ { name: 'Dashboard', path: 'dashboard' } ], // LOGGED ADMIN admin: [ { name: 'Dashboard', path: 'admin.dashboard' } ] } } }, mounted() { // } } </script>
سپس به جای محتوانی کنونی، کدهای زیر را در فایل 'resources/js/index.vue' قرار دهید:
<template> <div id="main"> <header id="header"> <h1> <router-link :to="{name: 'home'}"> Laravel Vue SPA </router-link> </h1> <navigationMenu></navigationMenu> </header> <div id="content"> <router-view></router-view> </div> </div> </template> <script> import navigationMenu from './components/Menu.vue' export default { data() { return { // } }, components: { navigationMenu } } </script>
کامپوننت navigation را که مربوط به پیمایش لینک صفحات مختلف است، جهت نمایش زیر title و در header قرار می دهیم و نیز در title جهت redirect به صفحه اصلی، با استفاده از تگ 'router-link' لینکی اضافه می کنیم. نتیجه مانند تصویر زیر خواهد بود:
حالا که سیستم احراز هویت در Backend و Frontend پیاده سازی شد، می خواهیم اطلاعات و یا خروجی api را در Vue نشان دهیم. ابتدا لیستی از کاربران را در داشبورد ادمین نمایش می دهیم.
فایل 'resources/js/components/user-list.vue' را ایجاد کنید و کدهای زیر را در آن قرار دهید:
<template> <div> <h3>Liste de utilisateurs</h3> <div class="alert alert-danger" v-if="has_error"> <p>Erreur, impossible de récupérer la liste des utilisateurs.</p> </div> <table class="table"> <tr> <th scope="col">Id</th> <th scope="col">Nom</th> <th scope="col">Email</th> <th scope="col">Date d'inscription</th> </tr> <tr v-for="user in users" v-bind:key="user.id" style="margin-bottom: 5px;"> <th scope="row">{{ user.id }}</th> <td>{{ user.name }}</td> <td>{{ user.email }}</td> <td>{{ user.created_at}}</td> </tr> </table> </div> </template> <script> export default { data() { return { has_error: false, users: null } }, mounted() { this.getUsers() }, methods: { getUsers() { this.$http({ url: `users`, method: 'GET' }) .then((res) => { this.users = res.data.users }, () => { this.has_error = true }) } } } </script>
نکته: ما از متد '()http$' استفاده می کنیم که به صورت اتوماتیک توکن را Request Header اضافه می کند.
حالا به جای کدهای موجود در فایل 'resources/js/pages/admin/Dashboard.vue' کدهای زیر را قرار دهید:
<template> <div class="container"> <div class="card card-default"> <div class="card-header">Admin Dashboard</div> <div class="card-body"> Bienvenue sur votre dashboard administrateur </div> </div> <div class="card card-default"> <div class="card-header">Liste des utilisateurs</div> <div class="card-body"> <userList></userList> </div> </div> </div> </template> <script> import userList from '../../components/user-list.vue' export default { mounted() { // }, components: { userList } } </script>
با کارهای صورت گرفته هر زمانی یک کاربر با سطح دسترسی ادمین وارد داشبورد می شود، می تواند لیست کاربران را مشاهده کند (تصویر پایین):
نکته: حالا اگر می خواهید با بک کاربر معمولی به داشبوردی که لیست کاربران را نشان دهد، دست پیدا کنید با خطا مواجه خواهید شد زیرا 'api/users/' جهت دسترسی کاربران با نقش مدیر، محافظت شده است!
آموزش پیاده سازی سیستم احراز هویت با سطح دسترسی همین جا به پایان رسید، و امیدوارم پروژه های خوبی را با لاراول و ویو بسازید.
منبع: سایت Medium
در این قسمت، به پرسشهای تخصصی شما دربارهی محتوای مقاله پاسخ داده نمیشود. سوالات خود را اینجا بپرسید.
در این قسمت، به پرسشهای تخصصی شما دربارهی محتوای مقاله پاسخ داده نمیشود. سوالات خود را اینجا بپرسید.
در این قسمت، به پرسشهای تخصصی شما دربارهی محتوای مقاله پاسخ داده نمیشود. سوالات خود را اینجا بپرسید.
در این قسمت، به پرسشهای تخصصی شما دربارهی محتوای مقاله پاسخ داده نمیشود. سوالات خود را اینجا بپرسید.
در این قسمت، به پرسشهای تخصصی شما دربارهی محتوای مقاله پاسخ داده نمیشود. سوالات خود را اینجا بپرسید.
در این قسمت، به پرسشهای تخصصی شما دربارهی محتوای مقاله پاسخ داده نمیشود. سوالات خود را اینجا بپرسید.