第3章 OAuth 2.0 认证流程理解
Google Play API 采用 OAuth 2.0 作为身份授权协议。对于服务器端自动化场景(如批量管理应用、读取销售报告),推荐使用 服务账号 + JWT 授权模式。本章将解析 JWT 的结构、scope 的作用、令牌获取流程以及 access_ 的有效期管理。
3.1 核心概念
| 概念 | 说明 |
|---|---|
| JWT 授权模式 | 服务账号使用 RSA 私钥签名一个 JWT 断言,向 Google 的 OAuth 令牌端点换取 access_,无需用户交互。 |
| OAuth scope | 以 URI 形式标识 API 权限范围,例如 https://www.googleapis.com/auth/androidpublisher。单一 可包含多个 scope。 |
| access_ | 用于调用 Google Play API 的临时凭证,默认有效期 3600 秒(1小时),过期后必须重新获取。 |
| refresh_ | 服务账号模式不提供 refresh_; 过期后需重新生成 JWT 并再次调用令牌端点。 |
3.2 流程概览
从创建 JWT 到成功调用 API 共 5 个关键步骤:
- 使用服务账号的私钥构建 JWT 头部和载荷(预设
iss、scope、aud、exp、iat)。 - 通过 RSA SHA256 对 JWT 签名,得到 assertion。
- 向
https://oauth2.googleapis.com/发送 POST 请求,携带grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer和assertion。 - 从 JSON 响应中提取
access_和expires_in(秒)。 - 在 HTTP 头中携带
Authorization: Bearer <access_>调用 Google Play API。
关键点:JWT 的
aud 必须为 https://oauth2.googleapis.com/,scope 必须与所需 API 权限一致。签名用私钥,公钥由 Google 验证。
3.3 JWT 结构与签名参数
| 字段 | 示例值 | 说明 |
|---|---|---|
alg | "RS256" | 签名算法,服务账号固定使用 RS256 |
iss | "service-account@project.iam.gserviceaccount.com" | 服务账号的邮箱 |
scope | "https://www.googleapis.com/auth/androidpublisher" | 所需权限,多个 scope 用空格连接 |
aud | "https://oauth2.googleapis.com/" | 令牌端点地址 |
iat | 当前 Unix 时间戳 | 签发时间 |
exp | iat + 3600 | 过期时间,不可超过 1 小时 |
3.4 请求令牌示例(Python)
使用 google-auth 库自动处理 JWT 签名和令牌交换。
from google.oauth2 import service_account
import googleapiclient.discovery
SCOPES = ["https://www.googleapis.com/auth/androidpublisher"]
SERVICE_ACCOUNT_FILE = "path/to/service-account-key.json"
credentials = service_account.Credentials.from_service_account_file(
SERVICE_ACCOUNT_FILE, scopes=SCOPES
)
# credentials 对象自动携带 access_,有效期内复用
# 手动获取 access_(调试用)
if credentials.expired:
credentials.refresh(None) # 服务账号模式 refresh 内部会重新走 JWT 流程
access_ = credentials.
print("Access :", access_)
3.5 手动构造 JWT 并获取 (curl)
适用于语言无现成库的调试场景:
# 1. 生成 JWT assertion(需借助 jwt.io 或 openssl)
HEADER='{"alg":"RS256","typ":"JWT"}'
PAYLOAD='{"iss":"your-sa@project.iam.gserviceaccount.com","scope":"https://www.googleapis.com/auth/androidpublisher","aud":"https://oauth2.googleapis.com/","iat":'$(date +%s)',"exp":'$(($(date +%s) + 3600))'}'
# 2. 拼接并签名(私钥文件 private.key)
JWT=$(echo -n "$HEADER.$PAYLOAD" | openssl dgst -sha256 -sign private.key -binary | base64 | tr -d '=' | tr '/+' '_-')
# 3. 请求令牌
curl -s -X POST https://oauth2.googleapis.com/ \
-d "grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer&assertion=$JWT"
成功响应示例:
{
"access_": "ya29.c.b0AXx...",
"expires_in": 3599,
"_type": "Bearer"
}
3.6 流程示意图
下方 SVG 展示服务账号模式的核心数据流:
3.7 调试与常见错误
| 错误码 | 原因 | 解决方式 |
|---|---|---|
invalid_grant | JWT 签名错误、已过期、iss 未授权等 | 检查私钥匹配、系统时间同步、服务账号授权范围 |
invalid_scope | scope 格式错误或未启用对应 API | 确保 scope URI 正确,在 Google Cloud Console 中启用 Google Play Android Developer API |
400 Bad Request: unauthorized_client | 服务账号未在 Play Console 中添加 | 在 Play Console > 用户和权限 > 邀请服务账号 |
| 返回正常但 API 返回 403 | scope 不足或资源未授权 | 确认服务账号具有相关应用的访问权限 |
3.8 性能与稳定性优化
- 缓存 access_:在内存或本地存储中保存 及获取时间戳,剩余有效期 > 60 秒时复用,避免每请求都去换取。
- JWT 签名缓存:JWT 本身有 1 小时有效期,可在签发后缓存到过期前,不必每次重新签名(但注意不要泄露私钥)。
- 异步获取:在高并发场景使用异步 HTTP 库提前刷新 ,避免同步阻塞。
- 重试机制:遇到
5xx或网络错误时,以指数退避重试令牌请求,间隔 1s、2s、4s。
注意:服务账号模式下 没有 refresh_。access_ 过期后必须重新构造 JWT 并调用令牌端点。务必确保系统时间与 NTP 同步,否则
iat 和 exp 偏差会导致 invalid_grant。
3.9 部署检查清单
- 确认服务账号邮箱已添加到 Google Play Console(用户和权限)。
- 确认私钥文件(JSON)安全存储在服务器,权限设置为 600。
- 验证
audience和scope参数不允许外部配置篡改。 - 实现 缓存层并记录日志,便于定位过期问题。
- 设置报警监控,当连续 3 次获取 失败时触发告警。