React Native中刷新令牌的神秘问题

3
我会尽可能地解释我遇到的问题。
背景:我有一个React Native应用程序,使用Microsoft令牌基础身份验证与.Net Framework后端(Web API)通信。令牌在30分钟后过期,然后当进行下一次调用时,服务器会响应401,然后刷新令牌API被调用,新的刷新令牌被创建,应用程序使用这个新的令牌。而在等待刷新令牌时,其他调用被排队在客户端上,并且一旦令牌到达,它们将使用新的令牌被调用。
问题(谜团):问题在于有时从服务器返回状态码0。我们试图找出原因,但没有成功。我们还看到的模式是,当为刷新令牌调用时,后端删除旧令牌并生成新令牌。但是当应用程序调用新的API时,它仍然使用相同的旧令牌而不是新令牌。这并不总是发生。可能是排队的调用没有被排队吗?是否存在其他问题?
工具:我们使用四台运行IIS 10的专用服务器。我们有一个负载均衡器来平衡负载。在前端,我们使用React Native 0.64.2。我们使用react-native-ssl-pinning进行调用。
代码:以下是代码
async request( route: string, action: string, params: any = null, isJson: boolean = true, ) {
var options = await this.handleOption(action, params, isJson)

route = Config.API_BASE_URI + route

return new Promise(async resolve => {
  try {
    var response = await fetchSSLPinning(route, options)
    return resolve(response)
  } catch (e) {
    // // console.log(e)
    var originalRequest = {
      route,
      options,
    }
    var p = this.handleError(e, originalRequest)
    return resolve(p)
  }
})
},

处理错误的代码

异步函数handleError(error: any, originalRequest: any): Promise { const errorResponse = error

if (this.isTokenExpiredError(errorResponse)) {
  return this.resetTokenAndReattemptRequest(error, originalRequest)
}
this.dispatchError(true, errorResponse.status)
return Promise.reject(error)
},

获取新的刷新令牌代码

  async resetTokenAndReattemptRequest(error: any, originalRequest: any) {
    try {
      
      const resetToken = await TokenStorage.getToken() 
      if (!resetToken) {
        return Promise.reject(error)
      }

     
      const retryOriginalRequest = new Promise(resolve => {
        this.addSubscriber((accessToken: string) => {
          var options = {
            ...originalRequest.options,
            headers: {
              ...originalRequest.options.headers,
              authorization: 'Bearer ' + accessToken,
            },
          }
         
          resolve(fetchSSLPinning(originalRequest.route, options))
        })
      })
      if (!this.isAlreadyFetchingAccessToken) {
        this.isAlreadyFetchingAccessToken = true
        const refreshToken = await TokenStorage.getRefreshToken()
        const userName = await TokenStorage.getUsername()

        const response = await AuthenticationService.refreshToken(
          refreshToken,
          userName,
        )

        if (!response) {
         
          this.isAlreadyFetchingAccessToken = false
          this.dispatchError(true, '404')
          return Promise.reject(error)
        }
        
        await TokenStorage.storeTokenInfo(
          response.access_token,
          response.refresh_token,
          response.userName,
        ) // save the newly refreshed token for other requests to use

        const newToken = response.access_token
        this.isAlreadyFetchingAccessToken = false
        this.onAccessTokenFetched(newToken)
      }
      return retryOriginalRequest
    } catch (err) {
      
      this.isAlreadyFetchingAccessToken = false
      this.dispatchError(true, err.status)
      return Promise.reject(err)
    }
  },

运行所有订阅者的代码

onAccessTokenFetched(accessToken: string) {
    // When the refresh is successful, we start retrying the requests one by one and empty the queue
    this.subscribers.forEach((callback: any) => {
      callback(accessToken)
    })
    this.subscribers = []
  },

我知道想出一个解决方案很难,但是我们已经被卡住了很长时间,所以把它公开出来。


同样的问题,你找到了什么吗?谢谢 - undefined
你可能在订阅令牌变更事件和调用令牌变更事件的顺序上出现了错误。在调用this.addSubscriber((accessToken: string)之前,你先调用了this.onAccessTokenFetched(newToken) - undefined
1个回答

0
听起来像是一个竞争条件(有时命令的顺序按照预期进行,有时不是,意味着有一些命令在其他命令开始之前没有完全执行)。
经过对您的代码的简短扫描,我想我找到了一些东西。
函数fetchSSLPinning是一个async函数(感谢下面的代码示例)。
 var response = await fetchSSLPinning(route, options)

然而,当从另一个函数中解析(在一个Promise中),它的使用方式如下:
resolve(fetchSSLPinning(originalRequest.route, options))

让我们来分解一下:
const result = fetchSSLPinning(originalRequest.route, options)
resolve(result)

很容易看出,result是一个Promise,这并不是解决Promise的好方法。如果你打算使用一系列的Promise,使用await关键字是更好的选择(更易读和易于维护,随着时间的推移作为一个函数)。
我猜你想要等待结果,可以使用以下方式:
const result = await fetchSSLPinning(originalRequest.route, options)
resolve(result)

或者

fetchSSLPinning(originalRequest.route, options).then(resolve)

这可能是使用案例(而且很可能在你的代码中还有更多类似的情况)。
而且,如果你在项目中使用了async / await关键字,尽量使其与其他部分保持一致,以保持一致性。
希望这些信息对你有所帮助。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接