Alamofire 4 重试器和适配器无法看到更改的 accessToken
Posted
技术标签:
【中文标题】Alamofire 4 重试器和适配器无法看到更改的 accessToken【英文标题】:Alamofire 4 retrier and adaptor unable to see the changed accessToken 【发布时间】:2018-10-17 23:31:18 【问题描述】:我正在使用 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
【问题讨论】:
【参考方案1】:我找到了问题的答案。
在适应方法中,我必须为 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
【讨论】:
【参考方案2】:不幸的是,我已经遇到了AFNetworking. 的同样问题,背后的原因非常愚蠢。经过2-3天的研发,我发现这是由于coockies
和caches
造成的。
让我们了解一下Alamofire的基本知识
Alamofire 基本上是NSURLSession
的封装。它的管理器通过调用 `defaultSessionConfiguration()
NSURLSessionConfiguration
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】:您能否通过为网络活动创建单例类来检查。单例模式保证只实例化一个类的一个实例。像这样:-
open class NetworkHelper
class var sharedManager: NetworkHelper
struct Static
static let instance: NetworkHelper = NetworkHelper()
return Static.instance
.... Put you network call method here
【讨论】:
已经为网络适配器执行此操作,但使用最新令牌创建此类 authTokenHandler以上是关于Alamofire 4 重试器和适配器无法看到更改的 accessToken的主要内容,如果未能解决你的问题,请参考以下文章
AuthenticationInterceptor(Alamofire5.2) 重试过程不能正常工作
Alamofire/RxSwift 如何在状态码 401 上自动刷新令牌和重试请求
Alamofire 5.0.0-rc.3 RequestInterceptor Adapt 方法没有被调用 Alamofire 虽然在响应中有任何错误时会调用重试