SwiftUI中高效实现HTTP请求拦截器的方案咨询
SwiftUI中实现JWT请求拦截与Token管理方案
一、原生URLSession拦截实现(无第三方依赖)
Swift原生的URLProtocol是实现请求拦截的核心方案,无需依赖任何框架就能完成JWT的注入、过期刷新逻辑:
- 自定义拦截协议类
class JWTInterceptorProtocol: URLProtocol { override class func canInit(with request: URLRequest) -> Bool { // 过滤需要拦截的请求,比如排除非API域名的请求 guard let url = request.url, url.host == "your-api-domain.com" else { return false } return true } override class func canonicalRequest(for request: URLRequest) -> URLRequest { return request } override func startLoading() { var mutableRequest = request // 注入AccessToken到请求头 if let token = KeychainManager.shared.accessToken { mutableRequest.setValue("Bearer \(token)", forHTTPHeaderField: "Authorization") } let task = URLSession.shared.dataTask(with: mutableRequest) { [weak self] data, response, error in guard let self = self else { return } if let httpResponse = response as? HTTPURLResponse, httpResponse.statusCode == 401 { // 处理Token过期:刷新Token并重试请求 self.refreshTokenAndRetry(mutableRequest) return } // 正常转发响应 self.client?.urlProtocol(self, didReceive: response!, cacheStoragePolicy: .notAllowed) if let data = data { self.client?.urlProtocol(self, didLoad: data) } self.client?.urlProtocolDidFinishLoading(self) } task.resume() } private func refreshTokenAndRetry(_ originalRequest: URLRequest) { // 调用刷新Token接口 APIClient.refreshToken { [weak self] result in guard let self = self else { return } switch result { case .success(let newToken): KeychainManager.shared.accessToken = newToken // 重试原请求 var retryRequest = originalRequest retryRequest.setValue("Bearer \(newToken)", forHTTPHeaderField: "Authorization") let task = URLSession.shared.dataTask(with: retryRequest) { data, response, error in self.client?.urlProtocol(self, didReceive: response!, cacheStoragePolicy: .notAllowed) if let data = data { self.client?.urlProtocol(self, didLoad: data) } self.client?.urlProtocolDidFinishLoading(self) } task.resume() case .failure: // 刷新失败,跳转登录页 DispatchQueue.main.async { // 这里用SwiftUI的状态管理(比如@EnvironmentObject)触发登录跳转 AppState.shared.isLoggedIn = false } self.client?.urlProtocol(self, didFailWithError: NSError(domain: "JWTError", code: 401, userInfo: [NSLocalizedDescriptionKey: "Token刷新失败"])) } } } override func stopLoading() {} }
- 注册拦截协议
在App启动时(比如App结构体的init方法)注册:
URLProtocol.registerClass(JWTInterceptorProtocol.self)
二、Alamofire:并未过时,仍是生产环境首选之一
Alamofire目前仍在积极维护(最新版本支持Swift 5.9+、iOS 13+),完全适用于生产环境。它提供的RequestInterceptor组件专门用于请求拦截,比原生实现更简洁:
- 实现自定义拦截器
import Alamofire class JWTInterceptor: RequestInterceptor { // 注入Token到请求头 func adapt(_ urlRequest: URLRequest, for session: Session, completion: @escaping (Result<URLRequest, Error>) -> Void) { var adaptedRequest = urlRequest if let token = KeychainManager.shared.accessToken { adaptedRequest.setValue("Bearer \(token)", forHTTPHeaderField: "Authorization") } completion(.success(adaptedRequest)) } // 处理请求失败(Token过期) func retry(_ request: Request, for session: Session, dueTo error: Error, completion: @escaping (RetryResult) -> Void) { guard let response = request.task?.response as? HTTPURLResponse, response.statusCode == 401 else { completion(.doNotRetryWithError(error)) return } // 刷新Token APIClient.refreshToken { result in switch result { case .success(let newToken): KeychainManager.shared.accessToken = newToken completion(.retry) case .failure: // 跳转登录页 DispatchQueue.main.async { AppState.shared.isLoggedIn = false } completion(.doNotRetryWithError(error)) } } } }
- 创建带拦截器的Alamofire Session
let session = Session(interceptor: JWTInterceptor()) // 后续所有请求都使用这个session发送 session.request("https://your-api-domain.com/user/data").responseJSON { response in // 处理响应 }
三、其他可选方案
- Moya:基于Alamofire的上层封装,采用面向协议的API管理方式,可结合Alamofire的Interceptor实现Token逻辑,适合API接口较多、需要统一管理的项目。
- 轻量自定义封装:基于
URLSessionDataTask封装自己的请求类,在请求发送前统一处理Token注入,失败后处理刷新逻辑,适合需求简单、不想引入第三方框架的场景。
总结
- 原生
URLProtocol适合轻量场景,无依赖但实现代码稍多; - Alamofire是成熟且活跃的框架,拦截器功能完善,生产环境完全可用;
- 根据项目规模和需求选择对应方案,核心逻辑都是请求前注入Token、401时刷新并重试。
内容的提问来源于stack exchange,提问作者user19402781
相关产品推荐
相关产品推荐

