在 bakcend 上,我有两个端点用于刷新和验证 jwt 令牌。 在客户端,我想在每个需要授权凭据的请求之前验证并刷新它们。
因此,在用户上下文提供程序中,我创建了用于验证和刷新令牌的异步函数,并创建了将这两者结合起来的函数。
这是我的用户上下文
import { createContext, useState, useEffect } from "react"
const backend_url = process.env.REACT_APP_BACKEND_URL
const UserContext = createContext({})
const UserContextProvider = ({children}) => {
const [user, setUser] = useState(JSON.parse(localStorage.getItem('tokens')))
// validate user token
const tokenIsValid = async () => {
return await fetch(`${backend_url}/auth/verify`,{
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({token: `${user.access}`})
})
.then(response => response.ok)
.catch(error => console.log(error))
}
// refresh token
const refreshToken = async () => {
return await fetch(`${backend_url}/auth/refresh`, {
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify({refresh: `${user.refresh}`})
})
.then(response => {
if(response.ok){
return response.json()
}
throw new Error("refresh Token error")
})
.then(data => {
const _user = {...user, access: data.access}
setUser({..._user})
localStorage.setItem('tokens', JSON.stringify({..._user}))
})
.catch(error => console.log(error))
}
// set params for authFetch
const getParams = params => {
return {
...params,
headers: {
...params.headers,
"Authorization": `Bearer ${user.access}`
}
}
}
// Authorized request. Token is validated, refreshed(if needed)
// and used in authorization header during fetch
const authFetch = async (url, params) => {
const isValid = await tokenIsValid()
if (isValid){
return fetch(url, getParams(params))
}
// check if token is refreshable
try{
await refreshToken()
return fetch(url, getParams(params))
}
catch(e){
setUser(null)
localStorage.removeItem('tokens')
throw new Error("Refresh token is invalid")
}
}
return (
<UserContext.Provider value={{user, setUser, authFetch}}>
{children}
</UserContext.Provider>
)
}
export {UserContext, UserContextProvider}
这是客户端尝试获取用户照片 url 时的使用示例
export default function AuthWidget(){
const {user, setUser, authFetch} = useContext(UserContext)
const [userPhoto, setUserPhoto] = useState("")
const handleAuthorization = response => {
fetch(`${backend_url}/auth/telegram/widget`, {
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify(response)
})
.then(response => response.json())
.then(data => {
setUser(data)
localStorage.setItem('tokens', JSON.stringify(data))
})
.catch(error => console.log(error))
}
// set user photo
useEffect(() => {
authFetch(`${photo_url}`, {
method: "GET"
})
.then(response => response.json())
.then(data => setUserPhoto(data.photo_url))
.catch(error => console.log(error))
}, [user])
return (
<Container fluid className='p-0'>
{user ?
<Link to="/user">
<Image src={userPhoto} roundedCircle height="40px"/>
</Link>
:
<TelegramLoginButton
className="mt-2"
dataOnauth={handleAuthorization}
buttonSize="small"
usePic={false}
botName={process.env.REACT_APP_BOT_NAME}
/>
}
</Container>
)
}
问题在于,当访问令牌过期时,authFetch 不会等待刷新令牌完成。 来自后端的日志:
[27/Jul/2023 17:41:34] "POST /auth/verify HTTP/1.1" 401 65
[27/Jul/2023 17:41:34] "POST /auth/refresh HTTP/1.1" 200 254
Unauthorized: /user/photo
[27/Jul/2023 17:41:34] "GET /user/photo HTTP/1.1" 401 183
[27/Jul/2023 17:41:34] "POST /auth/verify HTTP/1.1" 200 2
[27/Jul/2023 17:41:34] "GET /user/photo HTTP/1.1" 200 80
我该如何解决这个问题?