在发布我的问题之前,我已经搜索过很多但不幸的是我无法找到解决问题的方法。
我开发了一个应用程序,该应用程序连接到需要使用access token
和refresh token
进行身份验证的服务器。
access token
对1 hour
有效,可以多次使用。
refresh token
对1 month
有效,并在access token
到期时使用。 The refresh
令牌只能使用一次。
使用refresh token
时,除了refresh token
之外,我还会获得新的access token
。
这是我的问题:
我写了一个APIClient
class
来处理我的应用所需的所有请求。除class
到期外,此access token
效果很好。当access token
到期时,此时将运行的所有请求将以401 code
(未授权)失败。
我想要的是找到一个解决方案,使用access token
刷新refresh token
,然后重试所有失败状态为code 401
的请求。请记住,刷新令牌的功能只能调用一次,因为refresh
令牌仅对一次使用有效。
我想要做的是找到一种方法来编写我的APIClient
class
,以便它支持刷新令牌并重试所有失败的请求。如果你告诉我如何实现这一目标,我将非常感激。
查看以下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()
}
由于
答案 0 :(得分:0)
假设我正确理解了问题,你所描述的内容适用于单例模式。有一个存储当前令牌的单例,并作为请求门。
传入包含代码的完成块以启动新请求并获取访问令牌的参数。如果令牌可用,它会立即使用令牌调用该块。否则,对于第一个块,它会使用刷新令牌调用它,然后取消刷新令牌。当您收到响应时,调用一个方法来存储新的访问令牌,此时它会使用访问令牌调用其他块。如果请求失败,请告诉单例令牌xyzz135或其他什么是无效的,如果它仍然是活动令牌,它应该将其取消。然后重做尝试使用令牌运行请求块。如果另一个请求已经获得了新令牌,它将立即运行,否则如果已经有请求尝试,它将等待,否则将使用刷新令牌重新启动。
或者,您可以在该单例内执行响应处理,并使外部代码完全不关心身份验证。无论哪种方式,逻辑仍然是相同的。
答案 1 :(得分:-1)
我没有在Swift中编写代码,因此我无法为您提供代码,但我认为最简单的方法是:
1:在completionBlock
APIClient
2:在APIClient
处理程序的if (statusCode == 401)
内部处理所有内部电话,
3:刷新令牌后,您可以检查completionBlock != nil
是否再次请求数据,并在成功时返回回调。
当您在APIClient
内执行所有刷新方法时,在完成刷新并再次重试请求之前,您不会返回completionBlock。
您正在调用APIClient
的其他类将等待返回completionBlock,您可以传递completionBlock,直到您获得正确的令牌并重新拨打电话。
检查这些线程以获取有关如何定义completionBlock Swift的代码,你明白了。