在Vue.js中使用Vuex、Vue Router和Auth0处理认证问题

421 阅读9分钟

认证是一个重要的功能,对大多数现代网络应用需要的安全性至关重要。如果被破坏,它可能会将你的用户的数据暴露给错误的人,从而带来安全风险。许多现代Web应用程序不从头开始处理认证,而是使用Auth0,一个易于实施、可适应的认证和授权平台。

Auth0目前提供了一个SPA SDK来处理Vue中的认证。然后,文档中已有的Vue插件封装了SDK的功能,而SDK又提供了一个高级的API抽象,处理了很多认证实现的小细节。

在本教程中,你将学习如何使用Auth0、传统的Vuex和Vue Router来实现认证,而不是像上面解释的那样使用插件和SDK。最后,你将使用Vue和Auth0构建一个功能性的认证服务,并使用这些相同的过程在你选择的任何Web应用中实现认证。让我们开始吧!

安装Vue CLI

我们将使用Vue CLI来启动该项目。如果你的系统中已经安装了Vue CLI,你可以跳到下一节

首先,你需要在你的系统上安装Vue CLI。你可以用这个命令全局安装它。

npm install -g @vue/cli

现在我们可以使用Vue CLI来创建我们的项目。用这个命令创建一个新项目。

vue create vue-sample-auth0

你会被要求选择一个预设。选择Manually select features ,然后选择Babel,Vuex,Router, 和Linter/Formatter

还会问你是否要使用History mode 作为Router - 选择Yes 。你可以选择你想要的任何Linter,但在本教程中我将使用ESLint和Prettier。

项目创建完成后,导航到新的项目文件夹。

cd vue-sample-auth0

然后启动服务器。

npm run serve

一旦服务器启动,你就可以打开浏览器来查看。你应该看到这个。

Vue welcome screen

设置Auth0

我们现在要安装Auth0的库来处理客户端的认证。

npm i auth0-js

要开始使用Auth0,你需要注册,创建一个新租户,并选择你的地区。然后你可以通过这些步骤进行。

  1. 在你的仪表板上,点击应用程序
    Application menu in Auth0
  2. 单击 "创建应用程序"。
    Create your application in Auth0
  3. 这个模式应该弹出,你应该选择单页网络应用
    Click on single-page web applications
  4. 然后我们可以从选项中选择Vue。
    Select Vue in Auth0

配置你的Auth0设置

现在你已经创建了应用程序,现在是时候配置设置了。

Configuring your Auth0 Settings

你将在基本信息部分看到你的域名和客户ID。我们稍后再来讨论这个问题;首先,我们需要将这些值存储为环境变量,以便我们的应用程序能够工作。

在应用程序的URLs部分,我们将需要定义这些值。

  • 允许的回调URL
  • 允许注销的URL
  • 允许的网络源头
  • 允许的起源(CORS)

注意,你需要使用你自己的本地URL,在本地测试你的应用程序。

允许的回调URLs

这些是你需要在用户成功登录后重定向的唯一URL。我们将使用本地的URL。

http://localhost:8080/authfinalize

我将在文章后面解释为什么我们要把用户发送到authfinalize 路径。

允许的注销URLs

这些是Auth0可以用来在你的用户注销后重定向的URL。

http://localhost:8080

Allowed logout URLs screen in Auth0

允许的网络源头

这些是唯一允许向Auth0发送请求的URL。你需要添加这个URL以防止出现跨源资源共享(CORS)错误。现在,我们将添加本地的URL。

http://localhost:8080

Allowed web origins in Auth0

允许的起源(CORS)

这些是唯一允许向服务器发出任何请求的URL。

http://localhost:8080

在这里,我们要设置我们的id_token 过期时间,单位是秒。我们将为这个样本网站使用36,000s(10小时)。

id_token expiration set in sample site

现在,让我们回到我们的项目文件夹,为我们的本地开发创建一个.env 文件。添加这些环境变量。

VUE_APP_AUTH0_CONFIG_RESPONSETYPE=token id_token
VUE_APP_AUTH0_CONFIG_SCOPE=openid profile email
VUE_APP_AUTH0_CONFIG_DOMAIN={{ Domain }}
VUE_APP_AUTH0_CONFIG_DOMAINURL=https://{{ Domain }}
VUE_APP_AUTH0_CONFIG_CLIENTID={{ ClientID }}
VUE_APP_DOMAINURL={{Local host URL}}
VUE_APP_DOMAINURL_REDIRECT={{ Allowed Callback URL }}
VUE_APP_EXPIRY=36000

这些变量将在我们的项目中使用,因为我们要继续下去。access tokenid_token 将在认证后作为响应返回,这可以用来验证认证用户的身份。

Domain,ClientID, 和Allowed Callback URL 都可以在设置页面找到。我们的VUE_APP_AUTH0_CONFIG_DOMAINVUE_APP_AUTH0_CONFIG_DOMAINURL 的唯一区别是,我们需要在域名前面加上https:// 。Vue的所有环境变量的前缀都必须是VUE_APP

设置Vuex

我们将把Vuex视为我们应用程序的单一真理来源,因此它将被用来在我们的应用程序中初始化Auth0。我们还将使用它来处理我们应用程序中的数据。

首先,我们需要在我们的应用程序中导入auth0-js ,并设置我们的state 。在我们的state ,我们将初始化Auth0,并从上面讨论的环境变量中添加我们的应用程序细节。

import Vuex from "vuex";
import Vue from "vue";
import actions from "./auth/actions";
import getters from "./auth/getters";
import mutations from "./auth/mutations";
import auth0 from "auth0-js";
Vue.use(Vuex);
const state = () => ({
  userIsAuthorized: false,
  auth0: new auth0.WebAuth({
    domain: process.env.VUE_APP_AUTH0_CONFIG_DOMAIN,
    clientID: process.env.VUE_APP_AUTH0_CONFIG_CLIENTID,
    redirectUri: process.env.VUE_APP_DOMAINURL_REDIRECT,
    responseType: process.env.VUE_APP_AUTH0_CONFIG_RESPONSETYPE,
    scope: process.env.VUE_APP_AUTH0_CONFIG_SCOPE,
  }),
});
export default new Vuex.Store({
  namespaced: true,
  state,
  actions,
  getters,
  mutations,
});

我们将userIsAuthorized 添加到我们的state ,以接受boolean 。我们将使用它作为一个指标来了解用户是否已经登录。注意,在应用程序的初始加载时,这将注册为假。

Vuex动作

login

用Auth0认证用户包括将用户发送到我们应用程序外的Auth0门户。之后,我们将他们返回到我们的应用程序。这个登录动作将在我们的组件中被调用以触发这个重定向。

//login.js

const login = ({state}) => {
    state.auth0.authorize()

}
export default login;
//logging in within the component

<template>
  <div class="home">
    <button @click="login">Login</button>
    <button @click="signup">Signup</button>
  </div>
</template>
<script>

export default {
  name: "Home",
  methods: {
    login() {
      this.$store.dispatch('login')
    },
    signup() {
      this.$store.dispatch('login')
    }
  }
};
</script>

authHandleAuthentication

当用户被送回我们的应用程序时,access tokenid_token 作为一个响应被送回给我们。这个动作设置了令牌的过期时间,然后在将用户发送到安全页面或仪表板之前,在本地存储access tokenid_token 、和过期时间。

该函数使用我们的state 内的Auth0对象的parseHash ,以检查用户是否有access tokenid_token ,然后它在将用户发送到仪表板之前将它们保存在本地。另一方面,如果有错误,用户会看到一个错误,并被重定向到主页。

//auth0HandleAuthentication.js

import router from '../../../router/index'
const auth0HandleAuthentication = (context) => {
  context.state.auth0.parseHash((err, authResult) => {
    if (authResult && authResult.accessToken && authResult.idToken) {
      let expiresAt = JSON.stringify(
        (process.env.VUE_APP_EXPIRY * 1000) + new Date().getTime()
      );
      // save the tokens locally
      localStorage.setItem("access_token", authResult.accessToken);
      localStorage.setItem("id_token", authResult.idToken);
      localStorage.setItem("expires_at", expiresAt);
      router.push({ name : "dashboard"})
    }
    else if (err) {
      alert("There's is an issue with signing up", err)
      router.replace({ name: "home" })
    }
  })
};
export default auth0HandleAuthentication;

logout

我们使用logout 按钮来签出用户。它把用户带回Auth0的网站,让他们完全退出我们的应用程序。然后,它使用我们之前添加的Allowed Logout URLs ,将他们送回我们的应用程序。

//logout.js

const logout = () => {
  localStorage.removeItem("access_token");
  localStorage.removeItem("id_token");
  localStorage.removeItem("expires_at");
  window.location.href =
    process.env.VUE_APP_AUTH0_CONFIG_DOMAINURL +
    "/v2/logout?returnTo=" +
    process.env.VUE_APP_DOMAINURL +
    "/&client_id=" +
    process.env.VUE_APP_AUTH0_CONFIG_CLIENTID;
};
export default logout;

//logging out on the dashboard page

<template>
  <div class="hello">
    <h2>Here's the dashboard</h2>
    <button @click="logout">Logout</button>
  </div>
</template>
<script>
export default {
  name: 'Dashboard',
  methods: {
    logout() {
      this.$store.dispatch('logout')
    }
  }
}
</script>

Vuex突变

我们将只有一个突变,它将被用来在我们的状态中触发我们的userIsAuthorized 指标。

 const setUserIsAuthenticated = (state, replacement) => {
    state.userIsAuthorized = replacement;
}

vue-router

这是行动将被触发的地方,用户将被路由,基于我们的路由守护中的某些条件。

用户流是直接的。

  1. 用户尝试登录
  2. 我们把用户送到Auth0登录门户进行认证
  3. 然后他们被送回我们的应用程序(特别是/authfinalize 页面),并附上access tokenid_token ,等等。
  4. 我们在本地保存这些令牌和过期时间,并将用户重定向到安全页面

在设置我们的路由时,我们将为每个路由添加meta 对象。这将使我们能够使用requiresGuest 来识别向所有人开放的路由,并使用requiresAuth 来识别受保护的路由。

这些meta 属性可以在路由位置和导航卫士上访问。

import Vue from "vue";
import VueRouter from "vue-router";
import Store from "../store"
import Home from "../views/Home.vue";
import Authfinalize from "../views/Authfinalize"
import Dashboard from "../views/Dashboard"
Vue.use(VueRouter);
const routes = [
  {
    path: "/",
    name: "home",
    component: Home,
    meta : {
      requiresGuest : true,
    }
  },
  {
    path: "/dashboard",
    name: "dashboard",
    component: Dashboard,
    meta : {
      requiresAuth : true
    }
  },
  {
    path: "/authfinalize",
    name: "Authfinalize",
    component: Authfinalize,
  },
];
const router = new VueRouter({
  mode: "history",
  base: process.env.BASE_URL,
  routes,
});
router.beforeEach((to, from, next) => {
  const requiresAuth = to.matched.some((x) => x.meta.requiresAuth);
  const requiresGuest = to.matched.some((x) => x.meta.requiresGuest);
  const auth0callback = to.matched.some(record=>record.path == "/authfinalize");
  let routerAuthCheck = false;  
  if(auth0callback) {
    Store.dispatch('auth0HandleAuthentication');
    next(false);
  }
  if( localStorage.getItem('access_token') && localStorage.getItem('id_token') && localStorage.getItem('expires_at') ){
    let expiresAt = JSON.parse(localStorage.getItem('expires_at'));
    routerAuthCheck = new Date().getTime() < expiresAt;  
  }
  Store.commit('setUserIsAuthenticated', routerAuthCheck); 
  if (requiresAuth) {  
    if(routerAuthCheck){
      next();
    }
    else{
      Store.dispatch('logout');
    }
  }

  if (requiresGuest && routerAuthCheck) {  
      next({ name: "dashboard" }); 
  } 
  next(); 
})

export default router;

在我们的beforeEach 路由守卫中,我们应该检查预定路线有哪一个meta-object ,并相应地将其分配给requiresAuthrequiresGuest 变量。如果我们要导航到/authfinalize 路径,我们也可以创建一个auth0callback 变量。

然后,我们创建一个名为routerAuthCheck 的变量并将其分配给false 。我们将使用这个变量来触发突变,这将改变我们之前谈到的Vuex内的指标。

第一个if block ,检查我们是否在导航到/authfinalize 路线,然后调用auth0HandleAuthentication 。请注意,我们只有在从Auth0重定向回来的时候才会导航到这条路线。

第二个if block ,检查用户是否被认证,然后它做一个commit ,当登录时,它将我们的指标切换到true ,反之亦然。基本上,它检查本地存储,看看令牌和到期日是否还在那里。

然后它进一步检查过期日期是否大于当前时间。routerAuthCheck 将反映它是true 还是false ,然后它将提交到setUserIsAuthenticated 突变,这将切换我们的应用程序指标。

第三个if block 检查预定路线是否需要认证(requiresAuth),然后检查routerAuthChecktrue 还是false 。如果是true ,用户就会进入仪表板。如果是false ,则用户被注销。

最后一个if block ,检查已认证的用户是否正在前往主页(requiresGuest)。然后,它会自动把他们送到仪表板上。

部署你的应用程序

要部署该应用程序,你需要将你的实时URL添加到前面演示的每个设置中。用逗号把每个URL分开。

Deploying your app with one URL

Deploying your app with multiple URLs

记住,你必须创建一个新的.env 文件,并在你的代码库或你要部署的平台中设置实时版本的URL,这取决于你使用什么平台进行部署。

总结

虽然有其他处理认证的方法,但像Auth0这样的工具可以给你带来时间上的优势,同时你还可以确定你的应用程序的安全性。你可以在GitHub上查看我们的代码库,并在这里找到演示网站。你也可以从Auth0的文档中了解更多。

The postHandling authentication in Vue.js using Vuex, Vue Router, and Auth0appeared first onLogRocket Blog.