2017-03-26 33 views
2

我發佈我的問題之前我已經搜索了很多,但不幸的是我無法找到解決我的問題。訪問令牌過期後處理多個未授權的請求

我開發了一個應用程序,連接到需要使用access tokenrefresh token進行驗證的服務器。

  • access token有效期爲1 hour,可以多次使用。

  • refresh token適用於1 month,當access token到期時使用。令牌只能使用一次。

當使用refresh token,我得到一個新的refresh token以及除access token

這裏是我的問題:

我寫了一個APIClientclass處理所有要求,即我的應用程序的需要。這class很好,除非access token到期。當access token到期時,此時將運行的所有請求都將失敗,並顯示401 code(未經授權)。

我想要的是找到一個解決方案,它將使用refresh token刷新access token,然後重試所有這些失敗狀態爲code 401的請求。請記住,刷新令牌的功能只能被調用一次,因爲refresh令牌僅適用於一次使用。

我想要做的是找到一種方法來編寫我的APIClientclass,以便它支持刷新令牌並重試所有失敗的請求。如果你告訴我如何實現這一點,我將非常感激。

請看下面的getRequest和sendRefreshRequest的源代碼。

func getRequestWith(requestType: FSRequestType, usingToken: Bool, parameters: RequestParameters?, completionClosure: @escaping (NetworkResult) -> Void) { 
    let sessioConfig = URLSessionConfiguration.default 
    let session = URLSession(configuration: sessioConfig, delegate: nil, delegateQueue: nil) 
    guard var URL = APIClient.baseURL?.appendingPathComponent(requestType.rawValue) else { return } 

    if let parameters = parameters { 
     URL = URL.appendingQueryParameters(parameters) 
    } 

    var request = URLRequest(url: URL) 
    request.httpMethod = "GET" 

    if usingToken { 
     let tokenString = "Bearer " + TokenManager.sharedManager.accessToken()! 

     request.addValue(tokenString, forHTTPHeaderField: "Authorization") 
    } 

    let task = session.dataTask(with: request) { (data, response, error) in 
     guard error == nil else { return completionClosure(.Error(error!.localizedDescription))} 
     guard let data = data else { return completionClosure(.Error("Could not load data")) } 

     let statusCode = (response as! HTTPURLResponse).statusCode 

     if statusCode == 401 { 
      //Handle refresh token 
     } else if statusCode == 200 { 
      let json = JSON(data: data) 
      let responseJSON = json["response"] 
      completionClosure(.Success(responseJSON)) 
     } else { 
      completionClosure(.Error("Returned status code \(statusCode) from Get request with URL: \(request.url!)")) 
     } 
    } 
    task.resume() 
} 

func sendRefreshRequest() { 
    let session = URLSession(configuration: .default) 

    guard var URL = APIClient.baseURL?.appendingPathComponent(FSRequestType.RefreshToken.rawValue) else {return} 

    let URLParams = [ 
     "refresh_token" : TokenManager.sharedManager.refreshToken()! 
    ] 

    URL = URL.appendingQueryParameters(URLParams) 
    var request = URLRequest(url: URL) 
    request.httpMethod = "GET" 

    let task = session.dataTask(with: request, completionHandler: { (data, response, error) in 
     let statusCode = (response as! HTTPURLResponse).statusCode 

     if statusCode == 200 { 

      let json = JSON(data: data!) 
      if TokenManager.sharedManager.saveTokenWith(json) { 
       print("RefreshedToken") 
      } else { 
       print("Failed saving new token.") 
       SessionManager.sharedManager.deauthorize() 
      } 
     } else if statusCode == 400 { 
      print("The refresh token is invalid") 
      SessionManager.sharedManager.deauthorize() 
     } 
    }) 
    task.resume() 
} 

感謝

+1

嗨偷偷摸摸。感謝您編輯我的問題。它現在更具可讀性。我編輯了我的問題,並添加了getRequest和refreshToken函數。我希望我的問題現在不太寬泛。 – zarzonis

+0

偉大的編輯。我收回了太寬泛的旗幟,希望你現在得到一些幫助。如果到那時你還沒有得到任何幫助,我會在晚些時候回家看看。 –

+0

謝謝潛行。非常感激。 – zarzonis

回答

0

我不代碼斯威夫特,所以我不能爲你提供的代碼,但是我認爲最簡單的方法是:

1:定義你completionBlockAPIClient

2:內部處理所有的呼叫您APIClient,在裏面你if (statusCode == 401)處理,

3:在當您完成刷新令牌時,您可以檢查completionBlock != nil是否再次請求數據,並在成功時返回回調。

當您在執行APIClient中的所有刷新方法時,在完成刷新並重新嘗試重新請求之前,不會返回completionBlock。

你的另一個叫做APIClient的類將等待completionBlock被返回,你可以傳遞completionBlock,直到你得到正確的標記爲止,並且已經進行了重試的調用。

查看這些線程以獲取有關如何定義completionBlock Swift的代碼,您會明白這一點。

Another Example 1

Another example 2

Another Example 3

0

假設我理解正確的問題,您描述一下自己借給Singleton模式。有一個存儲當前令牌的單身人士,並作爲請求門。

傳遞一個包含代碼的完成塊以啓動一個新的請求併爲訪問令牌帶來一個參數。如果令牌可用,則使用該令牌立即調用該塊。否則,對於第一個塊,它會使用刷新標記調用它並刪除刷新標記。當你得到一個響應時,調用一個方法來存儲新的訪問令牌,然後用訪問令牌調用其他塊。如果請求失敗,告訴單例令牌xyzz135或其他任何無效的消息,並且如果它仍然是活動令牌,則它應該將其刪除。然後重新嘗試使用令牌運行請求塊。如果新的令牌已經被另一個請求獲得,它將立即運行,否則如果已經有請求嘗試,它將會等待,否則它將被刷新令牌重新啓動。

或者,您可以在單例內執行響應處理,並使外部代碼根本不關心認證。無論哪種方式,邏輯仍然是相同的。

+0

嗨,dgatwood。 APIClient和TokenManager已經有一個單例。 TokenManager保存並返回來自Keychain的令牌。使用API​​Client的類不應該負責調用網絡代碼,因爲那樣我就會得到相同的代碼給太多的地方。應答處理應在APIClient內完成。另外,當請求失敗時,我發出一個新的請求來刷新這兩個令牌。當它返回時,我需要重試每個失敗的請求。你介意多解釋你的答案,如果可能的話添加一些示例代碼?謝謝你的時間。 – zarzonis

+0

基本上,從「if usingToken」到函數末尾的所有內容都變成了一個包含兩個參數的塊:刷新令牌和訪問令牌。這被分配給一個變量。如果不是零,則添加代碼以處理將刷新標記添加到請求中。您將該塊作爲參數傳遞給TokenManager類上的方法。它檢查令牌是否爲零,如果不是,則調用該塊並將令牌傳遞給它。 – dgatwood

+0

如果它是零,它使用BOOL來決定要做什麼。如果NO(默認狀態),它將刷新標記傳遞給塊並將BOOL設置爲YES。如果BOOL爲YES,則將該塊添加到數組中。然後在APIClient塊中添加兩個額外的代碼:1.如果傳遞了一個刷新標記,則調用TokenManager類中的一個方法設置訪問標記,然後遍歷該數組,調用每個塊令牌。 2.如果請求401(或其他)失敗,請在TokenManager上調用令牌失效方法。 – dgatwood

相關問題