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

问题描述 投票:0回答:1

背景:我有一个 React Native 应用程序,它使用基于 Microsoft 令牌的身份验证与 .NET 框架后端(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)
      }
    })
    },

处理错误的代码

  async handleError(error: any, originalRequest: any): Promise<any> {
    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 = []
  },

我确实知道很难找到解决方案,但还是把它放在那里,因为我们已经被这个问题困扰了很长时间。

c# reactjs react-native fetch refresh-token
1个回答
0
投票

听起来像是竞争条件(有时命令序列按预期进行,有时不是,意思是 - 有些命令在其他命令开始之前没有完全执行)

在对您的代码进行简短扫描后,我想我已经得到了一些东西

函数

fetchSSLPinning
是一个
async
函数(感谢下面的代码示例)

 var response = await fetchSSLPinning(route, options)

但是,当从另一个函数(在承诺内)解析时,它的使用方式如下:

resolve(fetchSSLPinning(originalRequest.route, options))

让我们来分解一下:

const result = fetchSSLPinning(originalRequest.route, options)
resolve(result)

很容易看出

result
是一个
Promise
,这不是解决promise的好方法。如果您打算做出一系列承诺,则首选使用
await
关键字(作为时间的函数更具可读性和可维护性)

我的猜测是你想等待结果,使用:

const result = await fetchSSLPinning(originalRequest.route, options)
resolve(result)

或者

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

这可能是用例(并且可能 - 您的代码中还有更多这样的用例)

并且,是的 - 如果您在项目中使用

async
/
await
关键字,请尝试对齐其余部分,以保持一致性

希望这些信息有帮助

© www.soinside.com 2019 - 2024. All rights reserved.