访问令牌到期后处理多个未经授权的请求

时间:2017-03-26 08:56:00

标签: ios swift oauth nsurlsession

在发布我的问题之前,我已经搜索过很多但不幸的是我无法找到解决问题的方法。

我开发了一个应用程序,该应用程序连接到需要使用access tokenrefresh token进行身份验证的服务器。

  • access token1 hour有效,可以多次使用。

  • refresh token1 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()
}

由于

2 个答案:

答案 0 :(得分:0)

假设我正确理解了问题,你所描述的内容适用于单例模式。有一个存储当前令牌的单例,并作为请求门。

传入包含代码的完成块以启动新请求并获取访问令牌的参数。如果令牌可用,它会立即使用令牌调用该块。否则,对于第一个块,它会使用刷新令牌调用它,然后取消刷新令牌。当您收到响应时,调用一个方法来存储新的访问令牌,此时它会使用访问令牌调用其他块。如果请求失败,请告诉单例令牌xyzz135或其他什么是无效的,如果它仍然是活动令牌,它应该将其取消。然后重做尝试使用令牌运行请求块。如果另一个请求已经获得了新令牌,它将立即运行,否则如果已经有请求尝试,它将等待,否则将使用刷新令牌重新启动。

或者,您可以在该单例内执行响应处理,并使外部代码完全不关心身份验证。无论哪种方式,逻辑仍然是相同的。

答案 1 :(得分:-1)

我没有在Swift中编写代码,因此我无法为您提供代码,但我认为最简单的方法是:

1:在completionBlock

中定义APIClient

2:在APIClient处理程序的if (statusCode == 401)内部处理所有内部电话,

3:刷新令牌后,您可以检查completionBlock != nil是否再次请求数据,并在成功时返回回调。

当您在APIClient内执行所有刷新方法时,在完成刷新并再次重试请求之前,您不会返回completionBlock。

您正在调用APIClient的其他类将等待返回completionBlock,您可以传递completionBlock,直到您获得正确的令牌并重新拨打电话。

检查这些线程以获取有关如何定义completionBlock Swift的代码,你明白了。

Another Example 1

Another example 2

Another Example 3