OAuth 2.0 和 JWT 授权在 Swift 上:面向初学者的分步指南

面向初学者的在线Python训练器

轻松学习Python,无需理论负担。在浏览器中直接解决带自动检查的实践任务,无需安装任何东西。

开始课程

Swift 上的 OAuth 2.0 和 JWT 简介

\n\n

现代移动和桌面应用程序很少能绕过身份验证。两个关键协议——OAuth 2.0 和 JWT(JSON Web Token)——已成为客户端与服务器之间安全传输数据的事实标准。在本文中,我们将详细讲解如何在 Swift 上为 iOS 和 macOS 平台实现 OAuth 2.0 和 JWT 授权。您将学习如何配置令牌获取流程、将其安全存储在 Keychain 中,并在令牌过期时自动刷新。

\n\n

本文面向已熟悉 Swift 和 SwiftUI 基础的初学者开发者。我们将使用 URLSession 进行网络请求,并使用 Keychain 安全存储数据。代码基于 Swift 5.9 编写,兼容 iOS 15+ / macOS 12+。

\n\n

什么是 OAuth 2.0 和 JWT?

\n\n

在编写代码之前,让我们先了解一些理论知识。

\n\n

OAuth 2.0

\n

OAuth 2.0 是一种授权协议,允许应用程序在不传递用户登录名和密码的情况下,获取对用户资源的有限访问权限。它使用访问令牌代替。主要参与者包括:

\n
    \n
  • 资源所有者 — 用户。
  • \n
  • 客户端 — 您的应用程序。
  • \n
  • 授权服务器 — 颁发令牌的服务器。
  • \n
  • 资源服务器 — 提供数据的 API。
  • \n
\n\n

移动应用程序最常用的流程是 授权码流程与 PKCE(Proof Key for Code Exchange)。即使没有难以在应用程序中存储的客户端密钥,它也是安全的。

\n\n

JWT(JSON Web Token)

\n

JWT 是一种紧凑且自包含的令牌格式,包含 JSON 数据。它由三部分组成,用点分隔:

\n
    \n
  • 头部 — 令牌类型和签名算法。
  • \n
  • 载荷 — 有效数据(声明),例如用户 ID、角色、过期时间。
  • \n
  • 签名 — 用于验证完整性的签名。
  • \n
\n\n

JWT 通常用作 OAuth 2.0 中的 访问令牌。它允许服务器在不查询数据库的情况下验证请求的真实性。

\n\n

Swift 上的实践实现

\n\n

我们将创建一个简单的 AuthService 类来管理身份验证。示例中将使用服务器 https://api.example.com。

\n\n

步骤 1:通过 OAuth 2.0 获取令牌

\n\n

首先,我们实现一个通过授权码获取令牌的方法。假设我们已经通过 Web 视图(例如 ASWebAuthenticationSession)获得了 authorizationCode。

\n\n
import Foundation\n\nfinal class AuthService {\n    static let shared = AuthService()\n    private let baseURL = "https://api.example.com"\n    private let clientID = "your_client_id"\n    private let redirectURI = "yourapp://callback"\n    \n    // MARK: - 获取令牌\n    func exchangeCodeForTokens(code: String, codeVerifier: String) async throws -> TokenResponse {\n        let url = URL(string: "\\(baseURL)/oauth/token")!\n        var request = URLRequest(url: url)\n        request.httpMethod = "POST"\n        request.setValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type")\n        \n        let body = [\n            "grant_type": "authorization_code",\n            "code": code,\n            "redirect_uri": redirectURI,\n            "client_id": clientID,\n            "code_verifier": codeVerifier\n        ]\n        .map { "\\($0.key)=\\($0.value.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) ?? "")" }\n        .joined(separator: "&")\n        .data(using: .utf8)\n        \n        request.httpBody = body\n        \n        let (data, response) = try await URLSession.shared.data(for: request)\n        \n        guard let httpResponse = response as? HTTPURLResponse,                

博客

书籍推荐