介绍
在本教程中,您将使用模块在Nuxt.js应用程序中实现身份验证Auth
。
出于本教程的目的,您将JWT
用于身份验证。
下面是您将在本教程中构建的内容的快速演示:
您可以在 GitHub 上找到此应用程序的源代码。
警告:本教程中的几个包现在包含具有已知漏洞的依赖项。在生产环境中,您可以通过升级这些包、寻找替代方案或创建带有修补程序的分叉版本来解决这些问题。但是,在教程的有限上下文中,它按原样提供了教育价值。
先决条件
要完成本教程,您需要:
- Node.js 安装在本地,您可以按照如何安装 Node.js 和创建本地开发环境来完成。
- 克隆 API 需要有效的 Git 安装,请参阅Git 入门。
熟悉 Vue.js 和 Nuxt.js 可能会有所帮助。如果你开始使用 Nuxt.js,你可以参考这篇文章。
本教程已通过 Node v13.13.0、npm v6.14.4、vue
v2.6.11 和nuxt
v2.12.2 验证。
第 1 步 – 启动示例 API
您可以自由使用最适合您的任何框架。但是,为了快速开发,本教程将克隆一个使用AdonisJs构建的 API 。
API 使用:
- 用于身份验证的JWT(JSON Web 令牌)
- SQLite
- 启用CORS
API 具有三个端点:
/register
: 用户注册端点/login
: 用于验证用户的端点/me
: 用于获取当前经过身份验证的用户的详细信息的端点,它受auth
中间件保护,这意味着用户必须经过身份验证才能访问端点
首先,在终端窗口中运行以下命令:
- git clone https://github.com/do-community/jwt-auth-api.git
然后,导航到项目目录:
- cd jwt-auth-api
并安装 API 依赖项:
- npm install
注意:运行安装时,您可能会遇到sqlite3
版本问题,4.0.1
具体取决于您运行的 Node 版本。请参阅变更日志以确定与您的环境的兼容性。
在最初发布时,Node 的最新版本是 10。一种选择是将您的 Node 版本降级到10.20.1
(了解它即将结束生命支持)。然后,运行npm install
。
第二个选项是删除package-lock.json
文件,这将导致系统查找4.2.0
最高支持 Node 13 的文件。您可能还需要将 Node 版本降级到13.13.0
. 然后,运行npm install
。
第三种选择是修改package.json
为sqlite3
您当前版本的 Node 支持的版本,删除package-lock.json
并运行npm install
。但是,在测试时,5.0.0
尚未发布以处理 Node 14+ 支持。
不兼容的其他症状包括以下错误:TypeError: Cannot read property 'data' of undefined
和Error: Cannot find module '[...]/node_modules/sqlite3/lib/binding/[...]/node_sqlite3.node'
.
接下来,重命名.env.example
为.env
:
- mv .env.example .env
并生成一个APP_KEY
:
- npx @adonisjs/cli@4.0.12 key:generate
你应该看到:
Output- generated: unique APP_KEY
一旦完成,让我们运行迁移:
- npx @adonisjs/cli@4.0.12 migration:run
现在,您可以启动 API:
- # ensure that you are in the `jwt-auth-api` project directory
- npm start
您可以在 上访问 API http://127.0.0.1:3333/api
。在本教程的剩余时间里,让它在终端窗口中运行。
第 2 步 – 创建 Nuxt.js 应用程序
现在,您可以创建一个 Nuxt.js 应用程序。打开一个新的终端窗口并使用vue-cli
Nuxt starter 模板来初始化一个新的 Vue 项目:
- npx [email protected] init nuxt/starter nuxt-auth
注意:在测试时,vue-cli
已弃用。@vue/cli
是当前 Vue 项目的命令行工具。并且@vue/cli-init
是遗留vue-cli
项目的推荐方法。但是,这create-nuxt-app
是现代 Nuxt 项目的推荐方法。
接下来,您需要导航到项目目录:
- cd nuxt-auth
并安装依赖项:
npm install
然后,您可以启动该应用程序:
- npm run dev
该应用程序应该在http://localhost:3000
. 您可以在 Web 浏览器中查看该应用程序,以查看由vue-cli
.
第 3 步 – 安装必要的 Nuxt.js 模块
现在,让我们安装您的应用程序所需的 Nuxt.js 模块。您将使用Nuxt Auth 模块和Nuxt Axios 模块,因为该auth
模块在内部使用 Axios:
- # ensure that you are in the `nuxt-auth` project directory
- npm install @nuxtjs/auth@4.5.1 @nuxtjs/axios@5.3.1 --save
完成后,打开nuxt.config.js
:
- nano nuxt.config.js
将下面的代码添加到nuxt.config.js
:
module.exports = {
// ...
modules: [
'@nuxtjs/axios',
'@nuxtjs/auth'
],
}
注意:此时,较新版本的 Nuxt 可能会遇到错误:Enable vuex store by creating 'store/index.js'
. 可以通过向目录中添加一个空index.js
文件来store
解决此错误。
接下来,您需要设置模块。将下面的代码粘贴到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
}
}
}
}
}
在这里,您设置 Axios 在发出请求时将使用的基本 URL。在我们的例子中,我们引用了我们之前设置的示例 API。
然后,为与local
API 上的策略相对应的策略定义身份验证端点:
- 成功验证后,令牌将作为
token
对象内的data
对象出现在响应中。 - 同样,来自
/me
端点的响应将在data
对象内部。 - 最后,您设置
logout
为 ,false
因为您的 API 没有用于注销的端点。当用户注销时,您只需从 localStorage 中删除令牌。
第 4 步 – 创建导航栏组件
要设计您的应用程序,您可以使用Bulma。
nuxt.config.js
在link
对象内部的head
对象中打开并粘贴以下代码:
module.exports = {
// ...
head: {
// ...
link [
// ...
{
rel: 'stylesheet',
href: 'https://cdnjs.cloudflare.com/ajax/libs/bulma/0.7.1/css/bulma.min.css'
}
]
},
// ...
}
现在,让我们创建 Navbar 组件:
- nano components/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>
导航栏组件包含链接login
,register
,profile
,和logout
。
接下来,让我们更新默认布局以使用该Navbar
组件。
打开default.vue
:
- nano layouts/default.vue
并将内容替换为以下内容:
<template>
<div>
<Navbar/>
<nuxt/>
</div>
</template>
<script>
import Navbar from '~/components/Navbar'
export default {
components: {
Navbar
}
}
</script>
另外,让我们更新主页。
打开index.vue
:
- nano pages/index.vue
并将内容替换为以下内容:
<template>
<section class="section">
<div class="container">
<h1 class="title">Nuxt Auth</h1>
</div>
</section>
</template>
此时,您应该有一个显示标题的应用程序,"Nuxt Auth"
带有带有导航链接的标题栏:
步骤 5 — 处理用户注册
在pages
目录中,创建一个新register.vue
文件:
- nano pages/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>
这包含三个域的形式:username
,email
,和password
。每个字段都绑定到组件上的相应数据。提交表单时,register
将调用一个方法。使用 Axios 模块,您可以向/register
端点发出 post 请求,并传递用户数据。如果注册成功,您使用 Auth 模块的loginWith()
,使用该local
策略并传递用户数据来登录用户。然后,您将用户重定向到主页。如果注册过程中出现错误,则将error
数据设置为从 API 响应中获取的错误消息。
如果出现错误,则错误消息由 Notification 组件显示。
Notification.vue
在里面创建一个新文件components
:
- nano components/Notifaction.vue
并将下面的代码粘贴到其中:
<template>
<div class="notification is-danger">
{{ message }}
</div>
</template>
<script>
export default {
name: 'Notification',
props: ['message']
}
</script>
Notification 组件接受一个message
props,它是错误信息。
现在,您可以测试用户注册:
第 6 步 – 处理登录和注销的 LoggedUsers
注册成功后,用户应该已登录,但目前应用程序无法知道用户是否已登录。所以让我们通过更新 Navbar 组件并添加一些计算属性来解决这个问题。
在你这样做之前,让我们首先通过index.js
在store
目录中创建一个文件来激活 Vuex 商店。Auth 模块将用户身份验证状态以及 Vuex 状态内的用户详细信息存储在一个auth
对象中。因此,您可以使用 来检查用户是否已登录this.$store.state.auth.loggedIn
,这将返回true
或false
。同样,您可以使用 获取用户的详细信息this.$store.state.auth.user
,null
如果没有用户登录,则为。
注意:您还可以分别使用this.$auth.loggedIn
和直接通过 Auth 模块访问用户身份验证状态以及用户详细信息this.$auth.user
。
由于您可能希望在应用程序的多个位置使用计算属性,让我们创建商店 getter。
打开index.js
:
- nano store/index.js
并将下面的代码粘贴到其中:
export const getters = {
isAuthenticated(state) {
return state.auth.loggedIn
},
loggedInUser(state) {
return state.auth.user
}
}
在这里,您创建了两个 getter。第一个 ( isAuthenticated
) 将返回用户的身份验证状态,第二个 ( loggedInUser
) 将返回详细信息或登录用户。
接下来,让我们更新 Navbar 组件以使用 getter。将 的内容替换components/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>
您可以通过使用扩展运算符 ( ...
) 从 中提取 getter 来创建计算属性mapGetters
。然后使用isAuthenticated
中,显示用户菜单或链接login
或register
根据用户是否正在或没有登录。此外,您还loggedInUser
用于显示经过身份验证的用户用户名。
现在,如果您刷新应用程序,您应该会看到类似于以下内容的内容:
第 7 步 – 处理用户登录
现在,让我们允许返回用户登录。
login.vue
在pages
目录中创建一个新文件:
nano pages/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>
这与register
页面非常相似。该表单包含两个字段:email
和password
。提交表单时,login
将调用一个方法。使用 Auth 模块loginWith()
并传递用户数据,您登录用户。如果身份验证成功,您将用户重定向到主页。否则,设置error
为从 API 响应中获取的错误消息。同样,您使用之前的 Notification 组件来显示错误消息。
步骤 8 — 显示用户配置文件
让我们允许登录用户查看他们的个人资料。
profile.vue
在pages
目录中创建一个新文件:
- nano pages/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>
请注意您之前是如何使用loggedInUser
getter 来显示用户详细信息的。
单击“我的个人资料”链接应该会显示“我的个人资料”页面。
步骤 9 — 注销用户
更新导航栏组件内的注销链接。
打开Navbar.vue
:
- nano components/Navbar.vue
修改注销链接以使用@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>
// ...
当注销链接被点击时,它会触发一个logout
方法。
接下来,让我们logout
在 Navbar 组件的脚本部分添加方法:
// ...
export default {
// ...
methods: {
async logout() {
await this.$auth.logout();
},
},
}
您调用logout()
Auth 模块的 。这将从本地存储中删除用户的令牌并将用户重定向到主页。
第 10 步 – 限制个人资料页面
就目前而言,任何人都可以访问该profile
页面。如果用户未登录,则会导致错误。
要解决此问题,您需要将个人资料页面限制为仅登录用户。幸运的是,您可以使用 Auth 模块实现这一点。Auth 模块带有一个auth
中间件,您可以在这种情况下使用它。
所以让我们将auth
中间件添加到profile
页面中。更新script
部分如下:
// ...
export default {
middleware: 'auth',
// ...
}
现在,当未登录的用户尝试访问该profile
页面时,该用户将被重定向到该login
页面。
第 11 步 – 创建访客中间件
同样,即使作为登录用户,您仍然可以访问登录和注册页面。解决此问题的一种方法是将登录和注册页面仅限于未登录的用户。您可以通过创建访客中间件来做到这一点。
在middleware
目录中,创建一个新guest.js
文件:
- nano middleware/guest.js
并将下面的代码粘贴到其中:
export default function ({ store, redirect }) {
if (store.state.auth.loggedIn) {
return redirect('/')
}
}
中间件接受上下文作为它的第一个参数。所以你从上下文中提取store
和redirect
。然后,您检查用户是否已登录,然后将用户重定向到主页。否则,您允许正常执行请求。
接下来,让我们利用这个中间件。更新script
两部分login
,并register
如下:
// ...
export default {
middleware: 'guest',
// ...
}
现在,一切都会按预期进行。
结论
在本教程中,您了解了如何使用 Auth 模块在 Nuxt.js 应用程序中实现身份验证。您还看到了如何通过使用中间件来保持身份验证流程顺畅。
要了解有关 Auth 模块的更多信息,请查看文档。
如果您想了解有关 Vue.js 的更多信息,请查看我们的 Vue.js 主题页面以获取练习和编程项目。