考虑到 NSURLSession 的并行性质,在 oauth 流中处理 401 响应的刷新非常复杂。我花了很长时间构建一个对我们非常有效的内部解决方案。以下是对其实施方式的一般概念的非常高级的提取。
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 令牌
- 解析响应
- 将解析后的响应转换为适当的类型(泛型)
希望这有助于阐明一些问题。
更新
我们现在发布了?? Alamofire 4.0 ??,它添加了RequestAdapter 和RequestRetrier 协议,让您可以轻松构建自己的身份验证系统,而无需考虑授权实施细节!如需更多信息,请参阅我们的README,其中包含如何将 OAuth2 系统实施到您的应用中的完整示例。
完全披露: README 中的示例仅用作示例。请不要只是将代码复制粘贴到生产应用程序中。