Skip to content

认证指南

本文档深入介绍 Lovrabet OpenAPI 的认证机制、Token 管理和最佳实践。

认证概述

Lovrabet OpenAPI 采用基于 HMAC-SHA256 签名的认证机制,根据运行环境和使用场景,支持三种认证模式:

认证模式适用场景密钥要求
服务端模式Node.js 服务端、SSRaccessKey
浏览器 Token 模式浏览器公开数据访问预生成 token + timestamp
浏览器 Cookie 模式已登录用户私有数据无需密钥(使用 Cookie)

TIP

SDK 自动处理官方 SDK 会自动根据提供的参数选择合适的认证模式,无需手动指定。

认证凭证

您需要的凭证

凭证名称说明获取方式示例
App Code应用唯一标识联系商务经理app-c2dd52a2
Access Key访问密钥联系商务经理ak-xxxxxxxxxxxxx
Dataset Code数据集标识联系商务经理0fefba76fe29...ff

INFO

安全提示Access Key 是敏感信息,请妥善保管,不要泄露或硬编码到代码中。

模式 1: 服务端认证(accessKey)

适用于 Node.js 服务端、Next.js SSR、API 路由等场景。

基础用法

TypeScript
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 时:

  1. SDK 使用 accessKey 和当前时间戳

  2. 通过 HMAC-SHA256 算法实时生成 Token

  3. 自动在请求头中添加认证信息:

    • X-App-Code: 应用标识
    • X-Dataset-Code: 数据集标识
    • X-Time-Stamp: 当前时间戳
    • X-Token: 生成的签名

Next.js Server Component 示例

TypeScript
// 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 文件:

Bash
LOVRABET_APP_CODE=your-app-code
LOVRABET_ACCESS_KEY=your-access-key
LOVRABET_DATASET_CODE=your-dataset-code

TIP

安全最佳实践

  • ✅ 使用环境变量存储 Access Key
  • .env.local 加入 .gitignore
  • ✅ 不同环境使用不同的密钥
  • ❌ 不要硬编码密钥到代码中

模式 2: 浏览器 Token 认证(预生成)

适用于浏览器端公开数据访问、未登录用户等场景。

INFO

重要安全提示绝对不能在浏览器代码中使用 accessKey!必须在服务端生成 Token,然后传递给浏览器使用。

步骤 1: 服务端生成 Token

使用 generateOpenApiToken() 函数生成 Token:

TypeScript
// 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 });
  }
}

返回数据结构:

TypeScript
{
  token: string; // 生成的 Token
  timestamp: number; // 时间戳(毫秒)
  expiresAt: Date; // 过期时间
}

步骤 2: 浏览器使用 Token

TypeScript
// 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>
  );
}

工作原理

当提供 tokentimestamp 时:

  1. SDK 直接使用预生成的 Token

  2. 不再实时计算签名

  3. 在请求头中添加:

    • X-Token: 预生成的 Token
    • X-Time-Stamp: 对应的时间戳
    • X-App-CodeX-Dataset-Code

Token 有效期管理

Token 有效期为 10 分钟(600 秒),过期后需要刷新。

检查 Token 是否即将过期

TypeScript
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

TypeScript
"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 类:

TypeScript
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 方法

TypeScript
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>>;
}

适用于已登录用户访问私有数据的场景。

使用方法

TypeScript
"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();

工作原理

当未提供 accessKeytoken 时:

  1. SDK 使用 WebAPI 模式
  2. 请求会自动携带浏览器的 Cookie
  3. 使用用户的登录态进行认证
  4. 适用于已登录用户访问自己的数据

应用场景

  • ✅ 用户订单列表
  • ✅ 个人资料数据
  • ✅ 用户收藏/购物车
  • ❌ 公开数据访问(未登录用户)

认证模式选择

决策树

Plain
是否在服务端?
├─ 是 → 使用 accessKey 模式(模式 1)
└─ 否(浏览器端)
   ├─ 用户已登录且访问私有数据?
   │  ├─ 是 → 使用 Cookie 模式(模式 3)
   │  └─ 否 → 使用预生成 Token 模式(模式 2)
   └─ 公开数据访问?
      └─ 是 → 使用预生成 Token 模式(模式 2)

对比表

特性服务端模式Token 模式Cookie 模式
运行环境Node.js 服务端浏览器浏览器
认证信息accessKeytoken + timestampCookie
Token 生成实时生成预生成无需 Token
有效期无限制10 分钟依赖登录态
安全性最高
适用场景SSR、API 路由公开数据访问私有数据访问

错误处理

认证相关错误码

错误码说明解决方案
1002签名验证失败检查 Access Key 是否正确
1003时间戳过期Token 已过期,重新生成
1004应用不存在检查 App Code 是否正确
1005数据集不存在检查 Dataset Code 是否正确
1006无访问权限确认应用有该数据集的访问权限

错误处理示例

TypeScript
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. 凭证安全

INFO

关键安全规则

  • 服务端: 使用环境变量存储 Access Key
  • 浏览器: 使用预生成 Token,绝不暴露 Access Key
  • ✅ 定期更换 Access Key
  • ✅ 不同环境使用不同的密钥
  • 绝不在客户端代码中硬编码 Access Key
  • 绝不将 Access Key 提交到代码仓库

错误示例:

TypeScript
// ❌ 危险!会暴露 Access Key
const client = createClient({
  accessKey: "ak-xxxxx", // 不要这样做!
});

正确示例:

TypeScript
// ✅ 服务端:使用环境变量
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. 客户端实例复用

TypeScript
// ✅ 推荐:复用客户端实例
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 刷新策略

TypeScript
// 提前刷新,避免过期
if (isTokenExpiring(timestamp, 60000)) {
  // 提前 1 分钟刷新
  await refreshToken();
}

// 错误时重试
try {
  await api.call();
} catch (error) {
  if (error.statusCode === 1003) {
    // Token 过期,刷新后重试
    await refreshToken();
    await api.call();
  }
}

高级用法

动态更新 Token

TypeScript
const client = createClient({
  appCode: "your-app-code",
  models: {
    /* ... */
  },
});

// 后续更新 token
client.setToken(newToken, newTimestamp);

自定义请求选项

TypeScript
const client = createClient({
  appCode: "your-app-code",
  accessKey: process.env.LOVRABET_ACCESS_KEY,
  models: {
    /* ... */
  },
  options: {
    timeout: 30000, // 30 秒超时
    // 其他 fetch 选项
  },
});

签名算法详解(高级)

INFO

仅供参考SDK 已自动处理签名生成,通常无需关心底层实现。

签名生成步骤:

  1. 收集参数:
  2. 按字典序排序并拼接:
  3. 使用 HMAC-SHA256 计算签名:
  4. 添加到请求头:

常见问题

Q: 为什么推荐使用 SDK?

A: SDK 封装了所有认证细节,包括:

  • ✅ 自动生成签名
  • ✅ 处理时间戳
  • ✅ Token 生命周期管理
  • ✅ 错误处理和重试
  • ✅ TypeScript 类型支持

Q: Token 有效期是多久?

A: Token 有效期为 10 分钟(600 秒)。建议提前 1-2 分钟刷新,避免过期。

Q: 如何在浏览器中安全使用?

A: 绝不在浏览器中使用 accessKey,而是:

  1. 服务端生成 Token
  2. 通过 API 返回给前端
  3. 前端使用预生成的 Token

Q: Access Key 泄露了怎么办?

A: 立即采取行动:

  1. 联系商务经理更换新的 Access Key
  2. 停用旧的 Access Key
  3. 更新所有使用旧 Key 的应用
  4. 审计可能受影响的数据

Q: 可以自定义 Token 有效期吗?

A: 不可以,Token 有效期固定为 10 分钟。这是出于安全考虑的设计。

下一步

需要帮助?

如遇到认证相关问题:

  1. 检查本文档的错误处理部分
  2. 查看 GitHub Issues
  3. 联系商务经理获取技术支持

基于飞书知识库同步生成,内容以飞书源文档为准