You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

SwiftUI中高效实现HTTP请求拦截器的方案咨询

SwiftUI中实现JWT请求拦截与Token管理方案

一、原生URLSession拦截实现(无第三方依赖)

Swift原生的URLProtocol是实现请求拦截的核心方案,无需依赖任何框架就能完成JWT的注入、过期刷新逻辑:

  1. 自定义拦截协议类
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() {}
}
  1. 注册拦截协议
    在App启动时(比如App结构体的init方法)注册:
URLProtocol.registerClass(JWTInterceptorProtocol.self)

二、Alamofire:并未过时,仍是生产环境首选之一

Alamofire目前仍在积极维护(最新版本支持Swift 5.9+、iOS 13+),完全适用于生产环境。它提供的RequestInterceptor组件专门用于请求拦截,比原生实现更简洁:

  1. 实现自定义拦截器
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))
            }
        }
    }
}
  1. 创建带拦截器的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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.01 22:40:24