在 Vuex Store 从 localstorage 恢复之前执行的中间件
Middleware executing before Vuex Store restore from localstorage
在nuxtjs项目中,我创建了一个auth中间件来保护页面。
并使用 vuex-persistedstate(也尝试过 vuex-persist 和 nuxt-vuex-persist)来持久化 vuex 存储。
从一个页面导航到另一个页面时一切正常,但是当我刷新页面或直接登陆到受保护的路由时,它会将我重定向到登录页面。
localStorage 插件
import createPersistedState from 'vuex-persistedstate'
export default ({ store }) => {
createPersistedState({
key: 'store-key'
})(store)
}
授权中间件
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()
}
我使用这个插件 vuex-persistedstate 而不是 vuex-persist 插件解决了这个问题。似乎是 vuex-persist 中的一些错误(或者可能是设计架构)导致了它。
使用当前方法,我们总是会失败。
实际问题是 Vuex Store 永远无法与服务器端 Vuex store 同步。
事实是我们只需要数据字符串与客户端和服务器(令牌)同步。
我们可以通过Cookies实现这种同步。因为 cookie 会自动传递给来自浏览器的每个请求。所以我们不需要设置任何请求。您只需从浏览器地址栏或通过导航点击 URL。
我建议使用模块 'cookie-universal-nuxt' 来设置和删除 cookie。
登录后设置cookie
this.$cookies.set('token', 'Bearer '+response.tokens.access_token, { path: '/', maxAge: 60 * 60 * 12 })
注销时删除 cookie
this.$cookies.remove('token')
请阅读文档以便更好地理解。
我还使用 @nuxt/http 模块来处理 api 请求。
现在 nuxt 在 vuex 存储索引文件中有一个名为 nuxtServerInit() 的函数。您应该使用它从请求中检索令牌并设置为 http 模块 headers.
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)
})
},
下面是我的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()
}
}
我已经为不同来源的令牌编写了不同的条件,如代码所示。在服务器进程上,我从 cookie 获取令牌,在客户端获取令牌存储。 (这里我们也可以从cookies中获取)
在此之后,由于布局中的存储数据绑定,您可能会遇到一些水合作用问题。要解决此问题,请对此类模板代码使用 <no-ssr></no-ssr>
包装。
在nuxtjs项目中,我创建了一个auth中间件来保护页面。 并使用 vuex-persistedstate(也尝试过 vuex-persist 和 nuxt-vuex-persist)来持久化 vuex 存储。
从一个页面导航到另一个页面时一切正常,但是当我刷新页面或直接登陆到受保护的路由时,它会将我重定向到登录页面。
localStorage 插件
import createPersistedState from 'vuex-persistedstate'
export default ({ store }) => {
createPersistedState({
key: 'store-key'
})(store)
}
授权中间件
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()
}
我使用这个插件 vuex-persistedstate 而不是 vuex-persist 插件解决了这个问题。似乎是 vuex-persist 中的一些错误(或者可能是设计架构)导致了它。
使用当前方法,我们总是会失败。
实际问题是 Vuex Store 永远无法与服务器端 Vuex store 同步。
事实是我们只需要数据字符串与客户端和服务器(令牌)同步。
我们可以通过Cookies实现这种同步。因为 cookie 会自动传递给来自浏览器的每个请求。所以我们不需要设置任何请求。您只需从浏览器地址栏或通过导航点击 URL。
我建议使用模块 'cookie-universal-nuxt' 来设置和删除 cookie。
登录后设置cookie
this.$cookies.set('token', 'Bearer '+response.tokens.access_token, { path: '/', maxAge: 60 * 60 * 12 })
注销时删除 cookie
this.$cookies.remove('token')
请阅读文档以便更好地理解。
我还使用 @nuxt/http 模块来处理 api 请求。
现在 nuxt 在 vuex 存储索引文件中有一个名为 nuxtServerInit() 的函数。您应该使用它从请求中检索令牌并设置为 http 模块 headers.
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)
})
},
下面是我的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()
}
}
我已经为不同来源的令牌编写了不同的条件,如代码所示。在服务器进程上,我从 cookie 获取令牌,在客户端获取令牌存储。 (这里我们也可以从cookies中获取)
在此之后,由于布局中的存储数据绑定,您可能会遇到一些水合作用问题。要解决此问题,请对此类模板代码使用 <no-ssr></no-ssr>
包装。