6.0 KiB
6.0 KiB
腾讯文档 API 鉴权修复指南
关键发现
根据腾讯文档官方 API 文档,发现了之前鉴权方式的重大错误:
正确的鉴权方式
腾讯文档 V3 API 需要三个请求头进行鉴权:
Access-Token: {访问令牌}
Client-Id: {应用ID}
Open-Id: {开放平台用户ID}
而不是:
Authorization: Bearer {访问令牌} ❌ 错误!
推荐方案:使用应用级账号 Token
什么是应用级账号 Token?
- 不需要用户授权流程
- 直接使用
client_id和client_secret获取 - 响应包含所有需要的信息
API 接口
请求:
GET https://docs.qq.com/oauth/v2/app-account-token?client_id=CLIENT_ID&client_secret=CLIENT_SECRET
响应:
{
"access_token": "ACCESSTOKENEXAMPLE",
"token_type": "Bearer",
"refresh_token": "REFRESHTOKENEXAMPLE",
"expires_in": 259200,
"scope": "scope.file.editable,scope.folder.creatable",
"user_id": "bcb50c8a4b724d86bbcf6fc64c5e2b22"
}
字段映射
| 响应字段 | 对应请求头 | 说明 |
|---|---|---|
access_token |
Access-Token |
访问令牌 |
请求参数 client_id |
Client-Id |
应用ID |
user_id |
Open-Id |
开放平台用户ID(关键!) |
完整的 API 调用流程
步骤1:获取应用级账号 Token
JSONObject tokenInfo = TencentDocApiUtil.getAppAccountToken(appId, appSecret);
String accessToken = tokenInfo.getString("access_token");
String openId = tokenInfo.getString("user_id"); // 这就是 Open-Id!
String clientId = appId; // Client-Id 就是 appId
步骤2:调用业务 API
// 设置请求头
conn.setRequestProperty("Access-Token", accessToken);
conn.setRequestProperty("Client-Id", clientId);
conn.setRequestProperty("Open-Id", openId);
conn.setRequestProperty("Content-Type", "application/json");
步骤3:发送请求
GET https://docs.qq.com/openapi/spreadsheet/v3/files/{fileId}/{sheetId}/A1:Z100
Access-Token: ACCESSTOKENEXAMPLE
Client-Id: YOUR_CLIENT_ID
Open-Id: bcb50c8a4b724d86bbcf6fc64c5e2b22
Content-Type: application/json
代码修改方案
方案 A:简单封装(推荐)
在 Service 层创建一个包装类来管理鉴权信息:
public class TencentDocAuth {
private String accessToken;
private String clientId;
private String openId;
private long expiresAt;
// 获取或刷新 Token
public static TencentDocAuth getAuth(String appId, String appSecret) {
JSONObject tokenInfo = TencentDocApiUtil.getAppAccountToken(appId, appSecret);
TencentDocAuth auth = new TencentDocAuth();
auth.accessToken = tokenInfo.getString("access_token");
auth.openId = tokenInfo.getString("user_id");
auth.clientId = appId;
auth.expiresAt = System.currentTimeMillis() + tokenInfo.getIntValue("expires_in") * 1000;
return auth;
}
// Getters...
}
方案 B:修改现有方法签名
修改 callApi 方法,添加必要的参数:
public static JSONObject callApi(String accessToken, String clientId, String openId,
String apiUrl, String method, String body) {
conn.setRequestProperty("Access-Token", accessToken);
conn.setRequestProperty("Client-Id", clientId);
conn.setRequestProperty("Open-Id", openId);
// ...
}
然后更新所有调用此方法的地方。
实现步骤
1. 添加获取应用级账号 Token 的方法 ✅
已在 TencentDocApiUtil.java 中添加:
public static JSONObject getAppAccountToken(String appId, String appSecret)
2. 修改 callApi 方法 ✅
已更新为:
public static JSONObject callApi(String accessToken, String clientId, String openId,
String apiUrl, String method, String body)
3. 更新所有调用点(待完成)
需要更新以下方法:
readSheetData()writeSheetData()appendSheetData()getFileInfo()getSheetList()
以及所有调用这些方法的 Service 类。
测试验证
1. 获取应用级账号 Token
JSONObject tokenInfo = TencentDocApiUtil.getAppAccountToken(
"YOUR_CLIENT_ID",
"YOUR_CLIENT_SECRET"
);
System.out.println("Access Token: " + tokenInfo.getString("access_token"));
System.out.println("Open-Id: " + tokenInfo.getString("user_id"));
2. 调用表格 API
String accessToken = tokenInfo.getString("access_token");
String clientId = "YOUR_CLIENT_ID";
String openId = tokenInfo.getString("user_id");
JSONObject result = TencentDocApiUtil.readSheetData(
accessToken, clientId, openId,
"YOUR_FILE_ID", "SHEET_ID", "A1:Z10",
"https://docs.qq.com/openapi/spreadsheet/v3"
);
注意事项
1. Token 有效期
应用级账号 Token 默认有效期为 3 天(259200秒),需要定期刷新。
2. 存储安全
client_secret必须保密- Token 应该缓存并在过期前刷新
- 不要在日志中打印完整的 Token
3. 权限范围
应用级账号的权限取决于申请时的 scope:
scope.sheet- 读取表格scope.sheet.editable- 编辑表格scope.file.editable- 编辑文件scope.folder.creatable- 创建文件夹
错误排查
401 Unauthorized
- 检查 Access-Token 是否正确
- 检查 Token 是否过期
- 检查是否包含所有三个请求头
403 Forbidden
- 检查应用是否有相应的权限 (scope)
- 检查 Open-Id 是否正确
404 Not Found
- 检查 URL 路径是否正确
- 确认基础 URL 为
https://docs.qq.com/openapi/spreadsheet/v3 - 确认路径格式为
/files/{fileId}/...
参考文档
更新时间:2025-11-05
状态:部分完成,需要更新所有调用点