跳转到内容

REST API认证

REST API认证

本节介绍 TimeTagger REST API 的认证机制,包括生成 API 令牌、保护请求以及在 Node-RED 流程中处理认证。学习完本节后,您将能够:

  • 理解 TimeTagger 的 API 令牌认证模型
  • 生成和管理 API 令牌
  • 在 HTTP 请求中实现基于令牌的认证
  • 排查常见的认证问题

开始本节前,请确保您已完成:

  • TimeTagger 已安装并运行(参见 05-05)
  • Node-RED 可访问并已配置
  • 基本了解 HTTP 头部
  • TimeTagger 管理员凭据

TimeTagger 使用简单的基于令牌的认证系统:

客户端请求 → 包含令牌 → 服务器验证 → 授予/拒绝访问

与 OAuth 或 JWT 不同,TimeTagger 令牌:

  • 不过期(除非手动撤销)
  • 是单个字符串(无刷新令牌流程)
  • 授予完整 API 访问权限(无作用域/权限级别)
  • 绑定到用户账户(管理员或普通用户)

对 IoT 系统的优势

特性好处
无过期令牌可硬编码到 ESP32/Node-RED 中
无刷新流程简化了微控制器上的实现
单令牌一个令牌用于所有 API 操作
可撤销通过重新生成令牌来禁用访问

所有 API 请求使用以下基础 URL:

http://<服务器地址>:8820/api/v2/

示例端点:

端点方法用途
/api/v2/infoGET服务器信息
/api/v2/recordsGET获取记录
/api/v2/recordsPOST创建新记录
/api/v2/recordsPUT更新现有记录
/api/v2/recordsDELETE删除(隐藏)记录

Web UI 方法

1. 登录 TimeTagger Web UI(http://localhost:8820)
2. 点击您的用户名(右上角)
3. 从下拉菜单中选择"账户"
4. 找到"API 令牌"部分
5. 复制令牌字符串
┌─────────────────────────────────────┐
│ 账户设置 │
├─────────────────────────────────────┤
│ 用户名:admin │
│ │
│ API 令牌 │
│ ┌─────────────────────────────┐ │
│ │ eyJhbGciOiJIUzI1NiIs... │ │
│ └─────────────────────────────┘ │
│ [复制] [重置令牌] │
│ │
│ API 令牌允许第三方应用访问 │
│ 服务器。令牌不过期。 │
│ 重置令牌将撤销所有应用 │
│ 的访问权限。 │
└─────────────────────────────────────┘

API 方法(如果已认证):

Terminal window
# 使用现有令牌的 curl 命令
curl -s http://localhost:8820/api/v2/info \
-H "Authorization: Bearer 您的_TOKEN"

在 Node-RED 中使用前测试令牌:

Terminal window
# 使用 curl 测试
curl -s -X GET "http://localhost:8820/api/v2/info" \
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiIs..."
# 预期响应(成功):
# {"ok":true,"version":"24.12","user":"admin",...}
# 预期响应(失败):
# {"ok":false,"error":"Unauthorized"}

Node-RED 函数节点用于认证

// auth-config.js — 放置在 Function 节点中
// 配置 TimeTagger API 请求的认证信息
const authToken = "eyJhbGciOiJIUzI1NiIs..."; // 替换为您的实际令牌
msg.headers = {
"Authorization": "Bearer " + authToken,
"Content-Type": "application/json"
};
return msg;

可重用的认证模块

创建子流程或使用全局上下文以获得更清晰的代码:

// 在函数节点中,全局设置一次认证令牌
if (!global.get("timetaggerAuth")) {
global.set("timetaggerAuth", {
token: "eyJhbGciOiJIUzI1NiIs...",
baseUrl: "http://timetagger:80/api/v2"
});
}

为达到环境级别的安全性,将令牌存储在 Docker 环境变量中:

Docker Compose 方法

services:
nodered:
image: nodered/node-red:latest
environment:
- TIMETAGGER_TOKEN=eyJhbGciOiJIUzI1NiIs...
- TIMETAGGER_URL=http://timetagger:80/api/v2

读取环境变量的 Node-RED 流程

// 在启动时的函数节点中
const token = process.env.TIMETAGGER_TOKEN;
const url = process.env.TIMETAGGER_URL;
if (token && url) {
global.set("timetaggerAuth", {
token: token,
baseUrl: url
});
node.warn("已从环境变量配置 TimeTagger 认证");
} else {
node.error("环境中未配置 TimeTagger 认证");
}

为认证失败实现适当的错误处理:

handle-auth-error.js
// 在 HTTP 请求节点之后使用,处理 401 响应
if (msg.statusCode === 401) {
node.warn("TimeTagger 认证失败 - 请检查 API 令牌");
msg.authError = true;
msg.errorDetail = "令牌无效或已过期。请在 TimeTagger 账户设置中重新生成。";
} else if (msg.statusCode === 403) {
node.warn("TimeTagger 访问被拒绝");
msg.authError = true;
msg.errorDetail = "令牌没有足够的权限。";
} else if (msg.statusCode >= 400) {
node.warn("TimeTagger HTTP 错误:" + msg.statusCode);
msg.authError = true;
msg.errorDetail = "HTTP " + msg.statusCode;
}
return msg;

验证认证是否正常工作:

Terminal window
# 1. 使用正确令牌测试
curl -s -X GET "http://localhost:8820/api/v2/info" \
-H "Authorization: Bearer 有效令牌"
# 预期:包含用户信息的 JSON
# {"ok":true,"version":"24.12","user":"admin"}
# 2. 使用无效令牌测试
curl -s -X GET "http://localhost:8820/api/v2/info" \
-H "Authorization: Bearer 无效令牌"
# 预期:401 未授权
# {"ok":false,"error":"Unauthorized"}
# 3. 不带令牌测试
curl -s -X GET "http://localhost:8820/api/v2/info"
# 预期:401 未授权
# {"ok":false,"error":"Unauthorized"}

Node-RED 测试流程

[注入] → [函数:设置认证头] → [HTTP 请求] → [调试]
[http://localhost:8820/api/v2/info]
  1. 创建一个 Inject 节点(时间戳触发)
  2. 创建一个 Function 节点,包含认证头代码
  3. 创建一个 HTTP Request 节点(GET 到 /api/v2/info
  4. 连接到 Debug 节点
  5. 部署并触发

预期的调试输出

{
"ok": true,
"version": "24.12",
"user": "admin",
"now": 1715942400,
"from_epoch": 0,
"to_epoch": 0
}

症状:尽管使用了正确的令牌,HTTP 401 仍然存在

可能原因

  • 令牌包含多余的空白字符
  • 令牌已重置/重新生成
  • 使用了来自不同实例的令牌

解决方案

Terminal window
# 1. 在 TimeTagger 账户设置中重新生成令牌
# 2. 精确复制令牌(无前导/尾随空格)
# 3. 使用新令牌更新所有应用
# 逐字符测试令牌
echo -n "您的_TOKEN" | wc -c

症状:从 Node-RED 调用 API 时浏览器控制台显示 CORS 错误

原因:TimeTagger 的 CORS 策略阻止跨域请求

解决方案: 使用 Node-RED 服务器端 HTTP 请求节点(而非基于浏览器)或配置代理:

// 始终使用 Node-RED 的 http-request 节点(服务器端)
// 不要使用浏览器的 fetch() 或 XMLHTTPRequest

症状:在日志或错误消息中可见令牌

解决方案

// 在日志输出中对令牌进行脱敏处理
function sanitize(str) {
if (str && str.length > 10) {
return str.substring(0, 6) + "..." + str.substring(str.length - 4);
}
return str;
}
node.warn("使用令牌:" + sanitize(authToken));
// 输出:"使用令牌:eyJhb...Is..."
  • 建议:将 API 令牌存储在环境变量中,而非流程代码中
  • 建议:在生产部署中使用 Docker secrets
  • 建议:定期重新生成令牌(如每 6 个月)
  • 避免:在 GitHub 仓库或共享流程中硬编码令牌
  • 避免:在不同环境(开发/测试/生产)之间共享令牌
  1. TimeTagger 使用简单的令牌认证——没有 OAuth,没有过期
  2. API 令牌从 TimeTagger Web UI 的账户设置中获取
  3. 令牌通过 Authorization 头部传递Bearer <令牌>
  4. Node-RED 可以使用环境变量实现安全的令牌存储
  5. 401 响应表示令牌问题——如果需要请重新生成