Thứ năm, 10/09/2020 | 00:00 GMT+7

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 module Auth .

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:

Ảnh động gif của ứng dụng hiển thị  user  đang đăng nhập

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 :

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 gian auth , 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 undefinedError: 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 Authmô-đ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 :

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 :

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 token bên trong đối tượng data .
  • Tương tự, phản hồi từ điểm cuối /me sẽ nằm bên trong một đối tượng data .
  • Cuối cùng, bạn đặt logout thành false vì 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 :

nuxt.config.js
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:

thành phần / 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>           <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 , profilelogout .

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:

layouts / default.vue
<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:

trang / index.vue
<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 :

Trang ứng dụng có tiêu đề và thanh tiêu đề

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:

trang / 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> 

Điều này chứa một biểu mẫu với ba trường: username , emailpassword . 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 đó:

thành phần / Notification.vue
<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 :

Đăng ký trang với các trường Tên  user , Email và Mật khẩu

Đăng ký trang nhưng có thông báo cho  user  rằng có lỗi

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.loggedInthis.$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 đó:

store / index.js
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:

thành phần / 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> 

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:

Trang ứng dụng có tên  user  của  user  trong tiêu đề

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 đó:

trang / 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> 

Điều này khá giống với trang register . Biểu mẫu có hai trường: emailpassword . 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.

Trang chào mừng ứng dụng Quay lại chứa hai trường: email và password

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 đó:

trang / 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> 

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ị.

Trang Hồ sơ của tôi hiển thị tên  user  và email

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" :

thành phần / Navbar.vue
// ... <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:

thành phần / Navbar.vue
// ...  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.

TypeError trên trang ứng dụng

Để 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:

trang / profile.vue
// ...  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 đó:

middleware / guest.js
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 storeredirect 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ả loginregister như bên dưới:

trang / login.vue và trang / register.vue
// ...  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.


Tags:

Các tin liên quan