React查询使用过时的标头(旧的JWT令牌)之后

发布于 2025-02-13 00:50:36 字数 2582 浏览 0 评论 0原文

我在反应生态编程方面非常陌生,但这是上下文。

我们正在使用 React Query 和我们项目中的Axios库。作为Authmanager,我们正在使用KeyCloak,并且在库管理Auth Status的库中,我们有 a>。我们的服务器在一定的时间后根据我们的要求随机响应401,有时还会遇到一个繁琐的问题。

我们重现了错误的错误,使Keycloak的承载令仅在1分钟后到期。这几乎造成了401个错误,我们想知道为什么会发生这种错误。

假设我们有一个带有一些“活动”的屏幕,并且此屏幕是用户首先看到的屏幕。对于处理请求,在我们的代码中,我们使用一些引用useQuery的自定义钩子,例如:

export function useActivities(): UseQueryResult<ActivityList> {
    const { headers } = useHeaders();

    return useQuery(
        ['activities', today.start],
        () => getActivitiesList(headers), // Note 1
        {
            enabled: !!today.start,
        }
    );
}

重要的一点是我们useheaders用keycloak获取我们的更新标头令牌和我们的领域设置。 USEHEADERS几乎无处不在。

export function useHeaders(): UseHeaders {
    const { keycloak } = useKeycloak();
    const KEYCLOAK_REALM = remoteConfig().getString('KEYCLOAK_REALM');

    const headers = {
        Authorization: `Bearer ${keycloak?.token}`,
        Realm: KEYCLOAK_REALM,
    };

    return { headers };
}

现在,getActivitivitivitiesList很简单,如五:

async function getActivitiesList(headers: UseHeaders['headers']): Promise<ActivityList> {
    const url = `${BASE_URL}${VERSION}/activities/grouped?end=${end}&start=${start}`;

     // Note 2
    return axios
     .get(url, { headers })
     .then((res) => res.data)
     .catch((e) => console.error('Error fetching grouped activities:', e));
}

所有这些的问题是,每当KeyCloak触发刷新令牌时,keycloak>对象已更改,UseActivities内的标题已更改,但如果我在我的内部打印标题,则 getActivitivitiesList//注意2),甚至在查询函数(// Note 1)中,标题将不会更新。有时,这只是导致提出两个请求(一个失败并显示错误,另一个实际上有效),其他时候该应用程序崩溃而没有任何解释。 This makes me wonder why the query function will not update its headers and passed the "old" headers inside getActivitiesList.

目前,我们正在以两个不同的观点来减轻此问题。

  1. 给Axios全局标头
  2. 在KeyCloak Init之后,我们立即通过axios.defaults.headers.common.realm = KeyCloak_Realm;从KeyCloak接收有效的令牌后,立即将其传递 。 axios.defaults.headers.common.authorization ='bearer $ {keycloak?.token}';

这不是一个完美的解决方案,我们在这里搜索有关此问题的一些信息。

有人得到类似的东西吗?如何管理USEQUERY函数中的令牌刷新?

I'm pretty new in React-Native programming, but here is the context.

We are using React Query and Axios libraries in our project. As AuthManager we are using Keycloak and for the library managing auth status we have React Native Keycloak. We encounter a tedious problem with our server responding randomly 401 at our requests after a certain amount of time, bringing also to the app crash sometimes.

We reproduced the error making the Bearer Token of Keycloak expire after only 1 minute. This caused almost immediatly the 401 error and we wondered why this is happening.

Let's say we have a screen with some "Activities" and this screen is the first thing the user will see. For handling requests, in our code we use some custom hooks that reference useQuery, for example:

export function useActivities(): UseQueryResult<ActivityList> {
    const { headers } = useHeaders();

    return useQuery(
        ['activities', today.start],
        () => getActivitiesList(headers), // Note 1
        {
            enabled: !!today.start,
        }
    );
}

The important point of it is that we useHeaders to get our updated headers with the Keycloak token and our realm settings. useHeaders is almost everywhere in our app.

export function useHeaders(): UseHeaders {
    const { keycloak } = useKeycloak();
    const KEYCLOAK_REALM = remoteConfig().getString('KEYCLOAK_REALM');

    const headers = {
        Authorization: `Bearer ${keycloak?.token}`,
        Realm: KEYCLOAK_REALM,
    };

    return { headers };
}

Now, the getActivitiesList is simple as five:

async function getActivitiesList(headers: UseHeaders['headers']): Promise<ActivityList> {
    const url = `${BASE_URL}${VERSION}/activities/grouped?end=${end}&start=${start}`;

     // Note 2
    return axios
     .get(url, { headers })
     .then((res) => res.data)
     .catch((e) => console.error('Error fetching grouped activities:', e));
}

The problem with all of that is that whenever Keycloak will trigger the refresh token, the token inside keycloak object is changed, the headers inside useActivities are changed BUT if I print the headers inside my getActivitiesList (// Note 2), or even inside the query function (// Note 1), headers will not be updated. Sometimes it just causes to make two requests (one that fails and show error, the other one actually working), some other times the app crashes without any explain. This makes me wonder why the query function will not update its headers and passed the "old" headers inside getActivitiesList.

For now we are mitigating this problem in two different points.

  1. After keycloak init, we pass immediatly to axios a global header with axios.defaults.headers.common.Realm = KEYCLOAK_REALM;
  2. After receiving a valid token from Keycloak, we overwrite the Authorization header with a new one: axios.defaults.headers.common.Authorization = 'Bearer ${keycloak?.token}';

This is not a perfect solution and we are here to search some info about this problem.

Someone get something similar? How to manage the token refresh in useQuery function?

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

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

发布评论

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

评论(1

你是年少的欢喜 2025-02-20 00:50:36

近一年后,我可以承认我很愚蠢。实际上,解决方案非常简单。

tl; dr

一个人不能期望获得新的价值,如果值是一旦渲染就不会改变的const。但是,您可以“获取”一个新的令牌,并请求使用函数,而不仅仅是“初始化”为const

// This is a function, not a simple const anymore
const headers = () => {
    Authorization: `Bearer ${keycloak?.token}`,
    Realm: KEYCLOAK_REALM,
};

// Usage, please note the "()" after headers, it's a function now!
axios.get(url, { headers() })

长答案

使用useheaders反应本地渲染循环将使挂钩一次。挂钩本身将初始化const标题,它将永远不会再改变,直到重新渲染会发生。但是,如果const标题timeout时间之后不会更改,则令牌将无效。如果某些东西会导致重新渲染该钩子,则将重新定位const标题,因此它将包含一个新的令牌。

这是有时会起作用的原因,有时行不通。解决方案非常简单。由于usekeycloak始终是一个有效且新鲜的令牌,而不是使用简单的变量声明:

const headers = something

我们可以选择

const headers = () => something

始终返回新的新鲜(最终刷新)< em> ,在这种情况下,这将是我的宝贵令牌。

After almost a year I can admit I was dumb. Actually the solution was pretty simple.

TL;DR

One cannot expect to get fresh value, if value is a const that doesn't change once rendered. But you can "fetch" a new token, requesting with a function, not just "initialized" as const.

// This is a function, not a simple const anymore
const headers = () => {
    Authorization: `Bearer ${keycloak?.token}`,
    Realm: KEYCLOAK_REALM,
};

// Usage, please note the "()" after headers, it's a function now!
axios.get(url, { headers() })

LONG ANSWER

When using useHeaders the React-Native render cycle will render the hook once. The hook itself will initialize the const headers and it will never change again, until a re-render will happen. But if the const headers will not change after timeout time, token will be invalid. If something will cause a re-render of that hook, the const headers will be reinitialized, so it will contain a fresh token.

This was the reason sometimes it would work, sometimes not. The solution is pretty simple. Since useKeycloak has always a valid and fresh token, instead of going with a simple variable declaration:

const headers = something

we can go with a

const headers = () => something

that will always return a new fresh (eventually refreshed) something, that in this case would be my precious token.

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