在从本地存储还原 Vuex Store 之前执行的中间件

aue*_*aki 5 persistence local-storage vue.js vuex nuxt.js

在 nuxtjs 项目中,我创建了一个 auth 中间件来保护页面。并使用 vuex-persistedstate(也尝试过 vuex-persist 和 nuxt-vuex-persist)来持久化 vuex 存储。

在页面之间导航时一切正常,但是当我刷新页面或直接登陆受保护的路由时,它会将我重定向到登录页面。

本地存储插件

import createPersistedState from 'vuex-persistedstate'

export default ({ store }) => {
  createPersistedState({
      key: 'store-key'
  })(store)
}
Run Code Online (Sandbox Code Playgroud)

认证中间件

export default function ({ req, store, redirect, route }) {
    const userIsLoggedIn = !!store.state.auth.user
    if (!userIsLoggedIn) {
        return redirect(`/auth/login?redirect=${route.fullPath}`)
    }
    return Promise.resolve()
}
Run Code Online (Sandbox Code Playgroud)

aue*_*aki 1

按照当前的方法,我们总是会失败。

实际问题是 Vuex 存储永远无法与服务器端 Vuex 存储同步。

事实上,我们只需要数据字符串与客户端和服务器(令牌)同步。

我们可以通过Cookies来实现这种同步。因为 cookie 会自动传递给来自浏览器的每个请求。所以我们不需要设置任何请求。您只需从浏览器地址栏或通过导航点击 URL。

我建议使用模块“cookie-universal-nuxt”来设置和删除 cookie。

用于登录后设置cookie

this.$cookies.set('token', 'Bearer '+response.tokens.access_token, { path: '/', maxAge: 60 * 60 * 12 })
Run Code Online (Sandbox Code Playgroud)

用于在注销时删除 cookie

this.$cookies.remove('token')
Run Code Online (Sandbox Code Playgroud)

请仔细阅读文档以更好地理解。

我还使用@nuxt/http模块进行 api 请求。

现在 nuxt 在 vuex 存储索引文件中有一个名为 nuxtServerInit() 的函数。您应该使用它从请求中检索令牌并将其设置为 http 模块标头。

async nuxtServerInit ({dispatch, commit}, {app, $http, req}) {
    return new Promise((resolve, reject) => {
        let token = app.$cookies.get('token')
        if(!!token) {
          $http.setToken(token, 'Bearer')
        }
        return resolve(true)
    })
  },
Run Code Online (Sandbox Code Playgroud)

下面是我的 nuxt 页面级中间件

export default function ({app, req, store, redirect, route, context }) {
    if(process.server) {


        let token = app.$cookies.get('token')

        if(!token) {
            return redirect({path: '/auth/login', query: {redirect: route.fullPath, message: 'Token Not Provided'}})
        } else if(!isTokenValid(token.slice(7))) { // slice(7) used to trim Bearer(space)
            return redirect({path: '/auth/login', query: {redirect: route.fullPath, message: 'Token Expired'}})
        } 
        return Promise.resolve()
        
    }
    else {
        const userIsLoggedIn = !!store.state.auth.user
        if (!userIsLoggedIn) {
            return redirect({path: '/auth/login', query: {redirect: route.fullPath}})
            // return redirect(`/auth/login?redirect=${route.fullPath}`)
        } else if (!isTokenValid(store.state.auth.tokens.access_token)) {
            return redirect({path: '/auth/login', query: {redirect: route.fullPath, message: 'Token Expired'}})
            // return redirect(`/auth/login?redirect=${route.fullPath}&message=Token Expired`)
        } else if (isTokenValid(store.state.auth.tokens.refresh_token)) {
            return redirect(`/auth/refresh`)
        } else if (store.state.auth.user.role !== 'admin')
            return redirect(`/403?message=Not having sufficient permission`)
        return Promise.resolve()
    }
}
Run Code Online (Sandbox Code Playgroud)

我已经为不同的令牌源编写了不同的条件,如代码中所示。在服务器进程上,我从 cookie 获取令牌,在客户端获取令牌存储。(这里我们也可以从cookie中获取)

之后,由于布局中的存储数据绑定,您可能会遇到一些水合问题。为了解决这个问题,请使用<no-ssr></no-ssr>此类模板代码的包装。