成功登录后如何更新 ApolloClient 授权标头?
Posted
技术标签:
【中文标题】成功登录后如何更新 ApolloClient 授权标头?【英文标题】:How to update ApolloClient authorization header after successful login? 【发布时间】:2019-10-07 12:36:30 【问题描述】:基本上,我们在index.js
文件中有这个设置ApolloProvider
授权以进行查询/突变。
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
import ApolloClient from "apollo-boost";
import ApolloProvider from "react-apollo";
let session = localStorage.getItem("session");
let authorization = "";
if(session)
let sessionObj = JSON.parse(session);
authorization = sessionObj.access_token
const graphQLServerURL = process.env.REACT_APP_API_URL;
const client = new ApolloClient(
uri: graphQLServerURL + "/graphql",
headers:
authorization,
);
ReactDOM.render(
<ApolloProvider client=client>
<App />
</ApolloProvider>
, document.getElementById('root'));
当应用首次加载时,authorization
标头将为 null
。然而,在<App>
组件中,我们有一个<Login>
组件,它基本上使用username
和password
进行发布请求。在.then()
方法中成功请求后,我们有:
.then(res =>
if (res === 200)
localStorage.setItem("session", JSON.stringify(res.data));
history.push("/dashboard");
);
所以发生的情况是用户被重定向到具有<Query>
组件的<Dashboard>
组件(列出一些数据)。但是,ApolloClient
中的 authorization
仍然是 null
,直到我点击刷新。使用push
不会重新加载<App>
组件(因此它会从本地存储中获取更新的session
)。
我应该如何做到这一点,在登录成功发布请求后,来自index.js
的授权获得最新的session
对象,而无需重新加载整个应用程序?
【问题讨论】:
Apollo Client 3 文档似乎显示了如何在每次调用中包含标头。 apollographql.com/docs/react/networking/authentication/#headerncabral 下面有答案 【参考方案1】:如果你使用apollo-boost
,你可以使用request
函数
const getToken = () =>
const token = localStorage.getItem('token');
return token ? `Bearer $token` : '';
;
const client = new ApolloClient(
uri: `$graphQLServerURL/graphql`,
request: (operation) =>
operation.setContext(
headers:
authorization: getToken(),
,
);
,
);
【讨论】:
如果您使用的是 Apollo Client 3,您应该查看以下 ncabral 使用 Apollo Client 的 setContext 提供的答案。最新的 Apollo Client API 文档中引用了该答案(请参阅 ncabral 答案中的 REF 链接)。【参考方案2】:您必须使用链接。 Ref
const httpLink = createHttpLink(
uri: '/graphql',
);
const authLink = setContext((_, headers ) =>
// get the authentication token from local storage if it exists
const token = localStorage.getItem('token');
// return the headers to the context so httpLink can read them
return
headers:
...headers,
authorization: token ? `Bearer $token` : "",
);
const client = new ApolloClient(
link: authLink.concat(httpLink),
);
【讨论】:
但是,我们的应用正在使用来自apollo-boost
的ApolloClient
,它无法识别link
选项。
当,希望这不会被否决。对于新的 Apollo Client 3,这是首选路径,可以节省我的时间。由于投反对票,完全跳过了这个答案>.>
@afreeland 同意,这个答案被严重低估了。【参考方案3】:
我遇到了类似的问题。我想将token
用于ApolloProvider
。当应用程序第一次渲染时,我无法访问localStorage
,因此 WebSocket 无法工作。
我做了什么?
我创建了一个自定义挂钩并使用useEffect
和useState
来更新它。
import
ApolloClient, InMemoryCache, HttpLink, split,
from '@apollo/client';
import WebSocketLink from 'apollo-link-ws';
import getMainDefinition from 'apollo-utilities';
import useEffect, useState from 'react';
import config from './index';
export const useConfigClient = () =>
const [authTokenStorage, setAuthTokenStorage] = useState(localStorage.getItem('token')); // by default it is null
const cache = new InMemoryCache();
const httpLink = new HttpLink(
uri: config.GRAPHQL_URL,
headers:
authorization: `Bearer $authTokenStorage`,
,
);
const wsLink = new WebSocketLink(
uri: config.GRAPHQL_WS,
options:
reconnect: true,
connectionParams:
headers:
authorization: `Bearer $authTokenStorage`,
,
,
,
);
const link = split(
( query ) =>
const definition = getMainDefinition(query);
return (
definition.kind === 'OperationDefinition'
&& definition.operation === 'subscription'
);
,
wsLink,
httpLink,
);
useEffect(() =>
setAuthTokenStorage(localStorage.getItem('token')); // as soon as it is available just update the token
, []);
const client = new ApolloClient(
cache,
link,
);
return client;
;
我是如何使用自定义挂钩的?
import ApolloProvider from '@apollo/client';
import useConfigClient from '../config/apolloConfig'; // import the hooks
<ApolloProvider client=useConfigClient()>
// ... add your component
</ ApolloProvider>
我不确定这是修复它的最佳方法。目前,它有效,我希望它能对其他人有所帮助。
【讨论】:
当我使用这个时,我得到一个无效的 Hook 错误。 “钩子只能在函数组件的主体内部调用。”这是有道理的,因为这里使用的函数不是组件。【参考方案4】:实现此目的的另一种方法是您可以使用 apollo-config 中的 setLink
方法。
之后,导出一个函数,一旦你有你的令牌就会设置标题。
import ApolloClient, InMemoryCache, HttpLink from '@apollo/client';
import setContext from '@apollo/client/link/context';
const httpLink = new HttpLink(uri: '/graphql');
let config = new ApolloClient(
link: httpLink,
cache: new InMemoryCache(),
);
export function setGraphqlHeaders()
const token = localStorage.getItem(AUTH_TOKEN);
const authLink = setContext((_, headers) =>
return
headers:
...headers,
authorization: token || null,
,
;
);
config.setLink(authLink.concat(httpLink));
希望,这会对某人有所帮助
【讨论】:
【参考方案5】:praveen-me
的答案在 2021 年对我有用 Nextjs。我尝试了ncabral
的答案,但我不确定它会自动为我更新 JWT。
在praveen-me
的回答之上,我为我在代码中添加了一些内容:
userToken.ts
const userTokenKey = 'userToken'
export const getUserToken = () =>
try
return localStorage.getItem(userTokenKey)
catch (error)
...
export const setUserToken = (token: string) =>
try
localStorage.setItem(userTokenKey, token)
catch (error)
...
apolloClient.ts
import ApolloClient, createHttpLink, InMemoryCache from "@apollo/client";
import setContext from "@apollo/client/link/context";
import getUserToken from "./userToken";
const httpLink = createHttpLink(
uri: process.env.NEXT_PUBLIC_GRAPHQL_URL
)
let client = new ApolloClient(
// link: authLink.concat(httpLink),
link: httpLink,
cache: new InMemoryCache(),
);
export function updateGraphqlHeaders()
const authLink = setContext((_, headers ) =>
const token = getUserToken();
return
headers:
...headers,
authorization: token ? `Bearer $token` : "",
)
client.setLink(authLink.concat(httpLink))
updateGraphqlHeaders() // Init
export default client;
_app.tsx
...
import ApolloProvider from "@apollo/client";
import client from "../auth/apolloClient";
function MyApp( Component, pageProps : AppProps)
return (
<ApolloProvider client=client>
<Component ...pageProps />
</ApolloProvider>
);
export default MyApp;
【讨论】:
以上是关于成功登录后如何更新 ApolloClient 授权标头?的主要内容,如果未能解决你的问题,请参考以下文章
ApolloClient:optimisticResponse 在更新期间变为“未定义”
使用 Spring Security 成功登录后如何正确更新登录日期时间?
使用 Spring Security 成功登录后如何正确更新登录日期时间?