VUE 3,VUE Router 4 Navigation Guards和Pinia商店

发布于 2025-01-18 05:53:48 字数 1768 浏览 2 评论 0原文

我正在尝试使用JWT身份验证的应用程序创建一个VUE 3,并使用Pinia Store中的“ Isauth”变量匹配路由器的问题,以检查访问。最终,整个负载的Vue路由器和应用程序比商店更快,这就是为什么我总是从商店中获得“未经授权”的值,但实际上用户已登录并且他的数据在商店中。 我将尝试描述为注册和登录用户所做的所有步骤。

  1. 对Nodejs后端进行了注册,并创建了JWT令牌。
  2. 在登录屏幕上,用户输入电子邮件和密码,如果信息有效,他将被登录,JWT将保存到localStorage并通过JWTDecode进行解码,解码的令牌数据将保存在用户变量中,并将其保存到商店中,并将其设置为TRUE变量。 。
  3. Pinia商店有2个状态字段:用户(最初为null)和Isauth(最初为false)。
  4. 在主应用程序组件中,我使用的是Async onmounted Hook检查令牌,并通过调用API方法来保持用户登录,该方法比较JWT。
  5. 在VUE路由器中,我有几条必须保护未经授权的用户的路线,这就是为什么我试图通过从商店中检查用户信息来为其创建导航警卫的原因。问题是,路由器是在设置用户信息之后创建的,并且始终获取用户和ISAUTH变量的初始状态。

代码:

store

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;
   }
}
});

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));
       });

路由器护罩样品

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

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

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

提前致谢!

I'm trying to create an Vue 3 with app with JWT authentication and meet an issue with guarding the router using "isAuth" variable from Pinia store to check the access. Eventually Vue router and app in whole loads faster than the Store, that's why I'm always getting "unauthorized" value from the store, but in fact user is logged in and his data is in store.
I'll try to describe all the steps that are made to register and login user.

  1. Registration is made to NodeJS backend and JWT token is created.
  2. On the login screen user enters email and password, if info is valid he will be logged in and JWT will be saved to localstorage and decoded through JWTdecode, decoded token data will be saved to the store in user variable, and isAuth variable set to true.
  3. Pinia store has 2 fields in state: user(initially null), and isAuth(initially false).
  4. In the main App component I'm using async onMounted hook to check the token and keep user logged in by calling the API method, which compares JWT.
  5. In the Vue router i have several routes that must be protected from the unauthorized users, that's why I'm trying to create navigation guards for them by checking the user information from the store. Problem is, router is created after the setting user info and is always getting the initial state of the user and isAuth variables.

Code:

Store

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;
   }
}
});

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));
       });

Router guard sample

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

I feel that problem is with this async checking, but can't figure out how to rewrite it to load store before the app initialization.

I hope that somebody meet this problem too and can help.

Thanks in advance!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

末が日狂欢 2025-01-25 05:53:48

因此,我刚刚遇到了这个问题,并因此解决了该问题,请此解决方案

正如它所说,路由器在app应用之前实例化。

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

​> isauth 检查user是否不是null,例如:

isAuth: (state) => state.user !== null

也不建议将JWT存储在本地存储中,就好像您接触了XSS攻击,可以偷走令牌。您至少应该将其存储在httponly cookie中(这意味着它无法从JavaScript访问),它可以使用Express来实现。

So I just met this problem and fixed it thanks to this solution

As it says, the router gets instantiated before App.vue is fully mounted so check the token in beforeEach instead, like:

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

By the way instead of having an action setAuth you could just use your getter isAuth checking if user is not null, like:

isAuth: (state) => state.user !== null

Also it's not recommended to store a JWT in the local storage as if you're site is exposed to XSS attacks the token can be stolen. You should at least store it in an HttpOnly cookie (meaning it's not accessible from JavaScript), it's super easy to do with Express.

千纸鹤 2025-01-25 05:53:48

首先,我想说,使用 App.vue 和生命周期钩子是一个糟糕的实现,尽量不要那样使用它。

创建 authGuard.ts:

import { useUserStore } from '@/stores/user';
import type { NavigationGuard } from 'vue-router';

export const authGuard: NavigationGuard = async (to, from, next) => {
  const userStore = useUserStore();

  // Fetch user data from the server or local storage
  await userStore.fetchUser();

  // Check if the user is authenticated
  if (userStore.isAuthenticated) {
    next();
  } else {
    // You can use try/catch to get an id token and set it to your request header
    // ex: try { ... next() } catch { ... next({ name: '/login') }
    next('/login');
  }
};

将路由器防护添加到您的路由器配置中:

import { createRouter, createWebHistory } from 'vue-router';
import { authGuard } from '@/guards/authGuard';

const router = createRouter({
  history: createWebHistory(),
  routes: [
    {
      path: '/',
      component: Home,
      meta: { requiresAuth: true },
    },
    {
      path: '/login',
      component: Login,
    },
  ],
});

router.beforeEach((to, from, next) => {
  if (to.matched.some(record => record.meta.requiresAuth)) {
    authGuard(to, from, next);
  } else {
    next();
  }
});

export default router;

您可以在存储文件中定义自定义的 isAuthenticated 检查器。

希望有帮助!这是一个基本的实现,应该根据您的需要进行重构。

First of all, I want to say that using App.vue and the lifecycle hooks is a bad implementation, try not to use it like that.

Create authGuard.ts:

import { useUserStore } from '@/stores/user';
import type { NavigationGuard } from 'vue-router';

export const authGuard: NavigationGuard = async (to, from, next) => {
  const userStore = useUserStore();

  // Fetch user data from the server or local storage
  await userStore.fetchUser();

  // Check if the user is authenticated
  if (userStore.isAuthenticated) {
    next();
  } else {
    // You can use try/catch to get an id token and set it to your request header
    // ex: try { ... next() } catch { ... next({ name: '/login') }
    next('/login');
  }
};

Add the router guard to your router configuration:

import { createRouter, createWebHistory } from 'vue-router';
import { authGuard } from '@/guards/authGuard';

const router = createRouter({
  history: createWebHistory(),
  routes: [
    {
      path: '/',
      component: Home,
      meta: { requiresAuth: true },
    },
    {
      path: '/login',
      component: Login,
    },
  ],
});

router.beforeEach((to, from, next) => {
  if (to.matched.some(record => record.meta.requiresAuth)) {
    authGuard(to, from, next);
  } else {
    next();
  }
});

export default router;

You can define your custom isAuthenticated checker in the store file.

Hope it helps! It's a basic implementation and should be refactored according to your needs.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文