Alamofire 4 retrier和适配器无法看到已更改的accessToken

时间:2018-05-08 05:26:36

标签: ios swift oauth alamofire access-token

我正在使用alamofire的反向和适应协议获取新的访问令牌。我能够获取一个新的令牌,但有时当另一个线程调用相同的方法时,它不起作用,即使生成新的访问令牌,请求也会失败。

我刚刚更改了示例,现在我正在使用同步请求来获取访问令牌,因为如果我知道令牌无效,我不想发送额外的请求。

奇怪的问题是,当我在失败的请求上打印响应时,我看到该请求仍然在标头中有旧令牌。我在这里缺少什么?

func isTokenValid() -> Bool {
    return Date() < self.expiryTime
}

func adapt(_ urlRequest: URLRequest) throws -> URLRequest {
    var urlRequest = urlRequest
    urlRequest = processRequest(urlRequest: urlRequest)
    return urlRequest
}

func processRequest(urlRequest: URLRequest) -> URLRequest {
    DDLogInfo("******access token : \(self.accessToken)***************")
    DDLogInfo("***** expiry Time: \(self.expiryTime)***************")
    var urlRequest = urlRequest
    lock.lock(); defer {   DDLogInfo( "Thread UnLocked ⚡️: \(Thread.current)\r" + ": \(OperationQueue.current?.underlyingQueue?.label ?? "None")\r")
        lock.unlock()
    }
    DDLogInfo( "Thread Locked ⚡️: \(Thread.current)\r" + ": \(OperationQueue.current?.underlyingQueue?.label ?? "None")\r")

    if !isTokenValid() {
        let _ = self.refreshAccessToken()
    }

    urlRequest = self.appendToken(urlRequest: urlRequest)
    DDLogInfo("here \(urlRequest)")
    return urlRequest
}

func appendToken(urlRequest: URLRequest) -> URLRequest {
    if let urlString = urlRequest.url?.absoluteString, urlString.hasPrefix(baseURLString) {
        var urlRequest = urlRequest
        DDLogInfo("token appended : \(self.accessToken)")
        urlRequest.setValue(self.accessToken, forHTTPHeaderField: Constants.KeychainKeys.accessToken)
    }
    return urlRequest
}

// MARK: - RequestRetrier

 func handleFailedRequest(_ completion: @escaping RequestRetryCompletion) {
    requestsToRetry.append(completion)
    if !isRefreshing {
        lock.lock()
        print( "Thread Locked⚡️: \(Thread.current)\r" + ": \(OperationQueue.current?.underlyingQueue?.label ?? "None")\r")

        let succeeded = self.refreshAccessToken()
        self.requestsToRetry.forEach {
            print("token fetched \(succeeded): \(self.accessToken)")
            $0(succeeded, 0.0)
        }
        self.requestsToRetry.removeAll()
        DDLogInfo( "Thread UnLocked⚡️: \(Thread.current)\r" + ": \(OperationQueue.current?.underlyingQueue?.label ?? "None")\r")
        lock.unlock()
    }
}


func should(_ manager: SessionManager, retry request: Request, with error: Error, completion: @escaping RequestRetryCompletion) {

    if let response = request.task?.response as? HTTPURLResponse, response.statusCode == 401, request.retryCount < 3 {
        handleFailedRequest(completion)
    } else {
        completion(false, 0.0)
    }
}

func updateTokens (accessToken: String, refreshToken: String, accessTokenExpiresIn: Double) {
    self.accessToken = accessToken
    self.refreshToken = refreshToken
    let expiryDate = Date(timeIntervalSinceNow: accessTokenExpiresIn - Constants.KeychainKeys.expirationBuffer)
    AppSettings.sharedInstance.tokenExpiryTime = expiryDate
    self.expiryTime = expiryDate
    do {try keychainWrapper.save(values: ["accessToken": self.accessToken, "refreshToken": self.refreshToken])} catch {
        DDLogError("unable to save accessToken")
        }
}

// MARK: - Private - Refresh Tokens

  fileprivate func refreshAccessToken() -> Bool {
    DDLogInfo("^^^^^^^^")
    Thread.callStackSymbols.forEach {  DDLogInfo($0) }

    var success = false
    guard !isRefreshing else { return success }
    let refreshRequest = URLRequestConfigurations.configRefreshProviderAgent(refreshToken: self.refreshToken)
    let result = URLSession.shared.synchronousDataTask(with: refreshRequest)

    self.isRefreshing = false
    do {
        if let data = result.0, let json = try JSONSerialization.jsonObject(with: data, options: []) as? [String : Any] {
            if let accessToken = json["accessToken"] as? String, let refreshToken = json["refreshToken"] as? String, let time = json["accessTokenExpiresIn"] as? Double {
                updateTokens(accessToken: accessToken, refreshToken: refreshToken, accessTokenExpiresIn: time)
                success = true
            } else {
                DDLogError("unable to find tokens/expiryInterval from refresh request")
            }
        } else {
            DDLogError("unable to receive data from refresh request")
        }
    } catch {
        DDLogError("unable to parse json response from refersh token request")
    }
    return success
}

3 个答案:

答案 0 :(得分:3)

您可以通过为网络活动创建单例类来检查。单例模式保证只实例化一个类的一个实例。像这样: -

open class NetworkHelper {

class var sharedManager: NetworkHelper {
    struct Static{
        static let instance: NetworkHelper = NetworkHelper()
    }
    return Static.instance
}

.... Put you network call method here


}

答案 1 :(得分:0)

我已经遇到过与AFNetworking.相同的问题,不幸的是,背后的原因非常愚蠢。在R&amp; D 2-3天后,我发现它是由于coockiescaches而发生的。

让我们了解Alamofire

的基本知识

Alamofire基本上是NSURLSession的包装器。它的管理器通过调用`defaultSessionConfiguration()

来使用默认的NSURLSessionConfigurationdefaultSessionConfiguration()

NSURLSessionConfiguration引用说:

  

默认会话配置使用基于磁盘的持久缓存   (除非将结果下载到文件中)并存储   用户钥匙串中的凭据。它还存储cookie(通过   默认情况下)与NSURLConnection和。相同的共享cookie存储区   NSURLDownload类。

只需使用以下代码禁用API的缓存。

禁用URLCache

let manager: Manager = {
    let configuration = NSURLSessionConfiguration.defaultSessionConfiguration()
    configuration.URLCache = nil
    return Manager(configuration: configuration)
}()

或者您也可以配置请求缓存政策

let manager: Manager = {
    let configuration = NSURLSessionConfiguration.defaultSessionConfiguration()
    configuration.requestCachePolicy = .ReloadIgnoringLocalCacheData
    return Manager(configuration: configuration)
}()

解决方案:只需禁用API的chaching即可。它对我有用。在你的情况下,它可能适合你。

答案 2 :(得分:0)

我找到了问题的答案。

在Adapt方法中,我必须对urlRequest使用不同的变量,因为使用相同的变量名称时,它没有修改请求。如您在下面看到的,我将变量更改为“ mutableRequest”

func appendToken(urlRequest: URLRequest) -> URLRequest {
    var mutableRequest = urlRequest
    if let urlString = urlRequest.url?.absoluteString, urlString.hasPrefix(baseURLString) {
        DDLogInfo("token appended : \(self.accessToken)")
        mutableRequest.setValue(self.accessToken, forHTTPHeaderField: Constants.KeychainKeys.accessToken)
    }
    return mutableRequest
}