Skip to content

认证配置

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

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

认证方式总览

SDK 支持以下认证方式:

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

👤 用户 Token 认证

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

TypeScript
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:直接传入配置

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

实际应用示例

TypeScript
// 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 会带来严重的安全风险:

正确的做法:

认证流程对比:

安全的服务器端代理模式

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

TypeScript
// 服务器端:使用 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 指定(推荐)

TypeScript
// 先注册基础配置(推荐使用 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:直接传入配置

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

服务端应用示例

TypeScript
// 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 进行认证。这适用于已登录用户在浏览器中的使用场景。

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

混合认证策略

TypeScript
// 优先使用 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

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

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

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

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

实际登录流程示例

TypeScript
// 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 示例

TypeScript
// 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 存储安全

TypeScript
// 安全的 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. 环境变量管理

TypeScript
// .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. 错误处理和重试

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

🔍 调试认证问题

获取当前配置信息

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

认证状态检测

TypeScript
// 检测认证是否有效
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;
  }
};

📖 下一步

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

  • 📊 API 使用指南 - 深入了解 API 操作
  • 🎯 TypeScript 支持 - 类型安全的开发体验
  • 🛠️ 实际应用示例 - React/Vue 集成示例

❓ 常见问题

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

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

Q: 如何处理 Token 过期?

TypeScript
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 密钥认证:

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

下一步

  • 📋 配置详解 - 了解各种配置方式
  • 📊 API 使用指南 - 深入了解 API 操作
  • 🛠️ 实际应用示例 - React/Vue 集成示例

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