Cách triển khai xác thực trong ứng dụng Nuxt.js
Trong hướng dẫn này, bạn sẽ triển khai xác thực trong ứng dụng Nuxt.js bằng moduleAuth .   Với mục đích của hướng dẫn này, bạn sẽ sử dụng JWT để xác thực.
Dưới đây là bản demo nhanh về những gì bạn sẽ xây dựng trong hướng dẫn này:

Bạn có thể tìm thấy mã nguồn của ứng dụng này tại GitHub .
 Cảnh báo: Một số gói trong hướng dẫn này hiện chứa các gói phụ thuộc với các lỗ hổng đã biết. Trong cài đặt production , bạn sẽ giải quyết các vấn đề này bằng cách nâng cấp các gói này, tìm các giải pháp thay thế hoặc tạo các version  phân nhánh với các bản sửa lỗi được vá. Tuy nhiên, trong bối cảnh giới hạn của một hướng dẫn, nó cung cấp giá trị giáo dục như hiện tại.
Yêu cầu
Để hoàn thành hướng dẫn này, bạn cần :
- Node.js được cài đặt local mà bạn có thể thực hiện theo Cách cài đặt Node.js và Tạo Môi trường Phát triển Cục bộ .
- Tùy chọn cài đặt Git hợp lệ là bắt buộc để sao chép API, hãy tham khảo Bắt đầu với Git .
Một số thông tin quen thuộc với Vue.js và Nuxt.js có thể có ích. Bạn có thể tham khảo bài đăng này nếu bạn đang bắt đầu với Nuxt.js.
 Hướng dẫn này đã được xác minh với Node v13.13.0, npm v6.14.4, vue v2.6.11 và nuxt v2.12.2.
Bước 1 - Quay một API mẫu
Bạn có thể tự do sử dụng bất kỳ khuôn khổ nào phù hợp nhất với mình. Tuy nhiên, để phát triển nhanh, hướng dẫn này sẽ sao chép một API được xây dựng bằng AdonisJs .
API sử dụng:
API có ba điểm cuối:
-  /register: điểm cuối để đăng ký user
-  /login: endpoint để xác thực user
-  /me: endpoint để lấy thông tin chi tiết về user hiện được xác thực và nó được bảo vệ bởi phần mềm trung gianauth, nghĩa là user phải được xác thực để truy cập vào điểm cuối
Đầu tiên, hãy chạy lệnh sau trong cửa sổ terminal của bạn:
- git clone https://github.com/do-community/jwt-auth-api.git 
Sau đó, chuyển đến folder dự án:
- cd jwt-auth-api 
Và cài đặt các phụ thuộc API:
- npm install 
Lưu ý : Khi chạy cài đặt, bạn có thể gặp sự cố với sqlite3 version  4.0.1 tùy thuộc vào version  Node bạn đang chạy. Tham khảo bảng thay đổi để xác định khả năng tương thích với môi trường của bạn.
 Tại thời điểm xuất bản ban đầu, version  mới nhất của Node là 10. Một tùy chọn là hạ cấp version  Node của bạn xuống 10.20.1 (hiểu rằng nó sắp được hỗ trợ hết hạn sử dụng). Sau đó, chạy npm install .
 Tùy chọn thứ hai là xóa file  package-lock.json file  này sẽ khiến hệ thống tìm kiếm 4.2.0 được hỗ trợ lên đến Node 13. Bạn cũng có thể cần phải hạ cấp version  Node  của bạn  xuống 13.13.0 . Sau đó, chạy npm install .
 Tùy chọn thứ ba sẽ là sửa đổi package.json thành version  sqlite3 được hỗ trợ bởi version  Node hiện tại của bạn, xóa package-lock.json và chạy npm install . Tuy nhiên, tại thời điểm thử nghiệm, 5.0.0 vẫn chưa được phát hành để hỗ trợ Node 14+.
 Các hiện tượng không tương thích khác bao gồm các lỗi sau: TypeError: Cannot read property 'data' of undefined và Error: Cannot find module '[...]/node_modules/sqlite3/lib/binding/[...]/node_sqlite3.node' .
 Tiếp theo, đổi tên .env.example thành .env :
- mv .env.example .env 
Và tạo APP_KEY :
- npx @adonisjs/cli@4.0.12 key:generate 
Bạn nên thấy:
Output- generated: unique APP_KEY 
Sau khi hoàn tất, hãy chạy quá trình di chuyển:
- npx @adonisjs/cli@4.0.12 migration:run 
Bây giờ, bạn có thể khởi động API:
- # ensure that you are in the `jwt-auth-api` project directory 
- npm start 
Bạn có thể truy cập API trên http://127.0.0.1:3333/api . Để phần này chạy trong cửa sổ kỳ hạn trong phần còn lại của hướng dẫn.
Bước 2 - Tạo ứng dụng Nuxt.js
 Bây giờ, bạn có thể tạo một ứng dụng Nuxt.js. Mở cửa sổ  terminal  mới và sử dụng vue-cli để khởi tạo một dự án Vue mới với mẫu Nuxt starter:
- npx vue-cli@2.9.6 init nuxt/starter nuxt-auth 
Lưu ý: Tại thời điểm thử nghiệm, vue-cli không được dùng nữa. @vue/cli là công cụ dòng lệnh hiện tại cho các dự án Vue. Và @vue/cli-init là cách tiếp cận được đề xuất cho các dự án vue-cli kế thừa. Tuy nhiên, create-nuxt-app là cách tiếp cận  được khuyến khích  cho các dự án Nuxt hiện đại.
Tiếp theo, bạn cần chuyển đến folder dự án:
- cd nuxt-auth 
Và cài đặt các phụ thuộc:
npm install Sau đó, bạn có thể chạy ứng dụng:
- npm run dev 
Ứng dụng sẽ chạy trên http://localhost:3000 . Bạn có thể xem ứng dụng trong trình duyệt web để xem ứng dụng Vue mặc định do vue-cli .
Bước 3 - Cài đặt các module Nuxt.js cần thiết
 Bây giờ, hãy cài đặt các module  Nuxt.js mà bạn  cần  cho ứng dụng  của bạn . Bạn sẽ sử dụng mô-đun Nuxt Auth và mô-đun Nuxt Axios , vì module  auth sử dụng Axios bên trong:
- # ensure that you are in the `nuxt-auth` project directory 
- npm install @nuxtjs/auth@4.5.1 @nuxtjs/axios@5.3.1 --save 
Sau khi hoàn tất, hãy mở nuxt.config.js :
- nano nuxt.config.js 
Thêm mã bên dưới vào nuxt.config.js :
module.exports = {   // ...    modules: [     '@nuxtjs/axios',     '@nuxtjs/auth'   ], } Lưu ý:  Đến đây,  các version  Nuxt mới hơn có thể gặp lỗi: Enable vuex store by creating 'store/index.js' . Lỗi này có thể được giải quyết bằng cách thêm index.js trống vào folder  store .
 Tiếp theo, bạn cần  cài đặt  các module . Dán mã bên dưới vào nuxt.config.js :
module.exports = {   // ...    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         }       }     }   } } Tại đây, bạn đặt URL cơ sở mà Axios sẽ sử dụng khi đưa ra yêu cầu. Trong trường hợp của ta , ta đang tham khảo API mẫu mà ta đã cài đặt trước đó.
 Sau đó, bạn xác định các điểm cuối xác thực cho chiến lược local tương ứng với các điểm cuối trên API  của bạn :
-  Khi xác thực thành công, mã thông báo sẽ có sẵn trong phản hồi dưới dạng đối tượng tokenbên trong đối tượngdata.
-  Tương tự, phản hồi từ điểm cuối /mesẽ nằm bên trong một đối tượngdata.
-  Cuối cùng, bạn đặt logoutthànhfalsevì API của bạn không có điểm cuối để đăng xuất. Bạn sẽ chỉ xóa mã thông báo khỏi localStorage khi user đăng xuất.
Bước 4 - Tạo thành phần Navbar
Để tạo kiểu cho ứng dụng của bạn , bạn có thể sử dụng Bulma .
 Mở nuxt.config.js và dán mã bên dưới vào đối tượng link bên trong đối tượng head :
module.exports = {   // ...   head: {     // ...     link [       // ...       {         rel: 'stylesheet',         href: 'https://cdnjs.cloudflare.com/ajax/libs/bulma/0.7.1/css/bulma.min.css'       }     ]   },   // ... } Bây giờ, hãy tạo thành phần Navbar:
- nano components/Navbar.vue 
Và thêm mã sau:
<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>           <template>             <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> Thành phần Navbar chứa các liên kết để login , register , profile và logout .
 Tiếp theo, hãy cập nhật bố cục mặc định để sử dụng thành phần Navbar .
 Mở default.vue :
- nano layouts/default.vue 
Và thay thế nội dung bằng nội dung sau:
<template>   <div>     <Navbar/>     <nuxt/>   </div> </template>  <script> import Navbar from '~/components/Navbar'  export default {   components: {     Navbar   } } </script> Ngoài ra, hãy cập nhật trang chủ.
 Mở index.vue :
- nano pages/index.vue 
Và thay thế nội dung bằng nội dung sau:
<template>   <section class="section">     <div class="container">       <h1 class="title">Nuxt Auth</h1>     </div>   </section> </template>  Đến đây,  bạn sẽ có một ứng dụng hiển thị tiêu đề "Nuxt Auth" với thanh tiêu đề với các liên kết  chuyển : 

Bước 5 - Xử lý đăng ký user
 Bên trong folder  pages , tạo một file  register.vue mới:
- nano pages/register.vue 
Và thêm mã sau:
<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> Điều này chứa một biểu mẫu với ba trường: username , email và password . Mỗi trường được liên kết với một dữ liệu tương ứng trên thành phần. Khi biểu mẫu được gửi, một phương thức register sẽ được gọi. Sử dụng module  Axios, bạn thực hiện một yêu cầu /register điểm cuối /register , truyền dữ liệu  user . Nếu đăng ký thành công, bạn sử dụng loginWith() của module  Auth, sử dụng chiến lược local và chuyển dữ liệu  user  để  user  đăng nhập. Sau đó, bạn chuyển hướng  user  đến trang chủ. Nếu có một lỗi trong quá trình đăng ký, bạn  cài đặt  các error dữ liệu như thông báo lỗi nhận được từ phản ứng API.
Nếu có lỗi, thông báo lỗi được hiển thị bởi thành phần Thông báo.
 Tạo file  Notification.vue mới bên trong components :
- nano components/Notifaction.vue 
Và dán mã bên dưới vào đó:
<template>   <div class="notification is-danger">     {{ message }}   </div> </template>  <script> export default {   name: 'Notification',   props: ['message'] } </script> Thành phần Thông báo chấp nhận một đạo cụ message , đó là thông báo lỗi.
Bây giờ, bạn có thể kiểm tra đăng ký user :


Bước 6 - Xử lý đã đăng nhập User đã đăng nhập và đăng xuất
Sau khi đăng ký thành công, user phải đăng nhập nhưng hiện tại không có cách nào để ứng dụng biết user đã đăng nhập hay chưa. Vì vậy, hãy khắc phục điều đó bằng cách cập nhật thành phần Navbar và thêm một số thuộc tính được tính toán.
 Trước khi bạn làm điều đó, trước tiên hãy kích hoạt cửa hàng Vuex bằng cách tạo index.js bên trong folder  store . Mô-đun Auth lưu trữ trạng thái xác thực của  user  cũng như thông tin chi tiết về  user  bên trong trạng thái Vuex trong một đối tượng auth . Vì vậy, bạn có thể kiểm tra xem  user  có đăng nhập hay không với this.$store.state.auth.loggedIn , sẽ trả về true hoặc false . Tương tự, bạn có thể lấy thông tin chi tiết của  user  với this.$store.state.auth.user , sẽ không có null nếu không có  user  nào đăng nhập.
 Lưu ý: Bạn cũng có thể truy cập vào tình trạng xác thực  user  cũng như các chi tiết  user  trực tiếp với các module  Auth sử dụng this.$auth.loggedIn và this.$auth.user Tương ứng.
Vì bạn có thể cần sử dụng các thuộc tính được tính toán ở nhiều nơi trong ứng dụng của bạn , hãy tạo cửa hàng.
 Mở index.js :
- nano store/index.js 
Và dán mã bên dưới vào đó:
export const getters = {   isAuthenticated(state) {     return state.auth.loggedIn   },    loggedInUser(state) {     return state.auth.user   } } Ở đây, bạn tạo ra hai getters. Cái đầu tiên ( isAuthenticated ) sẽ trả về trạng thái xác thực của  user  và cái thứ hai ( loggedInUser ) sẽ trả về thông tin chi tiết hoặc  user  đã đăng nhập.
 Tiếp theo, hãy cập nhật thành phần Navbar để sử dụng các getters. Thay thế nội dung của các components/Navbar.vue bằng nội dung sau:
<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> Bạn tạo các thuộc tính được tính toán bằng cách sử dụng toán tử spread ( ... ) để   extract   getters từ mapGetters . Sau đó, sử dụng isAuthenticated , bạn hiển thị menu  user  hoặc các liên kết để login hoặc register tùy thuộc vào việc  user  đã đăng nhập hay chưa. Ngoài ra, bạn sử dụng loggedInUser để hiển thị tên  user  đã được xác thực.
Bây giờ, nếu bạn làm mới ứng dụng của bạn , bạn sẽ thấy một cái gì đó tương tự như bên dưới:

Bước 7 - Xử lý đăng nhập của user
Bây giờ, hãy cho phép user cũ khả năng đăng nhập.
 Tạo một file  login.vue mới bên trong folder  pages :
nano pages/login.vue Và dán mã bên dưới vào đó:
<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> Điều này khá giống với trang register . Biểu mẫu có hai trường: email và password . Khi biểu mẫu được gửi, một phương thức login sẽ được gọi. Sử dụng đăng nhập module  loginWith() và truyền dữ liệu  user , bạn đăng nhập  user . Nếu xác thực thành công, bạn chuyển hướng  user  đến trang chủ. Nếu không, hãy đặt error thành thông báo lỗi nhận được từ phản hồi API.   , bạn đang sử dụng thành phần Thông báo từ trước đó để hiển thị thông báo lỗi. 

Bước 8 - Hiển thị profile user
Hãy cho phép user đã đăng nhập để xem profile của họ.
 Tạo một file  profile.vue mới bên trong folder  pages :
- nano pages/profile.vue 
Và dán mã bên dưới vào đó:
<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> Lưu ý cách bạn đang sử dụng getter loggedInUser từ trước đó để hiển thị hiển thị chi tiết  user .
Nhấp vào liên kết Tiểu sử của tôi sẽ dẫn đến trang Tiểu sử của tôi được hiển thị.

Bước 9 - Đăng xuất user
Cập nhật liên kết đăng xuất bên trong thành phần Navbar.
 Mở Navbar.vue :
- nano components/Navbar.vue 
Sửa đổi liên kết đăng xuất để sử dụng @click="logout" :
// ... <div class="navbar-dropdown">   <nuxt-link class="navbar-item" to="/profile">My Profile</nuxt-link>   <hr class="navbar-divider"/>   <a class="navbar-item"  @click="logout">Logout</a> </div> // ... Khi liên kết đăng xuất được nhấp vào, nó sẽ kích hoạt phương thức logout .
 Tiếp theo, hãy thêm phương thức logout bên trong phần script của thành phần Navbar:
// ...  export default {   // ...   methods: {     async logout() {       await this.$auth.logout();     },   }, } Bạn gọi logout() của module  Auth. Thao tác này sẽ xóa mã thông báo của  user  khỏi localstorage và chuyển hướng  user  đến trang chủ.
Bước 10 - Hạn chế trang profile
 Như bây giờ, bất kỳ ai cũng có thể truy cập trang profile . Và nếu  user  chưa đăng nhập sẽ dẫn đến lỗi. 

 Để khắc phục điều này, bạn cần hạn chế trang profile  chỉ những  user  đã đăng nhập. Thật may mắn cho  ta , bạn có thể đạt được điều đó với module  Auth. Mô-đun Auth đi kèm với một phần mềm trung gian auth mà bạn có thể sử dụng trong trường hợp này.
 Vì vậy, hãy thêm phần mềm trung gian auth vào trang profile . Cập nhật phần script như bên dưới:
// ...  export default {   middleware: 'auth',   // ... } Bây giờ khi  user  chưa đăng nhập cố gắng truy cập trang profile ,  user  sẽ được chuyển hướng đến trang login .
Bước 11 - Tạo phần mềm trung gian cho khách
, ngay cả khi là user đã đăng nhập, bạn vẫn có thể truy cập các trang đăng nhập và đăng ký. Một cách để khắc phục điều đó là hạn chế các trang đăng nhập và đăng ký chỉ với những user chưa đăng nhập. Bạn có thể thực hiện bằng cách tạo một phần mềm trung gian dành cho khách.
 Bên trong folder  middleware , hãy tạo file  guest.js mới:
- nano middleware/guest.js 
Và dán mã bên dưới vào đó:
export default function ({ store, redirect }) {   if (store.state.auth.loggedIn) {     return redirect('/')   } } Phần mềm trung gian chấp nhận ngữ cảnh làm đối số đầu tiên của nó. Vì vậy, bạn   extract   store và redirect từ ngữ cảnh. Sau đó, bạn kiểm tra xem  user  đã đăng nhập chưa rồi chuyển hướng  user  đến trang chủ. Nếu không, bạn cho phép thực hiện yêu cầu bình thường.
 Tiếp theo, hãy sử dụng phần mềm trung gian này. Cập nhật phần script của cả login và register như bên dưới:
// ...  export default {   middleware: 'guest',   // ... } Bây giờ, mọi thứ sẽ hoạt động như mong đợi.
Kết luận
Trong hướng dẫn này, bạn đã xem cách triển khai xác thực trong ứng dụng Nuxt.js bằng module Auth. Bạn cũng đã thấy cách để giữ cho dòng xác thực mượt mà bằng cách sử dụng phần mềm trung gian.
Để tìm hiểu thêm về module Auth, hãy xem tài liệu .
Nếu bạn muốn tìm hiểu thêm về Vue.js, hãy xem trang chủ đề Vue.js của ta để biết các bài tập và dự án lập trình.
Các tin liên quan
 

