认证指南
本文档深入介绍 Lovrabet OpenAPI 的认证机制、Token 管理和最佳实践。
认证概述
Lovrabet OpenAPI 采用基于 HMAC-SHA256 签名的认证机制,根据运行环境和使用场景,支持三种认证模式:
| 认证模式 | 适用场景 | 密钥要求 |
|---|---|---|
| 服务端模式 | Node.js 服务端、SSR | accessKey |
| 浏览器 Token 模式 | 浏览器公开数据访问 | 预生成 token + timestamp |
| 浏览器 Cookie 模式 | 已登录用户私有数据 | 无需密钥(使用 Cookie) |
SDK 自动处理
官方 SDK 会自动根据提供的参数选择合适的认证模式,无需手动指定。
认证凭证
您需要的凭证
| 凭证名称 | 说明 | 获取方式 | 示例 |
|---|---|---|---|
| App Code | 应用唯一标识 | 联系商务经理 | app-c2dd52a2 |
| Access Key | 访问密钥 | 联系商务经理 | ak-xxxxxxxxxxxxx |
| Dataset Code | 数据集标识 | 联系商务经理 | 0fefba76fe29...ff |
安全提示
Access Key 是敏感信息,请妥善保管,不要泄露或硬编码到代码中。
模式 1: 服务端认证(accessKey)
适用于 Node.js 服务端、Next.js SSR、API 路由等场景。
基础用法
import { createClient } from "@lovrabet/sdk";
const client = createClient({
appCode: "your-app-code",
accessKey: process.env.LOVRABET_ACCESS_KEY, // ✅ 从环境变量读取
models: {
users: {
tableName: "users",
datasetCode: "your-dataset-code",
},
},
});
// SDK 自动生成 Token 并处理认证
const users = await client.models.users.filter();
工作原理
当提供 accessKey 时:
- SDK 使用
accessKey和当前时间戳 - 通过 HMAC-SHA256 算法实时生成 Token
- 自动在请求头中添加认证信息:
X-App-Code: 应用标识X-Dataset-Code: 数据集标识X-Time-Stamp: 当前时间戳X-Token: 生成的签名
Next.js Server Component 示例
// app/users/page.tsx (Server Component)
import { createClient } from "@lovrabet/sdk";
// 在服务端组件中创建客户端
const client = createClient({
appCode: process.env.LOVRABET_APP_CODE!,
accessKey: process.env.LOVRABET_ACCESS_KEY!,
models: {
users: {
tableName: "users",
datasetCode: process.env.LOVRABET_DATASET_CODE!,
},
},
});
export default async function UsersPage() {
// 直接在服务端调用
const { tableData: users } = await client.models.users.filter();
return (
<div>
{users.map((user) => (
<div key={user.id}>{user.name}</div>
))}
</div>
);
}
环境变量配置
创建 .env.local 文件:
LOVRABET_APP_CODE=your-app-code
LOVRABET_ACCESS_KEY=your-access-key
LOVRABET_DATASET_CODE=your-dataset-code
安全最佳实践
- ✅ 使用环境变量存储 Access Key
- ✅
.env.local加入.gitignore - ✅ 不同环境使用不同的密钥
- ❌ 不要硬编码密钥到代码中
模式 2: 浏览器 Token 认证(预生成)
适用于浏览器端公开数据访问、未登录用户等场景。
重要安全提示
绝对不能在浏览器代码中使用 accessKey!必须在服务端生成 Token,然后传递给浏览器使用。
步骤 1: 服务端生成 Token
使用 generateOpenApiToken() 函数生成 Token:
// app/api/token/route.ts
import { generateOpenApiToken } from "@lovrabet/sdk";
import { NextResponse } from "next/server";
export async function GET() {
try {
const result = await generateOpenApiToken({
appCode: process.env.LOVRABET_APP_CODE!,
datasetCode: process.env.LOVRABET_DATASET_CODE!,
accessKey: process.env.LOVRABET_ACCESS_KEY!,
// secretKey: "lovrabet", // 可选,默认为 "lovrabet"
// timestamp: Date.now(), // 可选,默认为当前时间
});
// result 包含: { token, timestamp, expiresAt }
return NextResponse.json(result);
} catch (error) {
return NextResponse.json({ error: "Token 生成失败" }, { status: 500 });
}
}
返回数据结构:
{
token: string; // 生成的 Token
timestamp: number; // 时间戳(毫秒)
expiresAt: Date; // 过期时间
}
步骤 2: 浏览器使用 Token
// app/users/client-page.tsx
"use client";
import { createClient } from "@lovrabet/sdk";
import { useEffect, useState } from "react";
export default function ClientUsersPage() {
const [users, setUsers] = useState([]);
useEffect(() => {
async function init() {
// 1. 从服务端获取 Token
const { token, timestamp } = await fetch("/api/token").then((r) =>
r.json()
);
// 2. 创建客户端(使用 token 和 timestamp)
const client = createClient({
appCode: "your-app-code",
token: token, // 预生成的 token
timestamp: timestamp, // 对应的时间戳
models: {
users: { tableName: "users", datasetCode: "your-dataset-code" },
},
});
// 3. 查询数据
const { tableData } = await client.models.users.filter();
setUsers(tableData);
}
init();
}, []);
return (
<div>
{users.map((user) => (
<div key={user.id}>{user.name}</div>
))}
</div>
);
}
工作原理
当提供 token 和 timestamp 时:
- SDK 直接使用预生成的 Token
- 不再实时计算签名
- 在请求头中添加:
X-Token: 预生成的 TokenX-Time-Stamp: 对应的时间戳X-App-Code和X-Dataset-Code
Token 有效期管理
Token 有效期为 10 分钟(600 秒),过期后需要刷新。
检查 Token 是否即将过期
import { isTokenExpiring, getTokenRemainingTime } from "@lovrabet/sdk";
const timestamp = Date.now();
// 检查是否即将过期(默认缓冲时间 1 分钟)
if (isTokenExpiring(timestamp)) {
console.log("Token 即将过期,需要刷新");
}
// 检查是否即将过期(自定义缓冲时间 2 分钟)
if (isTokenExpiring(timestamp, 120000)) {
console.log("2 分钟内将过期");
}
// 获取剩余时间(毫秒)
const remaining = getTokenRemainingTime(timestamp);
console.log(`Token 剩余 ${remaining / 1000} 秒`);
自动刷新 Token
"use client";
import {
createClient,
isTokenExpiring,
getTokenRemainingTime,
} from "@lovrabet/sdk";
import { useEffect, useState } from "react";
export default function UsersWithAutoRefresh() {
const [client, setClient] = useState(null);
const [timestamp, setTimestamp] = useState(null);
const [remainingTime, setRemainingTime] = useState(null);
// 初始化
useEffect(() => {
fetchTokenAndCreateClient();
}, []);
// 定期检查 Token 有效期
useEffect(() => {
if (!timestamp) return;
const interval = setInterval(() => {
const remaining = getTokenRemainingTime(timestamp);
setRemainingTime(remaining);
// 提前 1 分钟刷新
if (isTokenExpiring(timestamp, 60000)) {
console.log("Token 即将过期,刷新中...");
fetchTokenAndCreateClient();
}
}, 10000); // 每 10 秒检查一次
return () => clearInterval(interval);
}, [timestamp]);
async function fetchTokenAndCreateClient() {
const { token, timestamp: newTimestamp } = await fetch("/api/token").then(
(r) => r.json()
);
const newClient = createClient({
appCode: "your-app-code",
token: token,
timestamp: newTimestamp,
models: {
users: { tableName: "users", datasetCode: "your-dataset-code" },
},
});
setClient(newClient);
setTimestamp(newTimestamp);
}
// 显示剩余时间
return (
<div>
<p>Token 剩余: {Math.floor((remainingTime || 0) / 1000)} 秒</p>
{/* ... 其他内容 */}
</div>
);
}
批量生成 Token
如果需要为多个数据集生成 Token,使用 TokenGenerator 类:
import { TokenGenerator } from "@lovrabet/sdk";
const generator = new TokenGenerator(
process.env.LOVRABET_ACCESS_KEY!,
"lovrabet" // secretKey,可选
);
// 批量生成
const tokens = await generator.generateBatch({
appCode: "your-app-code",
datasets: [
{ name: "users", code: "dataset-001" },
{ name: "orders", code: "dataset-002" },
{ name: "products", code: "dataset-003" },
],
// timestamp: Date.now(), // 可选,默认当前时间
});
// 使用生成的 tokens
console.log(tokens.users.token);
console.log(tokens.users.timestamp);
console.log(tokens.users.expiresAt);
console.log(tokens.orders.token);
console.log(tokens.products.token);
// 返回一个 API 端点
export async function GET() {
return NextResponse.json(tokens);
}
TokenGenerator 方法
class TokenGenerator {
constructor(accessKey: string, secretKey?: string);
// 生成单个 Token
generate(params: {
appCode: string;
datasetCode: string;
timestamp?: number;
}): Promise<TokenResult>;
// 批量生成 Token
generateBatch(params: {
appCode: string;
datasets: Array<{ name: string; code: string }>;
timestamp?: number;
}): Promise<Record<string, TokenResult>>;
}
模式 3: 浏览器 Cookie 认证
适用于已登录用户访问私有数据的场景。
使用方法
"use client";
import { createClient } from "@lovrabet/sdk";
// 不提供任何认证信息
const client = createClient({
appCode: "your-app-code",
models: {
orders: { tableName: "orders", datasetCode: "your-dataset-code" },
},
});
// 请求会自动携带浏览器的登录 Cookie
const { tableData } = await client.models.orders.filter();
工作原理
当未提供 accessKey 或 token 时:
- SDK 使用 WebAPI 模式
- 请求会自动携带浏览器的 Cookie
- 使用用户的登录态进行认证
- 适用于已登录用户访问自己的数据
应用场景
- ✅ 用户订单列表
- ✅ 个人资料数据
- ✅ 用户收藏/购物车
- ❌ 公开数据访问(未登录用户)
认证模式选择
决策树
是否在服务端?
├─ 是 → 使用 accessKey 模式(模式 1)
└─ 否(浏览器端)
├─ 用户已登录且访问私有数据?
│ ├─ 是 → 使用 Cookie 模式(模式 3)
│ └─ 否 → 使用预生成 Token 模式(模式 2)
└─ 公开数据访问?
└─ 是 → 使用预生成 Token 模式(模式 2)
对比表
| 特性 | 服务端模式 | Token 模式 | Cookie 模式 |
|---|---|---|---|
| 运行环境 | Node.js 服务端 | 浏览器 | 浏览器 |
| 认证信息 | accessKey | token + timestamp | Cookie |
| Token 生成 | 实时生成 | 预生成 | 无需 Token |
| 有效期 | 无限制 | 10 分钟 | 依赖登录态 |
| 安全性 | 最高 | 高 | 中 |
| 适用场景 | SSR、API 路由 | 公开数据访问 | 私有数据访问 |
错误处理
认证相关错误码
| 错误码 | 说明 | 解决方案 |
|---|---|---|
| 1002 | 签名验证失败 | 检查 Access Key 是否正确 |
| 1003 | 时间戳过期 | Token 已过期,重新生成 |
| 1004 | 应用不存在 | 检查 App Code 是否正确 |
| 1005 | 数据集不存在 | 检查 Dataset Code 是否正确 |
| 1006 | 无访问权限 | 确认应用有该数据集的访问权限 |
错误处理示例
import { LovrabetError } from "@lovrabet/sdk";
try {
const users = await client.models.users.filter();
} catch (error) {
if (error instanceof LovrabetError) {
switch (error.statusCode) {
case 1002:
console.error("签名验证失败,请检查 Access Key");
break;
case 1003:
console.error("Token 已过期,正在刷新...");
// 重新获取 token
await fetchTokenAndCreateClient();
break;
case 1006:
console.error("无权访问该数据集");
break;
default:
console.error("API 错误:", error.message);
}
} else {
console.error("未知错误:", error);
}
}
最佳实践
1. 凭证安全
关键安全规则
- ✅ 服务端: 使用环境变量存储 Access Key
- ✅ 浏览器: 使用预生成 Token,绝不暴露 Access Key
- ✅ 定期更换 Access Key
- ✅ 不同环境使用不同的密钥
- ❌ 绝不在客户端代码中硬编码 Access Key
- ❌ 绝不将 Access Key 提交到代码仓库
错误示例:
// ❌ 危险!会暴露 Access Key
const client = createClient({
accessKey: "ak-xxxxx", // 不要这样做!
});
正确示例:
// ✅ 服务端:使用环境变量
const client = createClient({
accessKey: process.env.LOVRABET_ACCESS_KEY,
});
// ✅ 浏览器:使用预生成 Token
const { token } = await fetch("/api/token").then((r) => r.json());
const client = createClient({ token });
2. 客户端实例复用
// ✅ 推荐:复用客户端实例
const client = createClient({
/* 配置 */
});
const users = await client.models.users.filter();
const orders = await client.models.orders.filter();
// ❌ 避免:重复创建客户端
const client1 = createClient({
/* 配置 */
});
const users = await client1.models.users.filter();
const client2 = createClient({
/* 配置 */
}); // 不必要
const orders = await client2.models.orders.filter();
3. Token 刷新策略
// 提前刷新,避免过期
if (isTokenExpiring(timestamp, 60000)) {
// 提前 1 分钟刷新
await refreshToken();
}
// 错误时重试
try {
await api.call();
} catch (error) {
if (error.statusCode === 1003) {
// Token 过期,刷新后重试
await refreshToken();
await api.call();
}
}
高级用法
动态更新 Token
const client = createClient({
appCode: "your-app-code",
models: {
/* ... */
},
});
// 后续更新 token
client.setToken(newToken, newTimestamp);
自定义请求选项
const client = createClient({
appCode: "your-app-code",
accessKey: process.env.LOVRABET_ACCESS_KEY,
models: {
/* ... */
},
options: {
timeout: 30000, // 30 秒超时
// 其他 fetch 选项
},
});
签名算法详解(高级)
仅供参考
SDK 已自动处理签名生成,通常无需关心底层实现。
签名生成步骤:
-
收集参数:
{
accessKey: "your-access-key",
timeStamp: "1758903130713",
appCode: "app-c2dd52a2",
datasetCode: "0fefba76fe29..."
} -
按字典序排序并拼接:
accessKey=your-access-key&appCode=app-c2dd52a2&datasetCode=0fefba76fe29...&timeStamp=1758903130713 -
使用 HMAC-SHA256 计算签名:
const token = crypto
.createHmac("sha256", secretKey)
.update(sortedParams, "utf8")
.digest("base64"); -
添加到请求头:
X-Token: jdqqGtzecF2I6FIW0s2xv0YRIprUqt0FLEJPYCgGB+4=
常见问题
Q: 为什么推荐使用 SDK?
A: SDK 封装了所有认证细节,包括:
- ✅ 自动生成签名
- ✅ 处理时间戳
- ✅ Token 生命周期管理
- ✅ 错误处理和重试
- ✅ TypeScript 类型支持
Q: Token 有效期是多久?
A: Token 有效期为 10 分钟(600 秒)。建议提前 1-2 分钟刷新,避免过期。
Q: 如何在浏览器中安全使用?
A: 绝不在浏览器中使用 accessKey,而是:
- 服务端生成 Token
- 通过 API 返回给前端
- 前端使用预生成的 Token
Q: Access Key 泄露了怎么办?
A: 立即采取行动:
- 联系商务经理更换新的 Access Key
- 停用旧的 Access Key
- 更新所有使用旧 Key 的应用
- 审计可能受影响的数据
Q: 可以自定义 Token 有效期吗?
A: 不可以,Token 有效期固定为 10 分钟。这是出于安全考虑的设计。
下一步
需要帮助?
如遇到认证相关问题:
- 检查本文档的错误处理部分
- 查看 GitHub Issues
- 联系商务经理获取技术支持