代码之家  ›  专栏  ›  技术社区  ›  megha

Alamofire 4检索器和适配器无法查看更改的accessToken

  •  4
  • megha  · 技术社区  · 7 年前

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

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

    奇怪的是,当我在失败的请求上打印响应时,我看到该请求的头中仍然有旧的令牌。我错过了什么?

    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 回复  |  直到 7 年前
        1
  •  3
  •   Manish Mahajan    7 年前

    可以通过为网络活动创建singleton类进行检查吗。singleton模式保证只实例化一个类的一个实例。就像这样:-

    open class NetworkHelper {
    
    class var sharedManager: NetworkHelper {
        struct Static{
            static let instance: NetworkHelper = NetworkHelper()
        }
        return Static.instance
    }
    
    .... Put you network call method here
    
    
    }
    
        2
  •  0
  •   Hitesh Surani    7 年前

    我已经遇到了同样的问题 AFNetworking. 不幸的是,背后的原因很愚蠢。2-3天后R&D、 我发现这是由于 coockies caches

    让我们了解关于 Alamofire

    Alamofire基本上是一个包装器 NSURLSession 。其管理器使用默认值 NSURLSessionConfiguration 通过调用'defaultSessionConfiguration()

    NSURLSessionConfiguration 参考 defaultSessionConfiguration() 表示:

    默认会话配置使用基于磁盘的持久缓存 (结果下载到文件时除外)并存储 用户密钥链中的凭据。它还存储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。这对我有用。在你的情况下,它可能会为你。

        3
  •  0
  •   megha    7 年前

    我找到了问题的答案。

    在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
    }