经过长时间的搜索,当访问令牌过期时,我为我的应用程序制定了这个解决方案。与其他服务的区别在于,我必须使用外部服务,在使用我的谷歌帐户登录时为我提供访问和刷新令牌。然后,当访问令牌过期时,我需要检索刷新令牌,将其发送到为我提供新访问和刷新令牌的服务。我见过一些实现,您不会等待收到 401 错误,而是计算令牌过期之前的时间并发送请求,以及处理待处理请求的其他实现。这将是一个用于管理我们的资产的内部应用程序,因此我预计单个用户不会有很多并发查询/请求,因此我避免了处理待处理请求的部分。
不幸的是,目前我无法尝试,因为该服务要到明年才能提供,但我想问一下它是否正确或者我是否需要添加更多逻辑
import React from "react";
import ReactDOM from "react-dom";
import App from "./App";
import { AppProvider } from "./context";
import {
ApolloProvider,
ApolloClient,
ApolloLink,
createHttpLink,
InMemoryCache,
from,
fromPromise,
} from "@apollo/client";
import { setContext } from "@apollo/client/link/context";
import { onError } from "@apollo/client/link/error";
const axios = require("axios").default;
const httpLink = createHttpLink({
uri: "http://localhost:4000",
});
const authLink = new ApolloLink((operation, forward) => {
const accessToken = localStorage.getItem("accessToken");
operation.setContext(({ headers }) => ({
headers: {
...headers,
authorization: accessToken ? `Bearer ${accessToken}` : "",
},
}));
return forward(operation);
});
const getNewToken = async () => {
try {
const { data } = await axios.post(
"https://xxx/api/v2/refresh",
{ token: localStorage.getItem("refreshToken") }
);
localStorage.setItem("refreshToken", data.refresh_token);
return data.access_token;
} catch (error) {
console.log(error);
}
};
const errorLink = onError(
({ graphQLErrors, networkError, operation, forward }) => {
if (graphQLErrors) {
for (let err of graphQLErrors) {
switch (err.statusCode) {
case 401:
return fromPromise(
getNewToken().catch((error) => {
// Handle token refresh errors e.g clear stored tokens, redirect to login
return;
})
)
.filter((value) => Boolean(value))
.flatMap((accessToken) => {
const oldHeaders = operation.getContext().headers;
// modify the operation context with a new token
operation.setContext({
headers: {
...oldHeaders,
authorization: `Bearer ${accessToken}`,
},
});
// retry the request, returning the new observable
return forward(operation);
});
}
}
}
}
);
const client = new ApolloClient({
link: from[(authLink, errorLink, httpLink)],
cache: new InMemoryCache(),
});
ReactDOM.render(
<React.StrictMode>
<ApolloProvider client={client}>
<AppProvider>
<App />
</AppProvider>
</ApolloProvider>
</React.StrictMode>,
document.getElementById("root")
);