การตรวจสอบสิทธิ์ใน Vue.js

เผยแพร่แล้ว: 2022-03-10
สรุปโดยย่อ ↬ ทุกเว็บแอปพลิเคชันที่จัดการข้อมูลเฉพาะผู้ใช้จำเป็นต้องใช้การรับรองความถูกต้อง การรู้วิธีการทำเช่นนี้เป็นสิ่งสำคัญสำหรับนักพัฒนา Vue และนั่นคือสิ่งที่บทความนี้มุ่งเป้าไปที่การเน้นย้ำ บทช่วยสอนนี้จะเป็นประโยชน์สำหรับนักพัฒนามือใหม่ที่ต้องการเรียนรู้เกี่ยวกับการรับรองความถูกต้องใน Vue เพื่อให้สามารถปฏิบัติตามได้ คุณจะต้องมีความรู้เกี่ยวกับ Vue และ Vuex เป็นอย่างดี

การรับรองความถูกต้องเป็นคุณลักษณะที่จำเป็นสำหรับแอปพลิเคชันที่จัดเก็บข้อมูลผู้ใช้ เป็นกระบวนการในการยืนยันตัวตนของผู้ใช้ เพื่อให้มั่นใจว่าผู้ใช้ที่ไม่ได้รับอนุญาตจะไม่สามารถเข้าถึงข้อมูลส่วนตัวได้ — ข้อมูลที่เป็นของผู้ใช้รายอื่น สิ่งนี้นำไปสู่การมีเส้นทางที่จำกัดซึ่งสามารถเข้าถึงได้โดยผู้ใช้ที่ได้รับการรับรองความถูกต้องเท่านั้น ผู้ใช้ที่ผ่านการตรวจสอบสิทธิ์เหล่านี้จะได้รับการตรวจสอบโดยใช้รายละเอียดการเข้าสู่ระบบ (เช่น ชื่อผู้ใช้/อีเมล และรหัสผ่าน) และกำหนดโทเค็นเพื่อใช้ในการเข้าถึงทรัพยากรที่มีการป้องกันของแอปพลิเคชัน

ในบทความนี้ คุณจะได้เรียนรู้เกี่ยวกับ:

  1. การกำหนดค่า Vuex ด้วย Axios
  2. การกำหนดเส้นทาง
  3. การจัดการผู้ใช้
  4. การจัดการโทเค็นที่หมดอายุ

การพึ่งพา

เราจะทำงานกับการพึ่งพาต่อไปนี้ซึ่งช่วยในการรับรองความถูกต้อง:

  • Axios
    สำหรับการส่งและดึงข้อมูลจาก API . ของเรา
  • Vuex
    สำหรับการจัดเก็บข้อมูลที่ได้รับจาก API . ของเรา
  • Vue-เราเตอร์
    สำหรับการนำทางและการป้องกันเส้นทาง

เราจะทำงานร่วมกับเครื่องมือเหล่านี้และดูว่าจะทำงานร่วมกันได้อย่างไรเพื่อมอบฟังก์ชันการตรวจสอบสิทธิ์ที่มีประสิทธิภาพสำหรับแอปของเรา

แบ็กเอนด์ API

เราจะสร้างบล็อกไซต์อย่างง่าย ซึ่งจะใช้ประโยชน์จาก API นี้ คุณสามารถตรวจสอบเอกสารเพื่อดูปลายทางและวิธีส่งคำขอ

จากเอกสาร คุณจะสังเกตเห็นจุดปลายบางจุดที่แนบมาพร้อมกับล็อค นี่เป็นวิธีแสดงว่ามีเพียงผู้ใช้ที่ได้รับอนุญาตเท่านั้นที่สามารถส่งคำขอไปยังปลายทางเหล่านั้นได้ จุดสิ้นสุดที่ไม่จำกัดคือจุดปลาย /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 ที่ใช้ในการส่งคำขอจากเบราว์เซอร์ไปยัง API ตามเอกสาร 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 ไปยังร้านค้าของเรา

โมดูล

โมดูลเป็นส่วนต่างๆ ของร้านค้าของเราที่จัดการงานที่คล้ายคลึงกัน ซึ่งรวมถึง:

  • สถานะ
  • การกระทำ
  • การกลายพันธุ์
  • getters

ก่อนที่เราจะดำเนินการแก้ไขไฟล์ 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 รวมถึงแพ็คเกจ Axios

ดังที่กล่าวไว้ก่อนหน้านี้ คุกกี้โทเค็นการเข้าถึงและข้อมูลที่จำเป็นอื่นๆ ที่ได้รับจาก API จะต้องได้รับการตั้งค่าในส่วนหัวของคำขอสำหรับคำขอในอนาคต เนื่องจากเราจะใช้ 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

ใน dict ของ 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 เพื่อดึงข้อมูลโพสต์ใน API ของเรา และทำการเปลี่ยน 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 ตั้งค่าตัวแปรทั้งหมดกลับเป็น null

Getters

Getters เป็นฟังก์ชันในการรับสถานะ สามารถใช้ในหลายองค์ประกอบเพื่อรับสถานะปัจจุบัน ฟังก์ชัน isAuthenticatated จะตรวจสอบว่า state.user ถูกกำหนดหรือเป็น null และคืนค่า 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 ของคุณ ให้ลบ 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

นี่คือหน้าที่เราต้องการให้ผู้ใช้ของเราสามารถลงทะเบียนในแอปพลิเคชันของเรา เมื่อผู้ใช้กรอกแบบฟอร์ม ข้อมูลจะถูกส่งไปยัง API และเพิ่มไปยังฐานข้อมูล จากนั้นเข้าสู่ระบบ

เมื่อดูที่ API ปลายทาง /register ต้องการ username , full_name และ password ของผู้ใช้ของเรา มาสร้างเพจและแบบฟอร์มเพื่อรับข้อมูลเหล่านี้กัน:

 <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

เรามีวิธีการส่งซึ่งเรียกการดำเนินการ Register ซึ่งเราสามารถเข้าถึงได้โดยใช้ this.Register ส่ง this.form ไป หากไม่พบ error เราจะใช้ this.$router เพื่อส่งผู้ใช้ไปยังหน้าเข้าสู่ระบบ มิฉะนั้นเราตั้งค่า showError เป็นจริง

เมื่อทำอย่างนั้นแล้ว เราสามารถใส่สไตล์ลงไปได้

 <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 เพื่อรับการตรวจสอบสิทธิ์โดย API และลงชื่อเข้าใช้เว็บไซต์ของเรา

 <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 เป็นจริง

ตอนนี้ สไตล์บางอย่าง:

 <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 สิ่งนี้ทำให้ผู้ใช้สามารถเข้าถึงโพสต์และยังทำให้พวกเขาสร้างโพสต์ไปยัง 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>

ในโค้ดด้านบนนี้ เรามีแบบฟอร์มให้ผู้ใช้สร้างโพสต์ใหม่ได้ การส่งแบบฟอร์มควรทำให้การโพสต์ถูกส่งไปยัง API — เราจะเพิ่มวิธีการที่จะทำเช่นนั้นในไม่ช้า นอกจากนี้เรายังมีส่วนที่แสดงโพสต์ที่ได้รับจาก API (ในกรณีที่ผู้ใช้มี) หากผู้ใช้ไม่มีโพสต์ เราก็แสดงข้อความว่าไม่มีโพสต์

ตัวรับ 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. การจัดการผู้ใช้

  • ผู้ใช้ที่ไม่ได้รับอนุญาต
    หากคุณสังเกตเห็นในการกำหนดเส้นทางการโพสต์ของเรา เราได้เพิ่ม meta คีย์เพื่อระบุว่าผู้ใช้ต้องได้รับการตรวจสอบสิทธิ์ ตอนนี้เราจำเป็นต้องมีเราเตอร์ ก่อนที่ router.BeforeEach นำทางแต่ละตัวจะตรวจสอบว่าเส้นทางมีคีย์ 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} จะหยุดผู้ใช้ที่เข้าสู่ระบบจากการเข้าถึงเส้นทางด้วย meta ของ guest
 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.การจัดการโทเค็นที่หมดอายุ (คำขอต้องห้าม)

API ของเราถูกกำหนดให้หมดอายุโทเค็นหลังจาก 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

ทรัพยากร

  • “การจัดการคุกกี้ด้วย Axios,” Aditya Srivastava, Medium
  • “การสร้างการรับรองความถูกต้องของการนำทางใน Vue” ลอรี บาร์ธ จาก Ten Mile Square Blog
  • “การเริ่มต้นใช้งาน Vuex” คู่มืออย่างเป็นทางการ
  • “การตรวจสอบ Vue.js JWT ด้วย Vuex และ Vue Router” BezKoder