当使用pinia存储挂载vue页面时,无法从本地存储获取项目



我正在构建一个使用vue 3组合api和pinia store的应用程序。当用户登录成功时,我将令牌存储在localStorage中,然后将用户推送到仪表板。在安装仪表板视图时,我想使用存储数据,这意味着调用一个仅授权的端点,因此我创建了一个pinia存储profile

import { defineStore } from "pinia";
import http from '../http-common';
export const useProfile = defineStore("profile", {
state: () => {
return {
userSummary: {},
error: null
};
},
getters: {
async userSummary() {
try {
const res = await http.get('dashboard');
this.userSummary = res.data.data;
} catch (error) {
this.error = error;
}
}
}
});

http-common.js中,我使用axios并从localStorage获取存储的令牌,并将其用于api调用

import axios from "axios";
export default axios.create({
baseURL: "https://api-domain/api/",
headers: {
"Authorization": `Bearer ${localStorage.getItem('accessToken')}`
}
});

signin whereaccessToken存放在localStorage

const loginUser = async () => {
try {
const res = await axios.post(
"https://api-domain/api/login",
signin.value,
{
headers: {
Accept: "application/json",
"Content-Type": "application/json",
},
}
);
localStorage.setItem("accessToken", res.data.data.accessToken);
localStorage.setItem("verified", res.data.data.verified);
// redirect home
router.push({ name: "dashboard" });
} catch (error) {
error = error.response.data.message;
alert(error);
}
};

,在仪表板视图中,我使用配置文件存储

<script setup>
import {onMounted} from "vue";
import {useProfile} from "../stores/profile";
const profile = useProfile();
onMounted(() => {
profile.userSummary;
});
</script>

问题是,当仪表板视图挂载时,api调用失败,我可以看到请求授权为空Authorization: Bearer null。从开发人员工具检查,我可以看到accessToken实际上是设置的,而不是null。

如何解决这个问题?

您期望localStorage在这里响应:

headers: {
"Authorization": `Bearer ${localStorage.getItem('accessToken')}`
}

但它不是。设置为axios配置的header.Authorization的字符串以后不会神奇地改变,当您覆盖本地存储中的令牌值时。


这是你当前正在做的:

  1. 您通过从本地存储读取accessToken来配置axios(尚未进行身份验证调用)。即使本地存储中有令牌,它也是来自上一个会话的,因此可能会过期。

  2. 用户登录,并将令牌设置为本地存储。

  3. 您继续使用在步骤1中配置的axios实例。


你可能想使用axios拦截器。这是因为拦截器是在发出/接收请求/响应时运行的,而不是在配置axios时运行的。

  • 你需要一个请求拦截器:如果当前电话去你的API和如果它不是一个身份验证请求,你读到的令牌存储(典型的身份验证存储)。

    • 如果令牌为真,则将标头附加到调用并返回
    • 如果令牌为false,则将请求包装在承诺中,将该承诺推入数组并返回承诺(稍后将在您拥有令牌后解析)。
      你需要手表的令牌。当令牌更改时,如果它为真,则重新发送promises数组的所有成员并清空该数组。
      当我说重新发送时,我的意思是用使用原始请求的.config创建的新axios请求来解析存储的承诺。通过这样做,您将重新对请求进行排队,从而使其再次通过请求拦截器。它们现在将通过,因为您有一个令牌,并且它们将获得附加的身份验证头。
  • 您还需要一个响应拦截器:如果当前调用已返回401(这意味着令牌刚刚过期),

    • 将调用推送到承诺数组,
    • 从存储中删除令牌
    • 实例化一个新的认证调用。
      当这个调用被解析时,如果应用程序对API进行任何其他调用,它们将以承诺数组结束(因为你现在没有令牌)。

显然,如果错误有不同的代码(除了401),您需要抛出它:它是合法的。

差不多了。

它确实有一个小问题,但是:如果你的身份验证被打破了,而不是提供有效的令牌,它提供了不工作的令牌,你的应用程序将在获得错误的令牌后不断尝试身份验证,这将再次失败,然后一个新的身份验证请求将被制作,在一个无休止的循环中。
要解决这个问题,您可能需要对身份验证失败设置一个计数器。我没有在上面包括它,因为我认为尽可能清晰地呈现主要的身份验证逻辑是很重要的。