# OAuth/Ticket 认证系统 API 文档 ## 概述 本文档描述了 YuMi 应用中 OAuth 认证和 Ticket 会话管理的完整流程。系统采用两阶段认证机制: 1. **OAuth 阶段**:用户登录获取 `access_token` 2. **Ticket 阶段**:使用 `access_token` 获取业务会话 `ticket` ## 认证流程架构 ### 核心组件 - **AccountInfoStorage**: 负责账户信息和 ticket 的本地存储 - **HttpRequestHelper**: 网络请求管理,自动添加认证头 - **Api+Login**: 登录相关 API 接口 - **Api+Main**: Ticket 获取相关 API 接口 ### 认证数据模型 #### AccountModel ```objc @interface AccountModel : PIBaseModel @property (nonatomic, assign) NSString *uid; // 用户唯一标识 @property (nonatomic, copy) NSString *jti; // JWT ID @property (nonatomic, copy) NSString *token_type; // Token 类型 @property (nonatomic, copy) NSString *refresh_token; // 刷新令牌 @property (nonatomic, copy) NSString *netEaseToken; // 网易云信令牌 @property (nonatomic, copy) NSString *access_token; // OAuth 访问令牌 @property (nonatomic, assign) NSNumber *expires_in; // 过期时间 @end ``` ## API 接口详情 ### 1. OAuth 登录接口 #### 1.1 手机验证码登录 ```objc + (void)loginWithCode:(HttpRequestHelperCompletion)completion phone:(NSString *)phone code:(NSString *)code client_secret:(NSString *)client_secret version:(NSString *)version client_id:(NSString *)client_id grant_type:(NSString *)grant_type phoneAreaCode:(NSString *)phoneAreaCode; ``` **接口路径**: `POST /oauth/token` **请求参数**: | 参数名 | 类型 | 必填 | 描述 | |--------|------|------|------| | phone | String | 是 | 手机号(DES加密) | | code | String | 是 | 验证码 | | client_secret | String | 是 | 客户端密钥,固定值:"uyzjdhds" | | version | String | 是 | 版本号,固定值:"1" | | client_id | String | 是 | 客户端ID,固定值:"erban-client" | | grant_type | String | 是 | 授权类型,验证码登录为:"sms_code" | | phoneAreaCode | String | 是 | 手机区号 | **返回数据**: AccountModel 对象 #### 1.2 手机密码登录 ```objc + (void)loginWithPassword:(HttpRequestHelperCompletion)completion phone:(NSString *)phone password:(NSString *)password client_secret:(NSString *)client_secret version:(NSString *)version client_id:(NSString *)client_id grant_type:(NSString *)grant_type; ``` **接口路径**: `POST /oauth/token` **请求参数**: | 参数名 | 类型 | 必填 | 描述 | |--------|------|------|------| | phone | String | 是 | 手机号(DES加密) | | password | String | 是 | 密码(DES加密) | | client_secret | String | 是 | 客户端密钥 | | version | String | 是 | 版本号 | | client_id | String | 是 | 客户端ID | | grant_type | String | 是 | 授权类型,密码登录为:"password" | #### 1.3 第三方登录 ```objc + (void)loginWithThirdPart:(HttpRequestHelperCompletion)completion openid:(NSString *)openid unionid:(NSString *)unionid access_token:(NSString *)access_token type:(NSString *)type; ``` **接口路径**: `POST /acc/third/login` **请求参数**: | 参数名 | 类型 | 必填 | 描述 | |--------|------|------|------| | openid | String | 是 | 第三方平台用户唯一标识 | | unionid | String | 是 | 第三方平台联合ID | | access_token | String | 是 | 第三方平台访问令牌 | | type | String | 是 | 第三方平台类型(1:Apple, 2:Facebook, 3:Google等) | ### 2. Ticket 获取接口 #### 2.1 获取 Ticket ```objc + (void)requestTicket:(HttpRequestHelperCompletion)completion access_token:(NSString *)accessToken issue_type:(NSString *)issueType; ``` **接口路径**: `POST /oauth/ticket` **请求参数**: | 参数名 | 类型 | 必填 | 描述 | |--------|------|------|------| | access_token | String | 是 | OAuth 登录获取的访问令牌 | | issue_type | String | 是 | 签发类型,固定值:"multi" | **返回数据**: ```json { "code": 200, "data": { "tickets": [ { "ticket": "eyJhbGciOiJIUzI1NiJ9..." } ] } } ``` ### 3. HTTP 请求头配置 所有业务 API 请求都会自动添加以下请求头: ```objc // 在 HttpRequestHelper 中自动配置 - (void)setupHeader { AFHTTPSessionManager *client = [HttpRequestHelper requestManager]; // 用户ID头 if ([[AccountInfoStorage instance] getUid].length > 0) { [client.requestSerializer setValue:[[AccountInfoStorage instance] getUid] forHTTPHeaderField:@"pub_uid"]; } // Ticket 认证头 if ([[AccountInfoStorage instance] getTicket].length > 0) { [client.requestSerializer setValue:[[AccountInfoStorage instance] getTicket] forHTTPHeaderField:@"pub_ticket"]; } // 其他公共头 [client.requestSerializer setValue:[NSBundle uploadLanguageText] forHTTPHeaderField:@"Accept-Language"]; [client.requestSerializer setValue:PI_App_Version forHTTPHeaderField:@"App-Version"]; } ``` ## 使用流程 ### 完整登录流程示例 ```objc // 1. 用户登录获取 access_token [Api loginWithCode:^(BaseModel * _Nullable data, NSInteger code, NSString * _Nullable msg) { if (code == 200) { // 保存账户信息 AccountModel *accountModel = [AccountModel modelWithDictionary:data.data]; [[AccountInfoStorage instance] saveAccountInfo:accountModel]; // 2. 使用 access_token 获取 ticket [Api requestTicket:^(BaseModel * _Nullable data, NSInteger code, NSString * _Nullable msg) { if (code == 200) { NSArray *tickets = [data.data valueForKey:@"tickets"]; NSString *ticket = [tickets[0] valueForKey:@"ticket"]; // 保存 ticket [[AccountInfoStorage instance] saveTicket:ticket]; // 3. 登录成功,可以进行业务操作 [self navigateToMainPage]; } } access_token:accountModel.access_token issue_type:@"multi"]; } } phone:encryptedPhone code:verificationCode client_secret:@"uyzjdhds" version:@"1" client_id:@"erban-client" grant_type:@"sms_code" phoneAreaCode:areaCode]; ``` ### 自动登录流程 ```objc - (void)autoLogin { // 检查本地是否有账户信息 AccountModel *accountModel = [[AccountInfoStorage instance] getCurrentAccountInfo]; if (accountModel == nil || accountModel.access_token == nil) { [self tokenInvalid]; // 跳转到登录页 return; } // 检查是否有有效的 ticket if ([[AccountInfoStorage instance] getTicket].length > 0) { [[self getView] autoLoginSuccess]; return; } // 使用 access_token 重新获取 ticket [Api requestTicket:^(BaseModel * _Nonnull data) { NSArray *tickets = [data.data valueForKey:@"tickets"]; NSString *ticket = [tickets[0] valueForKey:@"ticket"]; [[AccountInfoStorage instance] saveTicket:ticket]; [[self getView] autoLoginSuccess]; } fail:^(NSInteger code, NSString * _Nullable msg) { [self logout]; // ticket 获取失败,重新登录 } access_token:accountModel.access_token issue_type:@"multi"]; } ``` ## 错误处理 ### 401 未授权错误 当接收到 401 状态码时,系统会自动处理: ```objc // 在 HttpRequestHelper 中 if (response && response.statusCode == 401) { failure(response.statusCode, YMLocalizedString(@"HttpRequestHelper7")); // 通常需要重新登录 } ``` ### Ticket 过期处理 - Ticket 过期时服务器返回 401 错误 - 客户端应该使用保存的 `access_token` 重新获取 ticket - 如果 `access_token` 也过期,则需要用户重新登录 ## 安全注意事项 1. **数据加密**: 敏感信息(手机号、密码)使用 DES 加密传输 2. **本地存储**: - `access_token` 存储在文件系统中 - `ticket` 存储在内存中,应用重启需重新获取 3. **请求头**: 所有业务请求自动携带 `pub_uid` 和 `pub_ticket` 头 4. **错误处理**: 建立完善的 401 错误重试机制 ## 相关文件 - `YuMi/Structure/MVP/Model/AccountInfoStorage.h/m` - 账户信息存储管理 - `YuMi/Modules/YMLogin/Api/Api+Login.h/m` - 登录相关接口 - `YuMi/Modules/YMTabbar/Api/Api+Main.h/m` - Ticket 获取接口 - `YuMi/Network/HttpRequestHelper.h/m` - 网络请求管理 - `YuMi/Structure/MVP/Model/AccountModel.h/m` - 账户数据模型