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

如何使用Alamofire统一添加Authorization头、处理401响应及实现登录视图

嘿,这个问题问到点子上了!这几乎是每个用Alamofire做iOS网络请求的开发者都会遇到的鉴权场景,我来给你梳理一套清晰的解决方案:

一、全局自动添加Authorization请求头

手动给每个请求加头太麻烦,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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.15 06:46:28