import SwiftUI import AuthenticationServices // 苹果登录功能 import Alamofire // 网络请求 import CryptoKit // 加密功能 /// 主登录视图 - 处理苹果登录 struct LoginView: View { // MARK: - 属性 @Environment(\.dismiss) private var dismiss // 用于关闭视图 @State private var isLoading = false // 加载状态 @State private var showError = false // 是否显示错误 @State private var errorMessage = "" // 错误信息 @State private var currentNonce: String? // 用于防止重放攻击的随机数 // MARK: - 视图主体 var body: some View { ZStack { // 背景 Color(.systemBackground) .edgesIgnoringSafeArea(.all) // 主要内容 VStack(spacing: 24) { Spacer() appHeaderView() // 应用标题 signInButton() // 登录按钮 Spacer() termsAndPrivacyView() // 服务条款和隐私政策 } .padding() .alert(isPresented: $showError) { Alert( title: Text("Error"), message: Text(errorMessage), dismissButton: .default(Text("OK")) ) } // 加载指示器 if isLoading { loadingView() } } .navigationBarHidden(true) } // MARK: - 视图组件 /// 应用标题视图 private func appHeaderView() -> some View { VStack(spacing: 16) { Image(systemName: "person.circle.fill") .resizable() .aspectRatio(contentMode: .fit) .frame(width: 80, height: 80) .foregroundColor(.blue) Text("Welcome to Wake") .font(.largeTitle) .fontWeight(.bold) Text("Sign in to continue") .font(.subheadline) .foregroundColor(.secondary) } .padding(.bottom, 40) } /// 苹果登录按钮 private func signInButton() -> some View { SignInWithAppleButton( onRequest: { request in // 生成随机数用于安全验证 let nonce = String.randomURLSafeString(length: 32) self.currentNonce = nonce // 配置登录请求 request.requestedScopes = [.fullName, .email] // 请求用户全名和邮箱 request.nonce = self.sha256(nonce) // 设置nonce }, onCompletion: handleAppleSignIn // 登录完成处理 ) .signInWithAppleButtonStyle(.black) // 按钮样式 .frame(height: 50) .padding(.horizontal, 40) .cornerRadius(10) } /// 服务条款和隐私政策链接 private func termsAndPrivacyView() -> some View { VStack(spacing: 8) { Text("By continuing, you agree to our") .font(.caption) .foregroundColor(.secondary) HStack(spacing: 16) { Button("Terms of Service") { openURL("https://yourwebsite.com/terms") } .font(.caption) .foregroundColor(.blue) Text("•") .foregroundColor(.secondary) Button("Privacy Policy") { openURL("https://yourwebsite.com/privacy") } .font(.caption) .foregroundColor(.blue) } } .padding(.bottom, 24) } /// 加载视图 private func loadingView() -> some View { return ZStack { Color.black.opacity(0.4) .edgesIgnoringSafeArea(.all) ProgressView() .scaleEffect(1.5) .progressViewStyle(CircularProgressViewStyle(tint: .white)) } } // MARK: - 苹果登录处理 /// 处理苹果登录结果 private func handleAppleSignIn(result: Result) { print("🔵 [Apple Sign In] 开始处理登录结果...") switch result { case .success(let authResults): print("✅ [Apple Sign In] 登录授权成功") processAppleIDCredential(authResults.credential) case .failure(let error): print("❌ [Apple Sign In] 登录失败: \(error.localizedDescription)") handleSignInError(error) } } /// 处理苹果ID凭证 private func processAppleIDCredential(_ credential: ASAuthorizationCredential) { print("🔵 [Apple ID] 开始处理凭证...") guard let appleIDCredential = credential as? ASAuthorizationAppleIDCredential else { print("❌ [Apple ID] 凭证类型不匹配") showError(message: "无法处理Apple ID凭证") return } // 获取用户数据 let userId = appleIDCredential.user let email = appleIDCredential.email ?? "" let fullName = [ appleIDCredential.fullName?.givenName, appleIDCredential.fullName?.familyName ] .compactMap { $0 } .joined(separator: " ") print("ℹ️ [Apple ID] 用户数据 - ID: \(userId), 邮箱: \(email.isEmpty ? "未提供" : email), 姓名: \(fullName.isEmpty ? "未提供" : fullName)") // 获取身份令牌 guard let identityTokenData = appleIDCredential.identityToken, let identityToken = String(data: identityTokenData, encoding: .utf8) else { print("❌ [Apple ID] 无法获取身份令牌") showError(message: "无法获取身份令牌") return } // 获取授权码(可选) var authCode: String? = nil if let authCodeData = appleIDCredential.authorizationCode { authCode = String(data: authCodeData, encoding: .utf8) print("ℹ️ [Apple ID] 获取到授权码") } else { print("ℹ️ [Apple ID] 未获取到授权码(可选)") } print("🔵 [Apple ID] 准备调用后端认证...") authenticateWithBackend( userId: userId, email: email, name: fullName, identityToken: identityToken, authCode: authCode ) } // MARK: - 网络操作 /// 与后端服务器进行认证 private func authenticateWithBackend( userId: String, email: String, name: String, identityToken: String, authCode: String? ) { isLoading = true print("🔵 [Backend] 开始后端认证...") let url = "https://your-api-endpoint.com/api/auth/apple" var parameters: [String: Any] = [ "appleUserId": userId, "email": email, "name": name, "identityToken": identityToken ] // 添加授权码(如果存在) if let authCode = authCode { parameters["authorizationCode"] = authCode } print("📤 [Backend] 请求参数: \(parameters)") AF.request(url, method: .post, parameters: parameters, encoding: JSONEncoding.default) .validate() .responseJSON { response in self.isLoading = false switch response.result { case .success(let value): print("✅ [Backend] 认证成功: \(value)") self.handleSuccessfulAuthentication() case .failure(let error): print("❌ [Backend] 认证失败: \(error.localizedDescription)") if let data = response.data, let json = String(data: data, encoding: .utf8) { print("❌ [Backend] 错误详情: \(json)") } self.handleAuthenticationError(error) } } } // MARK: - 辅助方法 /// 处理认证成功 private func handleSuccessfulAuthentication() { print("✅ [Auth] 登录成功,准备关闭登录页面...") DispatchQueue.main.async { self.dismiss() } } /// 处理登录错误 private func handleSignInError(_ error: Error) { let errorMessage = (error as NSError).localizedDescription print("❌ [Auth] 登录错误: \(errorMessage)") showError(message: "登录失败: \(error.localizedDescription)") } /// 处理认证错误 private func handleAuthenticationError(_ error: AFError) { let errorMessage = error.localizedDescription print("❌ [Auth] 认证错误: \(errorMessage)") showError(message: "登录失败: \(errorMessage)") } /// 显示错误信息 private func showError(message: String) { DispatchQueue.main.async { self.errorMessage = message self.showError = true } } /// 在Safari中打开URL private func openURL(_ string: String) { guard let url = URL(string: string) else { return } UIApplication.shared.open(url) } /// SHA256哈希函数 private func sha256(_ input: String) -> String { let inputData = Data(input.utf8) let hashedData = SHA256.hash(data: inputData) let hashString = hashedData.compactMap { String(format: "%02x", $0) }.joined() return hashString } } // MARK: - 字符串扩展:生成随机字符串 extension String { /// 生成指定长度的随机URL安全字符串 /// - Parameter length: 字符串长度 /// - Returns: 随机字符串 static func randomURLSafeString(length: Int) -> String { let characters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._~" var randomString = "" for _ in 0..