我正在使用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
}
答案 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天后,我发现它是由于coockies
和caches
而发生的。
让我们了解Alamofire
的基本知识 Alamofire基本上是NSURLSession
的包装器。它的管理器通过调用`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即可。它对我有用。在你的情况下,它可能适合你。
答案 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
}