要点概览:服务器与苹果相关服务交互主要涉及三类凭证:Sign in with Apple 的 client_secret(JWT)、APNs 的 Auth Key(p8)和 App Store/订阅相关的Server-to-Server key。
前提准备:1) Apple Developer 账号并加入团队;2) 在 Apple Developer 中创建 App ID/Services ID;3) 在“Keys”里生成对应的 p8 文件并记录 Key ID、Team ID、Client ID(Services ID 或 Bundle ID)。
本文假定你能访问开发者中心并有后端服务器(可用 Node/Python/Java 等实现)。
步骤:1) 登录 Apple Developer -> Identifiers -> 创建或编辑 Services ID(作为 client_id);2) 为该 Services ID 启用 "Sign In with Apple" 并配置回调 URL(在 App/网站中使用);3) 在 Keys 页面创建一个新的 Key,勾选 "Sign In with Apple",下载 .p8 文件并记录 Key ID 与 Team ID。
注意:client_id 必须与你在 Apple Developer 中的 Services ID 一致;回调 URL 需与前端/移动端配置匹配。
JWT 构成:header{alg:ES256, kid:KeyID},payload{iss:TeamID, iat:当前时间, exp:当前时间+最多6个月, aud:"https://appleid.apple.com", sub:client_id}。使用 p8 私钥(你下载的 Key)以 ES256 签名。
实际生成(Node 示例):1) 安装 jsonwebtoken;2) jwt.sign(payload, privateKey, {algorithm:'ES256', keyid: KEY_ID}); 3) 得到 client_secret。
校验要点:exp 最长 6 个月,服务器应定期刷新并缓存 client_secret,避免每次请求都生成影响性能。
交换流程:前端拿到 authorization code 后,服务器向 https://appleid.apple.com/auth/token 发送 POST:grant_type=authorization_code、code=前端code、client_id、client_secret、redirect_uri。
响应与存储:返回 access_token、refresh_token、id_token(JWT)。服务器应:1) 验证 id_token(通过 https://appleid.apple.com/auth/keys 获取公钥并验证签名);2) 保存 refresh_token(加密),用于后续刷新。
刷新流程:当 access_token 过期,用 grant_type=refresh_token 调用相同接口,携带 refresh_token、client_id、client_secret 获取新 token。
准备:在 Apple Developer -> Keys 创建 APNs Auth Key,下载 p8 文件并记录 Key ID 与 Team ID。你的 App 在 iOS 端会向 APNs 注册并返回 device token,需上传给后端并绑定用户。
发送步骤:1) 后端每次请求 APNs 时生成 JWT(header 包含 kid;payload 包含 iss=TeamID 和 iat),该 JWT 作为 Authorization: Bearer
错误处理:设备卸载或 token 变更时 APNs 会返回特定状态码,需及时删除或更新后端的 device token。
两种方式:1) 客户端把 receipt 发给你服务器,服务器再向 App Store 验证收据(沙盒或生产端点);2) 使用 App Store Server API(需要在 App Store Connect 配置密钥),通过 JWT 验证订阅状态。
步骤要点:1) 服务器向 https://buy.itunes.apple.com/verifyReceipt 或 sandbox 发送 receipt-data;2) 解析返回的 latest_receipt_info,依据 expires_date、status 判断订阅有效性;3) 对订阅状态变化使用通知(Server Notifications)并结合本地记录做补偿逻辑。
存储规则:1) 所有长期凭证(refresh_token、device token、用户绑定信息)必须加密存储(数据库加密列或使用 KMS);2) client_secret(短期 JWT)不必持久化,但私钥(p8)必须安全保管(仅服务器可访问,使用权限分离)。
生命周期管理:1) 为 access_token 设 TTL 并在过期前刷新;2) refresh_token 若长期无需则提供撤销/登出接口;3) 对异常登录/滥用实施风控(IP/设备白名单、速率限制)。
密钥轮换:定期更换服务器私钥并在 Apple Developer 中添加新 Key,旧 key 到期后撤销;实现平滑切换以避免中断服务。
监控要素:1) 统计 token 失败率、APNs 返回 410/400 频率、Sign in with Apple 的 token 交换失败量;2) 设置告警(错误率/延时阈值)。
排查步骤:1) 若 token 交换失败,检查 client_secret 是否正确(iss/kid/sub/aud/exp);2) 若 APNs 发送失败,检查 JWT 的 kid/iss 是否与 p8 匹配并确认 apns-topic;3) 校验时间同步(NTP),JWT 的 iat/exp 依赖服务器时间。
答:在服务器端用下载的 p8 私钥生成 ES256 签名的 JWT,payload 包含 iss(TeamID)、sub(client_id)、aud(https://appleid.apple.com)、iat/exp(exp 最长 6 个月)。实现方式:用后台定时任务生成新的 client_secret 并缓存(例如 Redis),缓存过期时间设为实际 exp 减少缓冲(如 exp-1 天)。推荐使用成熟库(Node jsonwebtoken、PyJWT)生成并存放私钥于受限文件系统或密钥管理服务(KMS)。
答:410/Unregistered 意味着该 device token 不再有效,应在后端将对应 token 标记为无效并删除或标记待回收,通知客户端在下次启动重新注册并上报新 token。并发情况下可用标记+队列延迟删除以避免误删。
答:策略包括:1) 将 refresh_token 加密存储,关联设备/用户并记录元信息(IP、设备ID);2) 限制同一 refresh_token 的并发使用频率与刷新次数;3) 登出时主动撤销 refresh_token 并清除服务端记录;4) 若检测异常(异地登录、短时间大量刷新)则强制登出并要求重新授权。