如何构建可用于生产的 Vue 身份验证
入门
使用 Amplify
编写代码
第 2 部分 - 构建自定义身份验证流程。
结论
要随时查看该项目的最终代码,请单击此处。
在本教程中,您将学习如何使用Vue Router、AWS Amplify和Amazon Cognito在您的 Vue 应用程序中构建真实的身份验证流程。虽然我们将使用的身份提供商是 AWS 和 Amazon Cognito,但我们应用程序的基本设计将与提供商无关,这意味着您应该能够使用您选择的提供商进行操作。
身份验证概述
如果您曾经尝试过推出自己的身份验证服务和实现(前端和后端),那么您已经意识到随之而来的痛苦。
值得庆幸的是,如今我们拥有众多出色的身份服务和提供商,可以为我们处理所有这些事务。您可能已经熟悉Auth0、Okta和Amazon Cognito等服务,它们在幕后完成了繁重的工作,因此您无需亲自实现用户和身份管理,而这却是大多数现代应用程序的必备功能。
在本教程中,您将学习如何管理用户注册、登录、忘记密码和 MFA 等所有流程。您还将学习如何使用 Vue Router 实现受保护的客户端路由,以便定义哪些路由可以公开,哪些路由需要仅对登录用户进行保护。
在本教程结束时,您将很好地掌握如何构建和部署启用企业级安全性和身份验证的 Vue 应用程序。
入门
创建 Vue 项目
我们要做的第一件事是使用 Vue CLI 搭建一个新的 Vue 应用程序。如果您尚未安装 Vue CLI,请点击此处按照安装说明进行操作。
~ vue create vue-auth
? Please pick a preset: default
cd vue-auth
一旦项目创建完毕并且您进入目录,让我们使用 npm 或 yarn 安装我们需要的必要依赖项:
~ yarn add vue-router aws-amplify @aws-amplify/ui-vue
什么是
aws-amplify-vue
?AWS Amplify 具有特定于平台的组件,使我们能够快速搭建并启动并运行重要功能,如身份验证流程、图像上传等。
创建文件夹结构
现在让我们创建用于实现身份验证流程的文件。在src目录中,创建以下文件:
~ touch router.js components/Auth.vue components/Home.vue components/Profile.vue components/Protected.vue
使用 Amplify
安装 Amplify CLI
要添加身份验证服务,我们将使用 AWS Amplify CLI。现在就开始安装它吧:
~ npm install -g @aws-amplify/cli
接下来,我们需要配置 CLI。为此,请运行以下命令:
~ amplify configure
有关如何配置 CLI 的完整演练,请观看此视频。
现在我们已经创建了项目并安装了 CLI,接下来就可以创建要使用的身份验证服务了。为此,我们将初始化一个新的 Amplify 项目,然后为其添加身份验证功能。
初始化 Amplify 项目
要初始化新的 Amplify 项目,请运行以下init
命令:
~ amplify init
这
init
将初始化项目并引导您完成一些步骤来配置项目名称、环境和其他构建设置。选择一个项目和环境名称,然后在其余问题中选择默认值。
添加身份验证服务
现在 Amplify 项目已经初始化,我们可以添加身份验证服务:
~ amplify add auth
? Do you want to use the default authentication and security configuration? Default configuration
? How do you want users to be able to sign in? Username
? Do you want to configure advanced settings? No
~ amplify push
默认安全配置将为我们提供此项目的智能默认值。如果您想要自定义配置,可以选择
No
,也可以稍后运行amplify update auth
。
成功运行后amplify push
,身份验证已成功创建,我们现在可以开始编写代码!
您应该注意到,现在您的src目录中有一个名为aws-exports.js的文件(包含基本项目配置),并且您的根目录中有一个名为amplify的文件夹(包含详细的项目配置和自定义代码)。
编写代码
我们将通过两种方式实现身份验证:
- 第 1 部分 - 使用
amplify-authenticator
AWS Amplify Vue 的预配置组件快速启动并运行我们的身份验证流程。 - 第 2 部分 - 构建完全自定义的身份验证流程。
第 1 部分 - 使用预配置amplify-authenticator
组件
接下来,我们需要更新main.js 文件,以配置 Vue 项目,使其能够与 Amplify 和我们新的aws-exports.js文件协同工作。我们还需要让应用程序知道下一步将要创建的路由器。
src/main.js
// src/main.js
import Vue from 'vue'
import VueRouter from 'vue-router'
import Amplify from 'aws-amplify'
import '@aws-amplify/ui-vue'
import config from './aws-exports';
import App from './App'
import router from './router'
Amplify.configure(config)
Vue.use(VueRouter)
Vue.config.productionTip = false
new Vue({
render: h => h(App),
router
}).$mount('#app')
接下来,我们将配置路由器。这里我们还会放置受保护路由的自定义逻辑。
src/router.js
// src/router.js
import VueRouter from 'vue-router'
import { Auth } from 'aws-amplify'
import Home from './components/Home'
import Profile from './components/Profile'
import AuthComponent from './components/Auth'
import Protected from './components/Protected'
const routes = [
{ path: '/', component: Home },
{ path: '/auth', component: AuthComponent },
{ path: '/protected', component: Protected, meta: { requiresAuth: true} },
{ path: '/profile', component: Profile, meta: { requiresAuth: true} }
]
const router = new VueRouter({
routes
})
router.beforeResolve((to, from, next) => {
if (to.matched.some(record => record.meta.requiresAuth)) {
Auth.currentAuthenticatedUser().then(() => {
next()
}).catch(() => {
next({
path: '/auth'
});
});
}
next()
})
export default router
src/router.js 的详细信息
- 我们导入 Vue 和 VueRouter
- 我们导入路线中要用到的组件
- 我们定义了一个路由数组。我们添加了一个额外的元属性,使用名为 的布尔值来指定需要身份验证的路由
requiresAuth
。 - 我们创建路由器变量
- 我们使用Vue Router 中的beforeResolve守卫(它会在导航确认之前被调用)来检查用户是否已通过身份验证。如果已通过身份验证,我们将允许他们进入下一个路由。如果未通过身份验证,我们将他们重定向到注册页面 ( /auth )。
接下来,让我们创建身份验证组件。
src/components/Auth.vue
// src/components/Auth.vue
<template>
<div class="auth">
<amplify-authenticator></amplify-authenticator>
</div>
</template>
<script>
export default {
name: 'auth'
}
</script>
<style>
.auth {
margin: 0 auto;
width: 460px;
}
</style>
src/components/Auth.vue 的详细信息
这是一个非常基础的组件,但实际上却功能强大!这个amplify-authenticator
Vue 组件实际上会为我们搭建整个身份验证流程(注册、登录和忘记密码)。
现在我们将更新App组件。该组件将执行以下操作:
- 显示导航链接
- 渲染路由器
- 保存大部分用于监听用户登录/注销的身份验证逻辑。
src/App.vue
// src/App.vue
<template>
<div id='app'>
<div class='nav'>
<router-link tag="p" to="/">
<a>Home</a>
</router-link>
<router-link tag="p" to="/profile">
<a>Profile</a>
</router-link>
<router-link tag="p" to="/protected">
<a>Protected</a>
</router-link>
<router-link tag="p" to="/auth" v-if="!signedIn">
<a>Sign Up / Sign In</a>
</router-link>
</div>
<router-view></router-view>
<div class='sign-out'>
<amplify-sign-out v-if="signedIn"></amplify-sign-out>
</div>
</div>
</template>
<script>
import { Auth, Hub } from 'aws-amplify'
export default {
name: 'app',
data() {
return {
signedIn: false
}
},
beforeCreate() {
Hub.listen('auth', data => {
console.log('data:', data)
const { payload } = data
if (payload.event === 'signIn') {
this.signedIn = true
this.$router.push('/profile')
}
if (payload.event === 'signOut') {
this.$router.push('/auth')
this.signedIn = false
}
})
Auth.currentAuthenticatedUser()
.then(() => {
this.signedIn = true
})
.catch(() => this.signedIn = false)
}
}
</script>
<style>
.nav {
display: flex;
}
.nav p {
padding: 0px 30px 0px 0px;
font-size: 18px;
color: #000;
}
.nav p:hover {
opacity: .7;
}
.nav p a {
text-decoration: none;
}
.sign-out {
width: 160px;
margin: 0 auto;
}
</style>
src/components/App.vue 的详细信息
- 如果用户已登录,我们将使用该
amplify-sign-out
组件来呈现退出按钮。 - 我们创建一个名为的布尔值
signedIn
,并在应用程序加载时将其设置为 false - 在生命周期方法中,我们使用API
beforeCreate
监听事件。如果检测到登录,我们会将其重定向至查看个人资料,并设置为 true。如果检测到退出,我们会将其重定向至路由,并设置为 false。authState
Hub
signedIn
/auth
signedIn
- 当应用程序加载时,我们还会调用来
Auth.currentAuthenticatedUser
检查用户是否已登录并signedIn
适当地设置变量。
接下来,让我们添加Profile组件。
这个基本组件将显示我们将使用 Amplify 检索的用户名。
src/components/Profile.vue
// src/components/Profile.vue
<template>
<h1>Welcome, {{user.username}}</h1>
</template>
<script>
import { Auth } from 'aws-amplify'
export default {
name: 'Profile',
data() {
return {
user: {}
}
},
beforeCreate() {
Auth.currentAuthenticatedUser()
.then(user => {
this.user = user
})
.catch(() => console.log('not signed in...'))
}
}
</script>
src/components/Profile.vue 的详细信息
关于此组件,需要注意的一点是,我们通过调用该Auth.currentAuthenticatedUser
方法来检索用户信息。该方法将返回一个user
包含已登录用户元数据的对象,如果用户未登录,则会出错。
现在我们可以创建最后两个基本组件。
src/components/Home.vue
// src/components/Home.vue
<template>
<h1>Home</h1>
</template>
<script>
export default {
name: 'home',
}
</script>
src/组件/受保护的.vue
// src/components/Protected.vue
<template>
<h1>Hello from protected route!</h1>
</template>
<script>
export default {
name: 'protected',
}
</script>
测试一下
我们的应用程序的第 1 部分已经完成,让我们测试一下:
~ npm run serve
当应用加载时,我们应该只能查看Home路由。如果我们尝试导航到某个受保护的路由,我们应该被重定向到身份验证屏幕。
一旦我们登录,我们就应该能够查看受保护的页面。
您会注意到用户信息已被持久化。这由 Amplify 客户端库为您处理。要退出,您必须明确点击我们渲染的退出按钮,或使用Auth.signOut
Auth 类别中的方法。
现在我们已经启动并运行了,下一步是什么?好吧,amplify-authenticator
组件可以在一定程度上进行自定义,以控制渲染的字段以及样式(要了解如何操作,请查看此处的文档)。但是,如果我们想要一个完全自定义的身份验证流程呢?我们现在就开始吧。
第 2 部分 - 构建自定义身份验证流程。
现在身份验证已经正常工作,接下来让我们更新一些功能,使其能够自定义。目前,我们所有的身份验证功能都存储在Auth.vue中。在这个文件中,我们使用该amplify-authenticator
组件搭建整个身份验证流程。接下来,让我们更新应用,使其支持自定义身份验证。
我们需要做的第一件事是在我们的组件目录中创建几个新文件,一个用于登录用户,一个用于注册新用户。
touch src/components/SignIn.vue src/components/SignUp.vue
接下来,让我们更新Auth.vue以使用新文件并添加一些新功能。在此文件中,我们将根据某些组件状态渲染SignUp和SignIn组件。我们还将渲染一个链接,允许我们在注册和登录状态之间切换:
src/components/Auth.vue
// src/components/Auth.vue
<template>
<div class="auth">
<sign-up :toggle='toggle' v-if="formState === 'signUp'"></sign-up>
<sign-in v-if="formState === 'signIn'"></sign-in>
<p v-on:click="toggle" class="toggle">{{ formState === 'signUp' ?
'Already sign up? Sign In' : 'Need an account? Sign Up'
}}</p>
</div>
</template>
<script>
import SignUp from './SignUp'
import SignIn from './SignIn'
export default {
name: 'app',
data() {
return {
formState: 'signUp'
}
},
methods: {
toggle() {
this.formState === 'signUp' ? this.formState = 'signIn' : this.formState = 'signUp'
}
},
components: {
SignUp,
SignIn
}
}
</script>
<style>
.auth {
margin: 0 auto;
width: 460px;
}
.toggle {
cursor: pointer;
font-size: 18px;
}
</style>
src/components/Auth.vue 的详细信息
这里主要考虑的是,我们导入了两个新组件,并根据formState
布尔值来渲染它们。目前还没有什么特别有趣的事情。
接下来,让我们创建注册表。
src/components/SignUp.vue
// src/components/SignUp.vue
<template>
<div>
<h2>{{ formState === 'signUp' ? 'Sign Up' : 'Confirm Sign Up' }}</h2>
<div class='formcontainer' v-if="formState === 'signUp'">
<input placeholder="username" v-model='form.username' class='input' />
<input placeholder="password" type='password' v-model='form.password' class='input' />
<input placeholder="email" v-model='form.email' class='input' />
<button v-on:click='signUp' class='button'>Sign Up</button>
</div>
<div class='formcontainer' v-if="formState === 'confirmSignUp'">
<input placeholder="confirmation code" v-model='form.authCode' class='input' />
<button v-on:click='confirmSignUp' class='button'>Confirm Sign Up</button>
</div>
</div>
</template>
<script>
import { Auth } from 'aws-amplify'
export default {
name: 'home',
props: ['toggle'],
data() {
return {
formState: 'signUp',
form: {
username: '',
password: '',
email: ''
}
}
},
methods: {
async signUp() {
const { username, password, email } = this.form
await Auth.signUp({
username, password, attributes: { email }
})
this.formState = 'confirmSignUp'
},
async confirmSignUp() {
const { username, authCode } = this.form
await Auth.confirmSignUp(username, authCode)
alert('successfully signed up! Sign in to view the app.')
this.toggle()
}
}
}
</script>
<style>
.formcontainer {
display: flex;
flex-direction: column;
width: 500px;
margin: 0 auto;
}
.input {
margin-bottom: 7px;
height: 38px;
border: none;
outline: none;
border-bottom: 2px solid #ddd;
font-size: 20px;
}
.button {
height: 45px;
border: none;
outline: none;
background-color: #dddddd;
margin-top: 8px;
cursor: pointer;
font-size: 18px;
}
.button:hover {
opacity: .7
}
</style>
src/components/SignUp.vue 的详细信息
- 我们有两个单独的表格 - 一个用于注册,一个用于确认注册(MFA 确认)
- 我们有一个
formState
布尔值,我们将使用它在两种形式之间切换。 - 我们的数据对象上有一个表单属性,当新用户注册时,它将跟上
username
、、password
& 的步伐。email
- 该
signUp
方法调用 AmplifyAuth.signUp
方法,传入表单属性。 - 该
confirmSignUp
方法调用 AmplifyAuth.confirmSignUp
方法,并传入username
&authCode
。用户成功注册后,我们将切换视图以显示SignUp组件。
最后,我们来看看SignIn组件。该组件与SignUp非常相似,因为它也有一个表单,并且调用了 AmplifyAuth
类的一个方法。
src/components/SignIn.vue
// src/components/SignIn.vue
<template>
<div>
<h2>Sign In</h2>
<div class='formcontainer'>
<input placeholder="username" v-model='form.username' class='input' />
<input placeholder="password" type='password' v-model='form.password' class='input' />
<button v-on:click='signIn' class='button'>Sign In</button>
</div>
</div>
</template>
<script>
import { Auth } from 'aws-amplify'
export default {
name: 'home',
data() {
return {
form: {
username: '',
password: ''
}
}
},
methods: {
async signIn() {
const { username, password } = this.form
await Auth.signIn(username, password)
}
}
}
</script>
src/components/SignIn.vue 的详细信息
- 我们有一个允许用户登录的表单
- 我们通过调用 Amplify 方法登录用户
Auth.signIn
。 - 在App.vue中,我们正在监听事件
signIn
,用户成功登录后将被路由到Profile路由。
测试一下
我们的应用程序的第 2 部分已经完成,让我们尝试一下吧!
~ npm run serve
您现在应该看到您的应用程序加载了我们创建的新注册/登录表单。
后续步骤
Amplify Auth 类有30 多种不同的方法,包括forgotPassword
、setPreferredMFA
、 &signOut
等。使用这些方法,您可以继续定制身份验证流程,使其更加健壮。
我们使用的样式很少,以防止这篇已经很长的博客文章过于冗长,但由于您可以完全控制身份验证流程,因此您可以根据自己的喜好设置样式。
Amplify 身份验证还支持 Facebook、Twitter、Google 和 Amazon 等提供商的联合登录。如需了解更多信息,请查看此处的文档。
结论
要查看最终的 repo 和源代码,请单击此处。
还可以查看Awesome AWS Amplify Repo以获取更多教程和入门项目。
文章来源:https://dev.to/dabit3/how-to-build-production-ready-vue-authentication-23mk我叫Nader Dabit。我是 Amazon Web Services 的开发倡导者,负责 AWS AppSync 和 AWS Amplify 等项目。我还擅长跨平台应用程序开发。