fix(ios): APIClient 401 错误码传递到 AuthManager,完成错误码分支闭环
- notifyTokenExpired 接受 errorCode 参数,通过 Notification userInfo 传递 - 401 响应提取 errorCode 后传给 notifyTokenExpired - AuthManager 观察者从 userInfo 读取 errorCode,调用 applyErrorCode - handleUnauthorized 根据 errorCode 切换精确状态(disabled/deleted/expired) - restoreSession 冷启动也检查 errorCode,禁用/删除用户不尝试 refresh Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
parent
539b9a7d2b
commit
49bebad402
@ -30,9 +30,10 @@ final class AuthManager: ObservableObject {
|
|||||||
init() {
|
init() {
|
||||||
tokenExpiredObserver = NotificationCenter.default.addObserver(
|
tokenExpiredObserver = NotificationCenter.default.addObserver(
|
||||||
forName: .tokenExpired, object: nil, queue: .main
|
forName: .tokenExpired, object: nil, queue: .main
|
||||||
) { [weak self] _ in
|
) { [weak self] notification in
|
||||||
|
let errorCode = notification.userInfo?["errorCode"] as? String
|
||||||
Task { @MainActor [weak self] in
|
Task { @MainActor [weak self] in
|
||||||
self?.handleUnauthorized()
|
self?.handleUnauthorized(errorCode: errorCode)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -59,6 +60,14 @@ final class AuthManager: ObservableObject {
|
|||||||
let _: UserProfileResponse = try await APIClient.shared.request("/users/me")
|
let _: UserProfileResponse = try await APIClient.shared.request("/users/me")
|
||||||
session = .authenticated
|
session = .authenticated
|
||||||
} catch {
|
} catch {
|
||||||
|
// 如果 /users/me 返回了特定错误码(禁用/删除),直接切状态,不尝试 refresh
|
||||||
|
if let apiError = error as? APIError, let code = apiError.errorCode {
|
||||||
|
await APIClient.shared.setToken(nil)
|
||||||
|
KeychainHelper.clear()
|
||||||
|
applyErrorCode(code)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
session = .refreshing
|
session = .refreshing
|
||||||
if let refreshed = await tryRefresh() {
|
if let refreshed = await tryRefresh() {
|
||||||
await APIClient.shared.setToken(refreshed.accessToken)
|
await APIClient.shared.setToken(refreshed.accessToken)
|
||||||
@ -120,13 +129,17 @@ final class AuthManager: ObservableObject {
|
|||||||
|
|
||||||
// MARK: - Private
|
// MARK: - Private
|
||||||
|
|
||||||
private func handleUnauthorized() {
|
private func handleUnauthorized(errorCode: String? = nil) {
|
||||||
Task {
|
Task {
|
||||||
await APIClient.shared.setToken(nil)
|
await APIClient.shared.setToken(nil)
|
||||||
KeychainHelper.clear()
|
KeychainHelper.clear()
|
||||||
|
if let code = errorCode {
|
||||||
|
applyErrorCode(code)
|
||||||
|
} else {
|
||||||
session = .unauthenticated
|
session = .unauthenticated
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private func tryRefresh() async -> AuthResponse? {
|
private func tryRefresh() async -> AuthResponse? {
|
||||||
guard let refreshToken = KeychainHelper.getRefreshToken() else { return nil }
|
guard let refreshToken = KeychainHelper.getRefreshToken() else { return nil }
|
||||||
|
|||||||
@ -69,10 +69,12 @@ actor APIClient {
|
|||||||
self.token = newToken
|
self.token = newToken
|
||||||
return try await performRequest(path, method: method, body: body, queryItems: queryItems, isRetry: true)
|
return try await performRequest(path, method: method, body: body, queryItems: queryItems, isRetry: true)
|
||||||
}
|
}
|
||||||
await notifyTokenExpired()
|
let info = extractErrorInfo(data)
|
||||||
|
await notifyTokenExpired(errorCode: info.errorCode)
|
||||||
throw decodeServerError(data, fallback: APIError.unauthorized)
|
throw decodeServerError(data, fallback: APIError.unauthorized)
|
||||||
case 401:
|
case 401:
|
||||||
throw decodeServerError(data, fallback: APIError.unauthorized)
|
let info = extractErrorInfo(data)
|
||||||
|
throw APIError.serverError(info.message ?? "未授权", code: info.errorCode)
|
||||||
case 400..<500:
|
case 400..<500:
|
||||||
let msg = String(data: data, encoding: .utf8) ?? ""
|
let msg = String(data: data, encoding: .utf8) ?? ""
|
||||||
throw APIError.serverError(msg)
|
throw APIError.serverError(msg)
|
||||||
@ -99,9 +101,13 @@ actor APIClient {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private func notifyTokenExpired() async {
|
private func notifyTokenExpired(errorCode: String? = nil) async {
|
||||||
|
var userInfo: [AnyHashable: Any]? = nil
|
||||||
|
if let code = errorCode {
|
||||||
|
userInfo = ["errorCode": code]
|
||||||
|
}
|
||||||
await MainActor.run {
|
await MainActor.run {
|
||||||
NotificationCenter.default.post(name: .tokenExpired, object: nil)
|
NotificationCenter.default.post(name: .tokenExpired, object: nil, userInfo: userInfo)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user