Vue 3 js路由器无法访问用户存储的Pinia状态(需要对Pinia状态进行Hypersonic)

ua4mk5z4  于 2023-10-23  发布在  Vue.js
关注(0)|答案(1)|浏览(272)

我试图在Vue 3 js应用程序中检查路由的授权。这个想法是授权应该根据用户的角色(NavGuard)来定义。出于这个原因,当用户登录时(JWT令牌),我将他的角色添加到Pinia存储的状态(useUserStore)。
我已经阅读了有关"Using a store outside of a component"的文档,当用户登录并被重定向到主页时,它实际上是工作的。如果我console.log(store),我可以看到用户的角色。
但是,每当我导航到另一个页面时,console.logged状态就是初始状态(所有值都为null)。我猜问题是路由器在状态准备好之前就加载了。

import NotFoundView from '@/views/public/NotFoundView.vue'
import { useUserStore } from '@/stores/user'
import pinia from "@/stores/store.js";

const router = createRouter({

  history: createWebHistory(import.meta.env.BASE_URL),

  routes: [
    {
      path: '/',
      name: 'home',
      component: HomeView
    },
    {
      path: '/about',
      name: 'about',
      component: AboutView
    },
   
    {
      path: '/:pathMatch(.*)*',
      name: 'NotFound',
      component: NotFoundView
    },

  ]
})

router.beforeEach(async (to, from) => {

  const store = await useUserStore();
 
  console.log(store.user)

})

export default router

Main.js

import { createApp } from 'vue'
import store from '@/stores/store'

import App from '@/App.vue'
import router from '@/router'
import axios from 'axios'

axios.defaults.baseURL = 'http://127.0.0.1:8000'

const app = createApp(App)
app.use(store)
app.use(router, axios)

router.isReady().then(() => {
    app.mount("#app");
});

store.js

import { createPinia } from "pinia";

const pinia = createPinia();

export default pinia;

user.js

import { defineStore } from 'pinia'
import axios from 'axios'

export const useUserStore = defineStore('user', {
    id: 'user',

    state: () => {
        return {
            user: {
                isAuthenticated: false,
                id: null,
                name: null,
                email: null,
                access: null,
                refresh: null,
                role: null,
            }
        }
    },

    actions: {
        initStore() {
            console.log('initStore')

            if (localStorage.getItem('user.access')) {
                this.user.access = localStorage.getItem('user.access')
                this.user.refresh = localStorage.getItem('user.refresh')
                this.user.id = localStorage.getItem('user.id')
                this.user.name = localStorage.getItem('user.name')
                this.user.email = localStorage.getItem('user.email')
                this.user.isAuthenticated = true

                this.refreshToken()

                console.log('Initialized user:', this.user)
            }
        },

        
        setToken(data) {
            console.log('setToken', data)

            this.user.access = data.access
            this.user.refresh = data.refresh
            this.user.isAuthenticated = true

            localStorage.setItem('user.access', data.access)
            localStorage.setItem('user.refresh', data.refresh)
        },

        removeToken() {
            console.log('removeToken')

            this.user.refresh = null
            this.user.access = null
            this.user.isAuthenticated = false
            this.user.id = false
            this.user.name = false
            this.user.email = false

            localStorage.setItem('user.access', '')
            localStorage.setItem('user.refresh', '')
            localStorage.setItem('user.id', '')
            localStorage.setItem('user.name', '')
            localStorage.setItem('user.email', '')
        },

        setUserInfo(user) {
            console.log('setUserInfo', user)

            this.user.id = user.id
            this.user.name = user.name
            this.user.email = user.email
            this.user.role = user.role

            localStorage.setItem('user.id', this.user.id)
            localStorage.setItem('user.name', this.user.name)
            localStorage.setItem('user.email', this.user.email)
            localStorage.setItem('user.role', this.user.role)

            console.log('User', this.user)
        },

        refreshToken() {
            axios.post('/api/refresh/', {
                refresh: this.user.refresh
            })
                .then((response) => {
                    this.user.access = response.data.access

                    localStorage.setItem('user.access', response.data.access)

                    axios.defaults.headers.common["Authorization"] = "Bearer " + response.data.access
                })
                .catch((error) => {
                    console.log(error)

                    this.removeToken()
                })
        }
    },
})

LogIn.vue

<template>
    <div class="max-w-7xl max-h-screen h-[740px] mx-auto grid grid-cols-2 gap-24 mt-6 m-10">

        <div class="main-left">
            <div class="p-4 h-full">
                <div class="p-12 bg-hero-mutrah h-full rounded-lg bg-cover">
                </div>
            </div>
        </div>
        <div class="main-right">
            <div class="p-12 bg-white rounded-lg">
                <form class="space-y-6" v-on:submit.prevent="submitForm">
                    <div>
                        <label>Email</label>
                        <input type="email" v-model="form.email" placeholder="Your email address"
                            class="w-full mt-2 py-4 px-6 border border-gray-200 rounded-lg">
                    </div>
                    <div>
                        <label>Password</label>
                        <input type="password" v-model="form.password" placeholder="Password"
                            class="w-full mt-2 py-4 px-6 border border-gray-200 rounded-lg">
                    </div>

                    <div>
                        <button class="py-4 px-6 bg-orange-500 font-bold text-white rounded-lg">Log In</button>
                    </div>
                </form>
            </div>
        </div>
    </div>
</template>

<script>
import axios from 'axios'
import { useUserStore } from '@/stores/user'
import { useToastStore } from '@/stores/toast'

export default {

    setup() {
        const userStore = useUserStore()
        const toastStore = useToastStore()

        return {
            userStore,
            toastStore
        }
    },

    data() {
        return {
            form: {
                email: '',
                password: '',
            },
            errors: []
        }
    },

    methods: {
        async submitForm() {
            this.errors = []

            if (this.form.email === '') {
                this.errors.push('Your email is missing')
            }

            if (this.form.password === '') {
                this.errors.push('Your password is missing')
            }

            if (this.errors.length === 0) {
                await axios
                    .post('/api/login/', this.form)

                    .then(response => {
                        console.log({ 'message': response.data })
                        this.userStore.setToken(response.data)

                        axios.defaults.headers.common["Authorization"] = `Bearer ${response.data.access}`;

                    })
                    .catch(error => {
                        this.toastStore.showToast(5000, 'Oops we could not find matching credentials', 'bg-red-300')
                    })

                await axios
                    .get('/api/me/')
                    .then(response => {
                        console.log({ 'message': response })
                        this.userStore.setUserInfo(response.data)
                        this.userStore.initStore(response.data)

                        this.$router.push('/')

                    })
                    .catch(error => {
                        console.log('error', error)
                    })
            }

            else {
                if (this.errors == 'Your email is missing')
                    this.toastStore.showToast(5000, 'Your email is missing', 'bg-red-300')

                if (this.errors == 'Your password is missing')
                    this.toastStore.showToast(5000, 'Your password is missing', 'bg-red-300')
            }

        }
    },
}

</script>

这段代码是工作,因为我有正确的预期行为时,用户登录。但是我需要在每个页面之前检查用户角色,而不仅仅是在登录时。
有什么建议吗?
感谢您花时间阅读!

nx7onnlm

nx7onnlm1#

看起来你在Vue 3和Pinia上遇到了一个问题,那就是在导航到每个页面之前检查用户的角色。你是正确的,问题可能源于路由器在用户状态准备好之前被加载。要解决这个问题,您可以将Vue Router的beforeEach导航保护与Pinia的商店结合使用。
下面是代码的修改版本,它应该可以帮助您实现所需的行为:
1.更新路由器配置:

import { createRouter, createWebHistory } from 'vue-router';
    import { useUserStore } from '@/stores/user';
    
    const router = createRouter({
      history: createWebHistory(import.meta.env.BASE_URL),
      routes: [
        {
          path: '/',
          name: 'home',
          component: HomeView
        },
        {
          path: '/about',
          name: 'about',
          component: AboutView
        },
        {
          path: '/:pathMatch(.*)*',
          name: 'NotFound',
          component: NotFoundView
        },
      ]
    });

    router.beforeEach(async (to, from, next) => {
      const store = useUserStore();
      await store.initStore(); // Make sure the user state is initialized.
    
      // Now, you can access the user's role and perform authorization checks.
      const userRole = store.user.role;
    
      // You can implement your authorization logic here.
      // For example, check if the userRole has access to the route.
      // You can also handle cases like redirecting to a login page if not authenticated.
    
      // For simplicity, let's assume all users can access all routes.
      // Replace this with your actual authorization logic.
      if (userRole !== null) {
        next(); // Allow navigation.
      } else {
        // Redirect to a login page or show an unauthorized message.
        // Modify this according to your application's requirements.
        next('/login'); // Redirect to a login page if the user's role is not defined.
      }
    });
    
    export default router;

更新你的user.js商店:在user.js存储中,似乎有一个initStore操作来初始化用户状态。您可以在应用启动时调用此操作,以确保在任何路线导航之前初始化用户状态。

// user.js
    
    import { defineStore } from 'pinia';
    import axios from 'axios';
    
    export const useUserStore = defineStore('user', {
      // ... (your existing state and actions)
    
      actions: {
        initStore() {
          if (localStorage.getItem('user.access')) {
            // ... (your existing code to initialize the user state)
          }
        }
      }
    });

通过遵循这些步骤,您的Vue 3应用程序现在应该在每个页面导航之前正确检查用户的角色,即使在登录之后也是如此。请记住,使用基于用户角色的实际授权检查来替换router.beforeEach保护中的占位符逻辑。

相关问题