Vue 3、Vue Router 4 导航卫士和 Pinia 商店

Vah*_*ahe 5 authentication vue.js pinia

我正在尝试使用具有 JWT 身份验证的应用程序创建 Vue 3,并遇到使用 Pinia 商店中的“isAuth”变量来检查访问权限来保护路由器的问题。最终,Vue 路由器和应用程序的整体加载速度比商店更快,这就是为什么我总是从商店获得“未经授权”的值,但实际上用户已登录并且他的数据在商店中。我将尝试描述注册和登录用户的所有步骤。

  1. 注册到 NodeJS 后端并创建 JWT 令牌。
  2. 在登录屏幕上,用户输入电子邮件和密码,如果信息有效,他将登录,JWT 将保存到本地存储并通过 JWTdecode 进行解码,解码后的令牌数据将保存到用户变量中的存储中,isAuth 变量设置为 true 。
  3. Pinia 商店有 2 个状态字段:user(最初为 null)和 isAuth(最初为 false)。
  4. 在主应用程序组件中,我使用异步 onMounted 挂钩来检查令牌并通过调用 API 方法(该方法与 JWT 进行比较)来保持用户登录状态。
  5. 在 Vue 路由器中,我有几条路线必须受到保护,免受未经授权的用户的侵害,这就是为什么我试图通过检查商店中的用户信息来为它们创建导航防护。问题是,路由器是在设置用户信息后创建的,并且始终获取用户的初始状态和 isAuth 变量。

代码:

店铺

import { defineStore } from 'pinia';

export const useLoggedInUserStore = defineStore({
  id: 'loggedInUser',
  state: () => ({
  isAuth: false,
  user: null
   }),

  getters: {
  getisAuth(state) {
  return state.isAuth;
    },
  getUser(state) {
  return state.user;
   }
  },
 actions: {
  setUser(user) {
  this.user = user;
  },
  setAuth(boolean) {
  this.isAuth = boolean;
   }
}
});
Run Code Online (Sandbox Code Playgroud)

App.vue onMounted

 onMounted(async () => {
    await checkUser()
      .then((data) => {
         isLoading.value = true;
          if (data) {
          setUser(data);
          setAuth(true);
         } else {
         router.push({ name: 'Login' });
          }
       })
       .finally((isLoading.value = false));
       });
Run Code Online (Sandbox Code Playgroud)

路由器防护示例

router.beforeEach((to, from, next) => {
   const store = useLoggedInUserStore();
   if (!store.isAuth && to.name !== 'Login') next({ name: 'Login' });
   else next();
});
Run Code Online (Sandbox Code Playgroud)

我觉得问题出在这个异步检查上,但无法弄清楚如何在应用程序初始化之前将其重写为加载存储。

我希望有人也遇到这个问题并提供帮助。

提前致谢!

小智 13

所以我刚刚遇到了这个问题并通过这个解决方案解决了它

正如它所说,路由器在App.vue完全安装之前被实例化,因此请检查令牌beforeEach,例如:

router.beforeEach(async (to, from, next): Promise<void> => {
  const user = useUser();
  await user.get();

  console.log(user) // user is defined

  if (to.meta.requiresAuth && !user.isLoggedIn) next({ name: "home" }); // this will work
Run Code Online (Sandbox Code Playgroud)

顺便说一句,setAuth您可以使用 getterisAuth检查是否user不为空,而不是执行操作,例如:

isAuth: (state) => state.user !== null
Run Code Online (Sandbox Code Playgroud)

另外,不建议将 JWT 存储在本地存储中,因为如果您的站点受到 XSS 攻击,令牌可能会被盗。您至少应该将其存储在 HttpOnly cookie 中(这意味着它无法从 JavaScript 访问),使用 Express 可以非常轻松地做到这一点。

  • 我认为隐藏令牌不值得任何努力...令牌无论如何都会随每个请求一起发送,因此仍然可以轻松检索甚至在手动请求期间进行操作...设置一个合理的小到期时间非常重要并且使用刷新令牌... (2认同)