如何使用Alamofire统一添加Authorization头、处理401响应及实现登录视图
嘿,这个问题问到点子上了!这几乎是每个用Alamofire做iOS网络请求的开发者都会遇到的鉴权场景,我来给你梳理一套清晰的解决方案:
手动给每个请求加头太麻烦,Alamofire提供了RequestAdapter机制,能帮你给所有请求自动注入鉴权头:
首先自定义一个适配器类:
class AuthAdapter: RequestAdapter { func adapt(_ urlRequest: URLRequest, for session: Session, completion: @escaping (Result<URLRequest, Error>) -> Void) { var adaptedRequest = urlRequest // 从本地存储(比如Keychain/UserDefaults)取出你的token if let token = KeychainHelper.shared.getToken() { adaptedRequest.setValue("Bearer \(token)", forHTTPHeaderField: "Authorization") } completion(.success(adaptedRequest)) } }
然后初始化Alamofire的Session时传入这个适配器:
let authSession = Session(adapter: AuthAdapter())
之后所有用这个authSession发起的请求,都会自动带上Authorization头,不用重复写逻辑!
二、拦截401响应并触发登录逻辑
当服务器返回401(未授权)时,我们需要清除本地无效token、跳转到登录页,这时候用RequestInterceptor最合适——它能同时处理请求适配和响应重试/拦截:
class AuthInterceptor: RequestInterceptor { // 这里复用上面的适配器逻辑,自动加鉴权头 func adapt(_ urlRequest: URLRequest, for session: Session, completion: @escaping (Result<URLRequest, Error>) -> Void) { var adaptedRequest = urlRequest if let token = KeychainHelper.shared.getToken() { adaptedRequest.setValue("Bearer \(token)", forHTTPHeaderField: "Authorization") } completion(.success(adaptedRequest)) } // 拦截响应,处理401逻辑 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 { // 不是401错误,直接返回失败 completion(.doNotRetryWithError(error)) return } // 1. 清除本地无效的token KeychainHelper.shared.clearToken() // 2. 回到主线程跳转登录页(UI操作必须在主线程) DispatchQueue.main.async { let loginVC = LoginViewController() // 这里根据你的APP架构跳转,比如替换根控制器或者push if let window = UIApplication.shared.connectedScenes.first?.windows.first { window.rootViewController = UINavigationController(rootViewController: loginVC) window.makeKeyAndVisible() } } // 不需要重试,因为已经跳转到登录流程了 completion(.doNotRetry) } }
然后把这个Interceptor传入Session:
let authSession = Session(interceptor: AuthInterceptor())
这样一套下来,加头和401拦截就都搞定了!
三、必须封装HttpService类吗?听我的,一定要!
虽然Alamofire本身已经很易用,但封装一个全局的HttpService类有N多好处:
- 统一管理Session实例,避免到处创建Session导致的配置混乱
- 统一处理请求参数编码、响应解析(比如自动把JSON转成Model)
- 隐藏Alamofire的底层细节,后续如果要替换网络框架(比如换成原生URLSession),只需要修改这个类,不用动所有请求代码
- 可以统一添加全局的loading提示、错误弹窗等逻辑
给你一个简单的封装示例:
class HttpService { // 单例模式,全局唯一实例 static let shared = HttpService() private let session: Session private init() { // 初始化时传入我们的鉴权拦截器 let interceptor = AuthInterceptor() self.session = Session(interceptor: interceptor) } // GET请求模板,自动解析Decodable类型的Model func get<T: Decodable>(url: String, parameters: [String: Any]?, completion: @escaping (Result<T, Error>) -> Void) { session.request(url, method: .get, parameters: parameters) .validate() // 自动验证HTTP状态码(200-299为成功) .responseDecodable(of: T.self) { response in switch response.result { case .success(let model): completion(.success(model)) case .failure(let error): completion(.failure(error)) } } } // POST请求模板,用JSON编码参数 func post<T: Decodable>(url: String, parameters: [String: Any]?, completion: @escaping (Result<T, Error>) -> Void) { session.request(url, method: .post, parameters: parameters, encoding: JSONEncoding.default) .validate() .responseDecodable(of: T.self) { response in switch response.result { case .success(let model): completion(.success(model)) case .failure(let error): completion(.failure(error)) } } } }
使用的时候超简单:
// 假设UserModel是遵循Decodable的模型类 HttpService.shared.get(url: "https://api.yourapp.com/user/profile", parameters: nil) { (result: Result<UserModel, Error>) in switch result { case .success(let user): print("用户信息:\(user)") case .failure(let error): print("请求失败:\(error.localizedDescription)") } }
四、有没有Alamofire内置的实现方式?
遗憾的是,Alamofire本身没有直接提供“一键加鉴权头+处理401”的内置功能——因为每个APP的鉴权逻辑(比如token存储方式、登录跳转逻辑、是否需要刷新token重试)都不一样,官方没法做通用的内置实现。
不过Alamofire提供的RequestAdapter和RequestInterceptor已经是非常灵活的扩展点,完全能满足绝大多数场景的需求,官方也是推荐用这种方式来实现自定义鉴权逻辑的。
内容的提问来源于stack exchange,提问作者Alex
相关产品推荐
相关产品推荐

