授权令牌过期时如何刷新AVPlayer头部授权令牌?解决401问题
解决HLS流令牌过期后的401授权问题
你的代码存在两个核心问题导致401错误:一是请求头被覆盖丢失了令牌,二是没有处理令牌过期后的刷新重试逻辑。以下是修复后的实现方案:
修复后的ResourceLoaderDelegate代码
class ResourceLoaderDelegate: NSObject, AVAssetResourceLoaderDelegate { let originalURL: URL var token: String private let tokenQueue = DispatchQueue(label: "com.yourdomain.tokenQueue") private var retryCount = 0 private let maxRetryCount = 1 // 限制重试次数,避免无限循环 init(originalURL: URL, token: String) { self.originalURL = originalURL self.token = token super.init() } // 供外部调用的令牌刷新方法 func updateToken(newToken: String) { tokenQueue.sync { self.token = newToken } } func resourceLoader(_ resourceLoader: AVAssetResourceLoader, shouldWaitForLoadingOfRequestedResource loadingRequest: AVAssetResourceLoadingRequest) -> Bool { guard let url = loadingRequest.request.url else { loadingRequest.finishLoading(with: NSError(domain: "Invalid URL", code: -1, userInfo: [NSLocalizedDescriptionKey: "请求URL无效"])) return false } tokenQueue.sync { [weak self] in guard let self = self else { return } // 先复制原有请求头,再添加Authorization令牌,避免覆盖 var request = URLRequest(url: url) request.allHTTPHeaderFields = loadingRequest.request.allHTTPHeaderFields request.addValue("Bearer \(self.token)", forHTTPHeaderField: "Authorization") let task = URLSession.shared.dataTask(with: request) { [weak self] data, response, error in guard let self = self else { return } if let error = error { loadingRequest.finishLoading(with: error) return } guard let httpResponse = response as? HTTPURLResponse else { let error = NSError(domain: "HTTP Error", code: -1, userInfo: [NSLocalizedDescriptionKey: "无效的HTTP响应"]) loadingRequest.finishLoading(with: error) return } if httpResponse.statusCode == 401 { // 令牌过期,尝试刷新并重试 if self.retryCount < self.maxRetryCount { self.retryCount += 1 // 这里调用你的令牌刷新接口,示例中假设已经通过updateToken更新了新令牌 // 注意:实际刷新令牌需要异步处理,完成后重新发起请求 self.refreshToken { success in if success { // 刷新成功,重新处理当前请求 _ = self.resourceLoader(resourceLoader, shouldWaitForLoadingOfRequestedResource: loadingRequest) } else { let error = NSError(domain: "Auth Error", code: 401, userInfo: [NSLocalizedDescriptionKey: "令牌刷新失败"]) loadingRequest.finishLoading(with: error) } } } else { let error = NSError(domain: "Auth Error", code: 401, userInfo: [NSLocalizedDescriptionKey: "令牌过期且重试次数耗尽"]) loadingRequest.finishLoading(with: error) } return } guard httpResponse.statusCode == 200 else { let error = NSError(domain: "HTTP Error", code: httpResponse.statusCode, userInfo: [NSLocalizedDescriptionKey: "HTTP状态码错误: \(httpResponse.statusCode)"]) loadingRequest.finishLoading(with: error) return } if let data = data { loadingRequest.dataRequest?.respond(with: data) } loadingRequest.finishLoading() self.retryCount = 0 // 重置重试计数 } task.resume() } return true } // 示例:令牌刷新方法,替换为你的实际刷新逻辑 private func refreshToken(completion: @escaping (Bool) -> Void) { // 调用后端刷新令牌接口,获取新令牌后调用updateToken // 示例模拟异步刷新 DispatchQueue.global().asyncAfter(deadline: .now() + 0.5) { [weak self] in guard let self = self else { completion(false) return } // 假设获取到了新令牌 let newToken = "your_new_refreshed_token" self.updateToken(newToken: newToken) completion(true) } } }
关键修复点说明
- 请求头顺序修正:先复制原有请求头,再添加Authorization字段,避免覆盖自定义令牌。
- 令牌可变与线程安全:用
tokenQueue保证令牌更新的线程安全,因为AVAssetResourceLoader的回调可能在后台线程执行。 - 401重试逻辑:检测到401响应时触发令牌刷新,刷新成功后重试当前请求,并限制重试次数防止无限循环。
- 外部令牌更新支持:提供
updateToken方法,允许外部主动刷新令牌(比如提前在令牌过期前更新)。
使用注意事项
- 初始化AVAsset时,需要将资源加载器关联到delegate:
let delegate = ResourceLoaderDelegate(originalURL: hlsURL, token: initialToken) let asset = AVURLAsset(url: hlsURL) asset.resourceLoader.setDelegate(delegate, queue: DispatchQueue(label: "com.yourdomain.resourceLoaderQueue"))
- 实际项目中,
refreshToken方法需要替换为你的后端令牌刷新接口调用,确保获取到有效的新令牌。 - 可以根据业务需求调整
maxRetryCount,或者添加令牌过期时间预判,提前触发刷新避免401。
内容的提问来源于stack exchange,提问作者Narendra
相关产品推荐
相关产品推荐

