跳到主要内容

认证配置

Lovrabet SDK 支持多种认证方式,满足不同场景的安全需求。本文详细介绍各种认证方式的配置和使用。

前置条件

开始之前,请确保已完成 SDK 配置。推荐使用 CLI 自动生成配置

认证方式总览

SDK 支持以下认证方式:

  1. 用户 Token 认证 - 适用于用户登录后的场景
  2. OpenAPI 密钥认证 - 适用于服务端到服务端的调用
  3. Cookie 认证(自动) - 适用于已登录用户的浏览器环境
  4. 运行时动态切换 - 支持认证信息的动态更新

👤 用户 Token 认证

方式 1:通过 ClientConfig 指定(推荐)

import { registerModels, createClient } from "@lovrabet/sdk";

// 先注册基础配置(推荐使用 CLI 自动生成)
registerModels({
appCode: "my-app",
models: [
{ datasetCode: "8d2dcbae08b54bdd84c00be558ed48df", tableName: "users", alias: "users" },
{ datasetCode: "a1b2c3d4e5f6789012345678abcdef12", tableName: "orders", alias: "orders" },
],
});

// 创建带认证的客户端
const client = createClient({
token: "your-user-token",
});

// 标准方式访问
const users = await client.models.dataset_8d2dcbae08b54bdd84c00be558ed48df.filter();
// 或使用别名(语法糖)
const users = await client.models.users.filter();

方式 2:直接传入配置

const client = createClient({
appCode: "my-app",
token: "your-user-token",
models: [
{ datasetCode: "8d2dcbae08b54bdd84c00be558ed48df", tableName: "users", alias: "users" },
{ datasetCode: "a1b2c3d4e5f6789012345678abcdef12", tableName: "orders", alias: "orders" },
],
});

// 标准方式访问
const users = await client.models.dataset_8d2dcbae08b54bdd84c00be558ed48df.filter();

实际应用示例

// auth/client.ts
import { createClient } from "@lovrabet/sdk";
import "./api/api"; // 导入 CLI 生成的配置
import { getAuthToken } from "./auth-service";

// 创建认证客户端工厂
export const createAuthenticatedClient = () => {
const token = getAuthToken(); // 从存储中获取 token

return createClient({
token,
options: {
onError: (error) => {
if (error.message.includes("unauthorized")) {
// Token 失效,重定向到登录页
window.location.href = "/login";
}
},
},
});
};

// 使用
const client = createAuthenticatedClient();
// 标准方式访问
const users = await client.models.dataset_8d2dcbae08b54bdd84c00be558ed48df.filter();
// 或使用别名
const users = await client.models.users.filter();

🔑 OpenAPI 密钥认证

OpenAPI 密钥认证适用于服务端到服务端的调用,提供更高的安全性。

安全警告:不要在浏览器中使用 accessKey

⚠️ accessKey 只能在服务器端使用!

在浏览器环境中使用 accessKey 会带来严重的安全风险:

  • 🔓 密钥会暴露在客户端代码中,任何人都可以查看
  • 🚨 攻击者可以获取您的密钥并冒充您的应用发起请求
  • 💸 可能导致数据泄露、恶意操作和费用损失

正确的做法:

  • ✅ 在服务器端使用 accessKey 进行认证
  • ✅ 在浏览器端使用用户 Token 或 Cookie 认证
  • ✅ 通过服务器端 API 为前端生成和分发 Token

认证流程对比:

安全的服务器端代理模式

推荐在服务器端创建 API 代理,为前端提供安全的数据访问:

// 服务器端:使用 accessKey
import { createClient } from '@lovrabet/sdk';

const serverClient = createClient({
accessKey: process.env.LOVRABET_ACCESS_KEY,
});

// API 路由:/api/users
export async function GET(request: Request) {
// 验证用户身份(从 session/cookie)
const user = await validateUser(request);
if (!user) {
return Response.json({ error: 'Unauthorized' }, { status: 401 });
}

// 使用服务器端 client 获取数据
const users = await serverClient.models.dataset_8d2dcbae08b54bdd84c00be558ed48df.filter();
return Response.json(users);
}

// 浏览器端:调用服务器 API
fetch('/api/users')
.then(res => res.json())
.then(data => console.log(data));

方式 1:通过 ClientConfig 指定(推荐)

// 先注册基础配置(推荐使用 CLI 自动生成)
registerModels({
appCode: "my-app",
models: [
{ datasetCode: "8d2dcbae08b54bdd84c00be558ed48df", tableName: "users", alias: "users" },
{ datasetCode: "a1b2c3d4e5f6789012345678abcdef12", tableName: "analytics", alias: "analytics" },
],
});

// 创建带密钥认证的客户端
const client = createClient({
accessKey: "your-access-key",
secretKey: "your-secret-key",
});

// 标准方式访问
const users = await client.models.dataset_8d2dcbae08b54bdd84c00be558ed48df.filter();

方式 2:直接传入配置

const client = createClient({
appCode: "my-app",
accessKey: "your-access-key",
secretKey: "your-secret-key",
models: [
{ datasetCode: "8d2dcbae08b54bdd84c00be558ed48df", tableName: "users", alias: "users" },
{ datasetCode: "a1b2c3d4e5f6789012345678abcdef12", tableName: "analytics", alias: "analytics" },
],
});

// 标准方式访问
const analytics = await client.models.dataset_a1b2c3d4e5f6789012345678abcdef12.filter();

服务端应用示例

// server/lovrabet-client.ts
import { registerModels, createClient } from "@lovrabet/sdk";

registerModels({
appCode: process.env.LOVRABET_APP_CODE,
models: [
{
datasetCode: process.env.USERS_DATASET_CODE,
tableName: "users",
alias: "users",
},
{
datasetCode: process.env.ORDERS_DATASET_CODE,
tableName: "orders",
alias: "orders",
},
],
});

// 使用环境变量中的密钥
export const serverClient = createClient({
accessKey: process.env.LOVRABET_ACCESS_KEY,
secretKey: process.env.LOVRABET_SECRET_KEY,
});

// API 路由中使用
export async function GET() {
try {
// 使用环境变量中的 datasetCode
const users = await serverClient.models[`dataset_${process.env.USERS_DATASET_CODE}`].filter({
currentPage: 1,
pageSize: 100,
});

return Response.json(users);
} catch (error) {
console.error("获取用户数据失败:", error);
return Response.json({ error: "Internal Server Error" }, { status: 500 });
}
}

如果没有提供 token 或密钥,SDK 会自动使用浏览器的 Cookie 进行认证。这适用于已登录用户在浏览器中的使用场景。

import { createClient } from "@lovrabet/sdk";
import "./api/api"; // 导入 CLI 生成的配置

// 不提供任何认证信息,自动使用 Cookie
const client = createClient();

// 如果用户已登录,会自动使用浏览器中的登录 Cookie
// 标准方式访问
const users = await client.models.dataset_8d2dcbae08b54bdd84c00be558ed48df.filter();
// 或使用别名
const users = await client.models.users.filter();

混合认证策略

// 优先使用 Token,回退到 Cookie
const createSmartClient = (token?: string) => {
const config = token ? { token } : {}; // 有 token 就用,没有就依赖 Cookie

return createClient(config);
};

// 使用
const tokenFromStorage = localStorage.getItem("auth_token");
const client = createSmartClient(tokenFromStorage);

🔄 运行时切换认证

SDK 支持在运行时动态更新认证信息,无需重新创建客户端。

动态设置 Token

const client = createClient(); // 初始无认证

// 用户登录后,动态设置 token
client.setToken("new-user-token");

// 现在可以访问需要认证的 API
const users = await client.models.users.filter();

// 用户登出时,清除 token
client.setToken(null);

实际登录流程示例

// auth/auth-manager.ts
import { createClient } from "@lovrabet/sdk";
import "./api"; // 导入预注册的配置

class AuthManager {
private client = createClient(); // 初始化客户端

async login(username: string, password: string) {
try {
// 调用登录 API
const response = await fetch("/api/auth/login", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ username, password }),
});

if (response.ok) {
const { token } = await response.json();

// 设置 token 到 SDK
this.client.setToken(token);

// 存储到本地
localStorage.setItem("auth_token", token);

return { success: true };
} else {
return { success: false, error: "登录失败" };
}
} catch (error) {
return { success: false, error: "网络错误" };
}
}

logout() {
// 清除 SDK 中的 token
this.client.setToken(null);

// 清除本地存储
localStorage.removeItem("auth_token");

// 重定向到登录页
window.location.href = "/login";
}

getClient() {
return this.client;
}

// 初始化时从本地存储恢复 token
init() {
const token = localStorage.getItem("auth_token");
if (token) {
this.client.setToken(token);
}
}
}

export const authManager = new AuthManager();

// 应用启动时初始化
authManager.init();

// 导出客户端供业务代码使用
export const lovrabetClient = authManager.getClient();

React Hook 示例

// hooks/useAuth.ts
import { useState, useEffect } from "react";
import { authManager, lovrabetClient } from "../auth/auth-manager";

export function useAuth() {
const [isAuthenticated, setIsAuthenticated] = useState(false);
const [loading, setLoading] = useState(true);

useEffect(() => {
// 检查认证状态
const checkAuth = async () => {
try {
// 尝试获取用户信息来验证认证状态
await lovrabetClient.models.users.getOne("me");
setIsAuthenticated(true);
} catch (error) {
setIsAuthenticated(false);
} finally {
setLoading(false);
}
};

checkAuth();
}, []);

const login = async (username: string, password: string) => {
const result = await authManager.login(username, password);
if (result.success) {
setIsAuthenticated(true);
}
return result;
};

const logout = () => {
authManager.logout();
setIsAuthenticated(false);
};

return {
isAuthenticated,
loading,
login,
logout,
client: lovrabetClient,
};
}

🛡️ 安全最佳实践

1. Token 存储安全

// 安全的 Token 存储管理
class SecureTokenStorage {
private readonly TOKEN_KEY = "lovrabet_auth_token";

setToken(token: string) {
// 生产环境建议使用 httpOnly cookie 或其他安全存储方案
if (process.env.NODE_ENV === "production") {
// 设置安全的 cookie
document.cookie = `${this.TOKEN_KEY}=${token}; secure; httpOnly; samesite=strict`;
} else {
// 开发环境使用 localStorage
localStorage.setItem(this.TOKEN_KEY, token);
}
}

getToken(): string | null {
if (process.env.NODE_ENV === "production") {
// 从 cookie 中读取
const cookies = document.cookie.split(";");
const tokenCookie = cookies.find(c => c.trim().startsWith(this.TOKEN_KEY));
return tokenCookie ? tokenCookie.split("=")[1] : null;
} else {
return localStorage.getItem(this.TOKEN_KEY);
}
}

removeToken() {
if (process.env.NODE_ENV === "production") {
document.cookie = `${this.TOKEN_KEY}=; expires=Thu, 01 Jan 1970 00:00:00 GMT`;
} else {
localStorage.removeItem(this.TOKEN_KEY);
}
}
}

2. 环境变量管理

// .env.example
LOVRABET_APP_CODE=your-app-code
LOVRABET_ACCESS_KEY=your-access-key
LOVRABET_SECRET_KEY=your-secret-key

// config/lovrabet.ts
export const lovrabetConfig = {
appCode: process.env.LOVRABET_APP_CODE!,
accessKey: process.env.LOVRABET_ACCESS_KEY,
secretKey: process.env.LOVRABET_SECRET_KEY,
};

// 验证必要的环境变量
if (!lovrabetConfig.appCode) {
throw new Error("LOVRABET_APP_CODE is required");
}

3. 错误处理和重试

const client = createClient({
token: "user-token",
options: {
timeout: 30000,
onError: (error) => {
console.error("Lovrabet SDK Error:", {
message: error.message,
status: error.status,
url: error.url,
});

// 根据错误类型进行不同处理
if (error.status === 401) {
// 认证失败
console.log("Authentication failed, redirecting to login...");
authManager.logout();
} else if (error.status >= 500) {
// 服务器错误,可以考虑重试
console.log("Server error, may retry later");
}
},
},
});

🔍 调试认证问题

获取当前配置信息

const client = createClient({ token: "your-token" });

// 获取当前配置(不包含敏感信息)
const config = client.getConfig();
console.log("当前配置:", {
appCode: config.appCode,
env: config.env,
hasToken: !!config.token,
hasAccessKey: !!config.accessKey,
serverUrl: config.serverUrl,
});

认证状态检测

// 检测认证是否有效
const checkAuthStatus = async (client) => {
try {
// 尝试调用一个需要认证的 API
await client.models.users.filter({ pageSize: 1 });
console.log("✅ 认证有效");
return true;
} catch (error) {
if (error.status === 401) {
console.log("❌ 认证无效或已过期");
} else {
console.log("❓ 其他错误:", error.message);
}
return false;
}
};

📖 下一步

了解认证配置后,您可以继续学习:

❓ 常见问题

Q: Token 和 OpenAPI 密钥有什么区别?

  • Token:用户级别的认证,适用于前端应用和用户相关的操作
  • OpenAPI 密钥:应用级别的认证,适用于服务端到服务端的调用,权限更高

Q: 如何处理 Token 过期?

const client = createClient({
token: "initial-token",
options: {
onError: async (error) => {
if (error.status === 401) {
// Token 可能过期,尝试刷新
try {
const newToken = await refreshToken();
client.setToken(newToken);
// 可以选择重试原始请求
} catch (refreshError) {
// 刷新失败,跳转到登录页
window.location.href = "/login";
}
}
},
},
});

Q: 在 Node.js 环境中如何使用?

Node.js 环境推荐使用 OpenAPI 密钥认证:

// server/lovrabet.js
const { registerModels, createClient } = require("@lovrabet/sdk");

registerModels({
appCode: process.env.LOVRABET_APP_CODE,
models: [
{ datasetCode: process.env.USERS_DATASET_CODE, tableName: "users", alias: "users" },
],
});

const client = createClient({
accessKey: process.env.LOVRABET_ACCESS_KEY,
secretKey: process.env.LOVRABET_SECRET_KEY,
});

module.exports = { client };

下一步