React Query在Key斗篷刷新令牌后使用过时的头(旧JWT令牌)



我对React Native编程很陌生,但这里是上下文。

我们在项目中使用React Query和Axios库。作为AuthManager,我们使用Keycapture,对于管理身份验证状态的库,我们有React Native Keycapture。我们遇到了一个乏味的问题,我们的服务器在一定时间后根据我们的请求随机响应401,有时还会导致应用程序崩溃。

我们复制了使Keycloft的Bearer代币仅在1分钟后过期的错误。这几乎直接导致了401错误,我们想知道为什么会发生这种情况。

假设我们有一个屏幕,上面有一些";活动;这个屏幕是用户将看到的第一个东西。为了处理请求,在我们的代码中,我们使用了一些引用useQuery的自定义挂钩,例如:

export function useActivities(): UseQueryResult<ActivityList> {
const { headers } = useHeaders();
return useQuery(
['activities', today.start],
() => getActivitiesList(headers), // Note 1
{
enabled: !!today.start,
}
);
}

其中重要的一点是,我们useHeaders使用Keycloft令牌和我们的领域设置来获得更新的标头。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 };
}

现在,getActivitiesList简单到五个:

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

所有这些的问题是,无论何时Key斗篷将触发刷新令牌,keycloak对象内的令牌都会更改,useActivities内的标头也会更改但是如果我在getActivitiesList(// Note 2)内,甚至在查询函数(// Note 1)内打印标头,则标头将不会更新。有时它只会导致发出两个请求(一个失败并显示错误,另一个实际工作),有时应用程序在没有任何解释的情况下崩溃。这让我想知道为什么查询函数不会更新它的头;旧的";CCD_ 10内部的标头。

目前,我们正在从两个不同的方面缓解这个问题。

  1. 在keycaptain init之后,我们立即向axios传递一个带有axios.defaults.headers.common.Realm = KEYCLOAK_REALM;的全局标头
  2. 在从Keycloft接收到有效令牌后,我们用一个新的令牌覆盖Authorization标头:axios.defaults.headers.common.Authorization = 'Bearer ${keycloak?.token}';

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

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

差不多一年后,我可以承认我很笨。事实上,解决方案非常简单。

TL;DR

如果值是一个在渲染后不会更改的常量,则不能期望获得新值。但你可以";获取";一个新的令牌,用function请求,而不仅仅是";初始化";作为CCD_ 14。

// 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时,React Native渲染周期将渲染钩子一次。钩子本身将初始化const headers,并且它将永远不会再次更改,直到发生重新渲染。但是,如果const headerstimeout时间之后不会改变,则令牌将无效。如果某个东西将导致该钩子的重新呈现,则const headers将被重新初始化,因此它将包含一个新的令牌。

这就是为什么有时它会起作用,有时却不起作用。解决方案非常简单。由于useKeycloak总是有一个有效且新鲜的令牌,而不是使用简单的变量声明:

const headers = something

我们可以使用

const headers = () => something

这将始终返回一个新的(最终刷新的)东西,在这种情况下,这将是我的宝贵令牌。

最新更新