Skip to content

实现刷新令牌功能

刷新令牌是现代身份验证系统的关键组件,允许应用程序维持用户会话,而无需频繁重新验证。在本章中,我们将探讨如何在 Swift 中使用 Alamofire 实现刷新令牌。我们将涵盖以下主题:

  • 理解刷新令牌在身份验证中的作用。
  • 设置令牌刷新机制。
  • 处理令牌过期和重试失败的请求。
  • 实现安全且可重用的解决方案。

理解刷新令牌

刷新令牌是长期有效的令牌,用于在当前访问令牌过期时获取新的访问令牌。访问令牌是短期有效的,用于对 API 请求进行身份验证。当访问令牌过期时,应用程序可以使用刷新令牌来请求新的访问令牌,而无需用户再次登录。

其流程通常如下:

  1. 用户登录,服务器返回访问令牌和刷新令牌。
  2. 访问令牌用于 API 请求,直到其过期。
  3. 当访问令牌过期时,应用程序将刷新令牌发送到服务器以获取新的访问令牌。
  4. 新的访问令牌用于后续请求。

设置令牌刷新机制

要使用 Alamofire 实现刷新令牌,我们需要拦截请求、检查令牌过期情况,并在必要时刷新令牌。设置该机制的方法如下:

  1. 安全存储令牌:使用 Keychain 或其他安全存储机制来存储访问令牌和刷新令牌。
  2. 创建请求拦截器:Alamofire 的 RequestInterceptor 协议允许我们调整和重试请求。我们将使用它来处理令牌刷新逻辑。

以下是 TokenInterceptor 类的示例:

swift
import Alamofire
import Foundation

class TokenInterceptor: RequestInterceptor {
    private let baseURL = "https://api.example.com"
    private let accessTokenKey = "accessToken"
    private let refreshTokenKey = "refreshToken"

    // 通过向 headers 中添加访问令牌来调整请求
    func adapt(_ urlRequest: URLRequest, for session: Session, completion: @escaping (Result<URLRequest, Error>) -> Void) {
        var request = urlRequest
        if let accessToken = KeychainHelper.shared.read(key: accessTokenKey) {
            request.setValue("Bearer \(accessToken)", forHTTPHeaderField: "Authorization")
        }
        completion(.success(request))
    }

    // 如果请求因令牌过期而失败,则重试请求
    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(.doNotRetry)
            return
        }

        // 刷新令牌
        refreshToken { success in
            if success {
                completion(.retry) // 重试原始请求
            } else {
                completion(.doNotRetryWithError(error)) // 请求失败
            }
        }
    }

    // 使用刷新令牌刷新访问令牌
    private func refreshToken(completion: @escaping (Bool) -> Void) {
        guard let refreshToken = KeychainHelper.shared.read(key: refreshTokenKey) else {
            completion(false)
            return
        }

        let parameters: [String: Any] = ["refresh_token": refreshToken]
        AF.request("\(baseURL)/refresh", method: .post, parameters: parameters)
            .validate()
            .responseDecodable(of: TokenResponse.self) { response in
                switch response.result {
                case .success(let tokenResponse):
                    // 保存新的访问令牌
                    KeychainHelper.shared.save(tokenResponse.accessToken, forKey: self.accessTokenKey)
                    completion(true)
                case .failure:
                    completion(false)
                }
            }
    }
}

// Keychain 操作的辅助类
class KeychainHelper {
    static let shared = KeychainHelper()

    func save(_ value: String, forKey key: String) {
        // 将值保存到 Keychain
    }

    func read(key: String) -> String? {
        // 从 Keychain 读取值
        return nil
    }
}

// 令牌响应模型
struct TokenResponse: Decodable {
    let accessToken: String
    let refreshToken: String
}

处理令牌过期和重试请求

TokenInterceptor 中的 retry 方法会检查请求是否因 401 状态码(表示令牌过期)而失败。如果是,则尝试刷新令牌。如果刷新成功,将使用新令牌重试原始请求。否则,请求失败。

实现安全且可重用的解决方案

为了使解决方案可重用,如上面所示,将令牌刷新逻辑封装在一个单独的类中。这使您可以轻松地将其集成到应用程序中任何使用 Alamofire 进行网络请求的部分。

以下是如何在 Alamofire 中使用 TokenInterceptor

swift
let session = Session(interceptor: TokenInterceptor())

session.request("https://api.example.com/data")
    .validate()
    .responseDecodable(of: DataResponse.self) { response in
        switch response.result {
        case .success(let data):
            print("收到数据:\(data)")
        case .failure(let error):
            print("请求失败:\(error.localizedDescription)")
        }
    }

这种方法确保所有请求都能自动处理令牌过期和刷新,为用户提供无缝体验。

通过使用 Alamofire 实现刷新令牌,您可以构建健壮且安全的应用程序,有效地维持用户会话。对于依赖经过身份验证的 API 并需要提供流畅用户体验的应用程序,此机制至关重要。

本站使用 VitePress 制作