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