使用 RESTful API 在 Vue 中处理身份验证

Avatar of David Atanda
David Atanda

DigitalOcean 为您旅程的每个阶段提供云产品。立即开始使用 $200 免费额度!

身份验证(登录!)是许多网站的重要组成部分。让我们看看如何在使用 Vue 的网站上进行身份验证,就像使用任何自定义后端一样。Vue 本身无法执行身份验证,我们需要另一个服务来完成,因此我们将使用另一个服务(Firebase),然后将整个体验整合到 Vue 中。

单页应用程序 (SPA) 的身份验证机制与每次页面重新加载的网站大不相同。您不必使用 Vue 创建 SPA,但本教程中我们将使用 SPA。

以下是计划。我们将构建一个 UI 供用户登录,提交的数据将发送到服务器以检查用户是否存在。如果存在,我们将收到一个令牌。这非常有用,因为它将在整个网站中使用,以检查用户是否仍然登录。如果不存在,用户始终可以注册。换句话说,它可以在许多条件上下文中使用。除此之外,如果我们需要从服务器获取任何需要登录才能获取的信息,则令牌将通过 URL 发送到服务器,以便信息只能发送给登录用户。

本教程的完整演示已 发布在 GitHub 上,供那些熟悉代码阅读的人使用。我们其他人可以阅读文章。启动文件也 在 GitHub 上,这样您就可以在我们的共同编码过程中进行操作。

下载仓库后,您将在终端中运行 npm install。如果您要完全独立构建此应用程序,则必须安装 VuexVue Routeraxios。我们还将使用 Firebase 来完成此项目,因此请花点时间设置一个免费帐户并在其中创建一个新项目。

将项目添加到 Firebase 后,转到身份验证部分,设置一个登录方法,我们将使用传统的电子邮件/密码提供程序,这些信息将存储在我们的 Firebase 服务器上。

之后,我们将转到 Firebase Auth REST API 文档 获取我们的注册和登录 API 端点。我们需要一个 API 密钥才能在我们的应用程序中使用这些端点,它可以在 Firebase 项目设置中找到。

Firebase 通过 SDK 提供身份验证,但我们使用 Auth API 来演示通过任何自定义后端服务器进行身份验证。

在我们的启动文件中,我们有以下注册表单。由于我们专注于学习概念,因此我们将内容保持非常简单。

<template>
  <div id="signup">
    <div class="signup-form">
      <form @submit.prevent="onSubmit">
        <div class="input">
          <label for="email">Mail</label>
          <input
             type="email"
             id="email"
             v-model="email">
        </div>
        <div class="input">
          <label for="name">Your Name</label>
          <input
            type="text"
            id="name"
            v-model.number="name">
        </div>
        <div class="input">
          <label for="password">Password</label>
          <input
            type="password"
            id="password"
            v-model="password">
        </div>
        <div class="submit">
          <button type="submit">Submit</button>
        </div>
      </form>
    </div>
  </div>
</template>

如果我们没有使用 SPA,我们会自然地使用 axios 在脚本标签中发送数据,如下所示

axios.post('https://identitytoolkit.googleapis.com/v1/account
  s:signUp?key=[API_KEY]', {
    email: authData.email,
    password: authData.password,
    returnSecureToken: true
  })
  .then(res => {
    console.log(res)
  })
  .catch(error => console.log(error))        
  }
}

注册和登录

使用 SPA(在本例中使用 Vue)与上述方法非常不同。相反,我们将使用 Vuexstore.js 文件中的操作中发送授权请求。我们之所以这样做,是因为我们希望整个应用程序都知道用户身份验证状态的任何更改。

actions: {
  signup ({commit}, authData) {
    axios.post('https://identitytoolkit.googleapis.com/v1/accounts:signUp?key=[API_KEY]', {
      email: authData.email,
      password: authData.password,
      returnSecureToken: true
    })
    .then(res => {
      console.log(res)
      router.push("/dashboard")
    })
    .catch(error => console.log(error))
  },
  login ({commit}, authData) {
    axios.post(https://identitytoolkit.googleapis.com/v1/accounts:signIn?key=[API_KEY]', {
      email: authData.email,
      password: authData.password,
      returnSecureToken: true
    })
    .then(res => {
      console.log(res)
      router.push("/dashboard")
    })
    .catch(error => console.log(error))
  }
}

我们可以对登录方法使用几乎相同的内容,但使用登录 API 端点。然后,我们从组件中调度注册和登录,到存储中的相应操作。

methods : { 
  onSubmit () {
    const formData = {
      email : this.email,
      name : this.name,     
      password : this.password
    }
    this.$store.dispatch('signup', formData)
    }
  }
}

formData 包含用户数据。

methods : {
  onSubmit () {
    const formData = {
      email : this.email,
      password : this.password
    }
    this.$store.dispatch('login', {email: formData.email, password: formData.password})
  }
}

我们将从注册/登录表单中接收到的身份验证数据(即令牌和用户 ID)作为状态与 Vuex 一起使用。它最初将是 null

state: {
  idToken: null,
  userId: null,
  user: null
}

我们现在在变异中创建一个名为 authUser 的新方法,它将存储从响应中收集的数据。我们需要将路由器导入到存储中,因为我们稍后将需要它。

import router from '/router'


mutations : {
  authUser (state, userData) {
    state.idToken = userData.token
    state.userId = userData.userId
  }
}

在操作中的注册/登录方法的 .then 块中,我们将响应提交到刚刚创建的 authUser 变异,并保存到本地存储。

actions: {
  signup ({commit}, authData) {
    axios.post('https://identitytoolkit.googleapis.com/v1/accounts:signUp?key=[API_KEY]'), {
      email: authData.email,
      password: authData.password,
      returnSecureToken: true
    })
    .then(res => {
      console.log(res)
      commit('authUser', {
        token: res.data.idToken,
        userId: res.data.localId
      })
      localStorage.setItem('token', res.data.idToken)
      localStorage.setItem('userId', res.data.localId)
      router.push("/dashboard")
    })
    .catch(error => console.log(error))
  },
  login ({commit}, authData) {
    axios.post('https://identitytoolkit.googleapis.com/v1/accounts:signIn?key=[API_KEY]'), {
      email: authData.email,
      password: authData.password,
      returnSecureToken: true
    })
    .then(res => {
      console.log(res)
      commit('authUser', {
        token: res.data.idToken,
        userId: res.data.localId
      })
        localStorage.setItem('token', res.data.idToken)
        localStorage.setItem('userId', res.data.localId)
        router.push("/dashboard")
      })
    .catch(error => console.log(error))
  }
}

设置身份验证守卫

现在我们已将令牌存储在应用程序中,我们将使用此令牌设置身份验证守卫。什么是身份验证守卫?它保护仪表盘免遭未经身份验证的用户在没有令牌的情况下访问它。

首先,我们将进入我们的路由文件并导入存储。导入存储是因为将确定用户的登录状态的令牌。

import store from './store.js'

然后,在我们的路由数组中,转到仪表盘路径并添加 beforeEnter 方法,它接受三个参数:tofromnext。在此方法中,我们只是说,如果令牌已存储(如果已通过身份验证,则会自动完成),那么 next,这意味着它将继续执行指定的路由。否则,我们将未经身份验证的用户带回注册页面。

{
  path: '/dashboard',
  component: DashboardPage,
  beforeEnter (to, from, next) {
    if (store.state.idToken) {
      next()
    } 
    else {
      next('/signin')
    }
  }
}

创建 UI 状态

在这一点上,我们仍然可以在导航中看到仪表盘,无论我们是否已登录,这不是我们想要的。我们必须在 getter 下添加另一个方法,称为 ifAuthenticated,它检查状态中的令牌是否为 null,然后相应地更新导航项目。

getters: {
  user (state) {
    return state.user
  },
  ifAuthenticated (state) {
    return state.idToken !== null
  }
}

接下来,打开标题组件并创建一个名为 auth 的方法,放在 computed 属性中。它将调度到我们在存储中创建的 ifAuthenticated getter。ifAuthenticated 如果没有令牌,将返回 false,这意味着 auth 也将为 null,反之亦然。之后,我们添加一个 v-if 来检查 auth 是否为 null,确定仪表盘选项是否显示在导航中。

<template>
  <header id="header">
    <div class="logo">
      <router-link to="/">Vue Authenticate</router-link>
    </div>
    <nav>
      <ul>
        <li v-if='auth'>
          <router-link to="/dashboard">Dashboard</router-link>
        </li>
        <li  v-if='!auth'>
          <router-link to="/signup">Register</router-link>
        </li>
        <li  v-if='!auth'>
          <router-link to="/signin">Log In</router-link>
        </li>
      </ul>
    </nav>
  </header>
</template>
<script>
  export default {
    computed: {
      auth () {
        return this.$store.getters.ifAuthenticated
      }
    },
  }
</script>

注销

没有注销按钮的应用程序是什么?让我们创建一个名为 clearAuth 的新变异,它将令牌和 userId 都设置为 null

mutations: {
  authUser (state, userData) {
    state.idToken = userData.token
    state.userId = userData.userId
  },
  clearAuth (state) {
    state.idToken = null
    state.userId = null
  }
}

然后,在我们的 logout 操作中,我们提交到 clearAuth,删除本地存储,并添加 router.replace('/') 以在注销后正确重定向用户。

回到标题组件。我们有一个 onLogout 方法,它将我们的 logout 操作调度到存储中。然后,我们向按钮添加一个 @click,它调用 onLogout 方法,如下所示

<template>
  <header id="header">
    <div class="logo">
      <router-link to="/">Vue Authenticate</router-link>
    </div>
    <nav>
      <ul>
        <li v-if='auth'>
          <router-link to="/dashboard">Dashboard</router-link>
        </li>
        <li  v-if='!auth'>
          <router-link to="/signup">Register</router-link>
        </li>
        <li  v-if='!auth'>
          <router-link to="/signin">Log In</router-link>
        </li>
         <li  v-if='auth'>
          <ul @click="onLogout">Log Out</ul>
        </li>
      </ul>
    </nav>
  </header>
</template>
<script>
  export default {
    computed: {
      auth () {
        return this.$store.getters.ifAuthenticated
      }
    },
    methods: {
      onLogout() {
        this.$store.dispatch('logout')
      }
    }
  }
</script>

自动登录?当然!

我们离完成应用程序还有最后一步。我们可以注册、登录和注销,所有 UI 更改都已完成。但是,当我们刷新应用程序时,我们将丢失数据并注销,不得不重新开始,因为我们将令牌和 ID 存储在 Vuex 中,它是 JavaScript。这意味着应用程序中的所有内容在刷新时都会在浏览器中重新加载。

我们将从本地存储中检索令牌。通过这样做,我们可以让用户在刷新窗口时始终拥有令牌,甚至可以自动登录用户,只要令牌仍然有效。

创建一个名为 AutoLogin 的新操作方法,我们将在其中从本地存储获取令牌和 userId,只有在用户拥有它们的情况下才会进行此操作。然后,我们将数据提交到变异中的 authUser 方法。

actions : {
  AutoLogin ({commit}) {
    const token = localStorage.getItem('token')
    if (!token) {
      return
    }
    const userId = localStorage.getItem('userId')
    const token = localStorage.getItem('token')
    commit('authUser', {
      idToken: token,
      userId: userId
    })
  }
}

然后,转到 App.vue 并创建一个 created 方法,当应用程序加载时,我们将从中调度存储中的 autoLogin

created () {
  this.$store.dispatch('AutoLogin')
}

好了!有了它,我们已成功在应用程序中实现身份验证,现在可以使用 npm run build 进行部署。查看实时演示以了解它的实际运行情况。

示例网站仅用于演示目的。请在测试演示应用程序时不要共享真实数据,例如您的真实电子邮件和密码。