1
This commit is contained in:
240
doc/腾讯文档API鉴权修复指南.md
Normal file
240
doc/腾讯文档API鉴权修复指南.md
Normal file
@@ -0,0 +1,240 @@
|
||||
# 腾讯文档 API 鉴权修复指南
|
||||
|
||||
## 关键发现
|
||||
|
||||
根据腾讯文档官方 API 文档,发现了之前鉴权方式的重大错误:
|
||||
|
||||
### 正确的鉴权方式
|
||||
|
||||
腾讯文档 V3 API 需要**三个请求头**进行鉴权:
|
||||
|
||||
```http
|
||||
Access-Token: {访问令牌}
|
||||
Client-Id: {应用ID}
|
||||
Open-Id: {开放平台用户ID}
|
||||
```
|
||||
|
||||
**而不是**:
|
||||
```http
|
||||
Authorization: Bearer {访问令牌} ❌ 错误!
|
||||
```
|
||||
|
||||
## 推荐方案:使用应用级账号 Token
|
||||
|
||||
### 什么是应用级账号 Token?
|
||||
|
||||
- 不需要用户授权流程
|
||||
- 直接使用 `client_id` 和 `client_secret` 获取
|
||||
- 响应包含所有需要的信息
|
||||
|
||||
### API 接口
|
||||
|
||||
**请求:**
|
||||
```http
|
||||
GET https://docs.qq.com/oauth/v2/app-account-token?client_id=CLIENT_ID&client_secret=CLIENT_SECRET
|
||||
```
|
||||
|
||||
**响应:**
|
||||
```json
|
||||
{
|
||||
"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
|
||||
|
||||
```java
|
||||
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
|
||||
|
||||
```java
|
||||
// 设置请求头
|
||||
conn.setRequestProperty("Access-Token", accessToken);
|
||||
conn.setRequestProperty("Client-Id", clientId);
|
||||
conn.setRequestProperty("Open-Id", openId);
|
||||
conn.setRequestProperty("Content-Type", "application/json");
|
||||
```
|
||||
|
||||
### 步骤3:发送请求
|
||||
|
||||
```http
|
||||
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 层创建一个包装类来管理鉴权信息:
|
||||
|
||||
```java
|
||||
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` 方法,添加必要的参数:
|
||||
|
||||
```java
|
||||
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` 中添加:
|
||||
```java
|
||||
public static JSONObject getAppAccountToken(String appId, String appSecret)
|
||||
```
|
||||
|
||||
### 2. 修改 callApi 方法 ✅
|
||||
|
||||
已更新为:
|
||||
```java
|
||||
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
|
||||
|
||||
```java
|
||||
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
|
||||
|
||||
```java
|
||||
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}/...`
|
||||
|
||||
## 参考文档
|
||||
|
||||
- [批量更新接口](https://docs.qq.com/open/document/app/openapi/v3/sheet/batchUpdate.html)
|
||||
- [获取应用级账号 Token](https://docs.qq.com/open/document/app/oauth2/app-account-token.html)
|
||||
- [请求头部说明](https://docs.qq.com/open/document/app/openapi/v3/)
|
||||
|
||||
---
|
||||
|
||||
**更新时间**:2025-11-05
|
||||
**状态**:部分完成,需要更新所有调用点
|
||||
|
||||
Reference in New Issue
Block a user