Ответ 1
Обработка обновления для 401 ответов в потоке oauth довольно сложна, учитывая параллельность NSURLSessions. Я потратил довольно много времени на создание внутреннего решения, которое очень хорошо работало для нас. Ниже приведено очень высокое изложение общей идеи о том, как оно было реализовано.
import Foundation
import Alamofire
public class AuthorizationManager: Manager {
public typealias NetworkSuccessHandler = (AnyObject?) -> Void
public typealias NetworkFailureHandler = (NSHTTPURLResponse?, AnyObject?, NSError) -> Void
private typealias CachedTask = (NSHTTPURLResponse?, AnyObject?, NSError?) -> Void
private var cachedTasks = Array<CachedTask>()
private var isRefreshing = false
public func startRequest(
method method: Alamofire.Method,
URLString: URLStringConvertible,
parameters: [String: AnyObject]?,
encoding: ParameterEncoding,
success: NetworkSuccessHandler?,
failure: NetworkFailureHandler?) -> Request?
{
let cachedTask: CachedTask = { [weak self] URLResponse, data, error in
guard let strongSelf = self else { return }
if let error = error {
failure?(URLResponse, data, error)
} else {
strongSelf.startRequest(
method: method,
URLString: URLString,
parameters: parameters,
encoding: encoding,
success: success,
failure: failure
)
}
}
if self.isRefreshing {
self.cachedTasks.append(cachedTask)
return nil
}
// Append your auth tokens here to your parameters
let request = self.request(method, URLString, parameters: parameters, encoding: encoding)
request.response { [weak self] request, response, data, error in
guard let strongSelf = self else { return }
if let response = response where response.statusCode == 401 {
strongSelf.cachedTasks.append(cachedTask)
strongSelf.refreshTokens()
return
}
if let error = error {
failure?(response, data, error)
} else {
success?(data)
}
}
return request
}
func refreshTokens() {
self.isRefreshing = true
// Make the refresh call and run the following in the success closure to restart the cached tasks
let cachedTaskCopy = self.cachedTasks
self.cachedTasks.removeAll()
cachedTaskCopy.map { $0(nil, nil, nil) }
self.isRefreshing = false
}
}
Самое главное здесь помнить, что вы не хотите запускать вызов обновления для каждых 401, которые возвращаются. В то же время может участвовать множество запросов. Поэтому вы хотите действовать в первом 401 и ставить в очередь все дополнительные запросы до тех пор, пока не будет выполнено 401. Решение, описанное выше, делает именно это. Любая задача данных, запущенная с помощью метода startRequest
, автоматически обновится, если она достигнет 401.
Некоторые другие важные моменты, которые следует учитывать здесь, которые не учитываются в этом очень упрощенном примере:
- Резьбонарезную безопасность
- Гарантированные неудачные или неудачные вызовы закрытия
- Сохранение и извлечение маркеров oauth
- Анализ ответа
- Передача анализируемого ответа на соответствующий тип (generics)
Надеюсь, это поможет пролить свет.
Update
Теперь мы выпустили 🔥🔥 Alamofire 4.0 🔥🔥, который добавляет протоколы RequestAdapter
и RequestRetrier
, позволяющие вам легко создавать собственную систему аутентификации независимо от деталей реализации авторизации! Для получения дополнительной информации см. наш README, в котором приведен полный пример того, как вы можете реализовать систему OAuth2 в своем приложении.
Полное раскрытие: Пример в README предназначен только для использования в качестве примера. Пожалуйста, пожалуйста, не просто переходите и скопируйте код в производственное приложение.