Bknd认证配置Skill bknd-setup-auth

这个技能用于配置Bknd后端框架的身份验证系统,包括启用认证、设置JWT令牌、配置Cookie、定义用户角色和权限,适用于后端开发和应用程序安全。关键词:Bknd, 认证, JWT, Cookie, 角色, 后端安全

后端开发 0 次安装 0 次浏览 更新于 3/8/2026

名称: bknd设置认证 描述: 用于初始化和配置Bknd身份验证系统。涵盖启用认证、配置密码策略、设置JWT和Cookie选项、定义角色和生产安全设置。

设置认证

初始化和配置Bknd身份验证系统,包括策略、JWT、Cookie和角色。

前提条件

  • Bknd项目已初始化
  • 代码优先配置(认证配置仅限代码)
  • 对于OAuth:提供商凭证(客户端ID、客户端密钥)

何时使用UI模式

  • 查看当前认证配置
  • 切换策略开关
  • 通过管理面板测试认证端点

UI步骤: 管理面板 > 认证 > 设置

注意: 完整认证配置需要代码模式。UI仅显示/切换现有设置。

何时使用代码模式

  • 初始认证设置
  • 配置JWT密钥和过期时间
  • 设置密码哈希
  • 定义角色和权限
  • 生产安全加固

代码方法

步骤1:启用认证并最小化配置

从基本密码认证开始:

import { serve } from "bknd/adapter/bun";
import { em, entity, text } from "bknd";

const schema = em({
  posts: entity("posts", { title: text().required() }),
});

serve({
  connection: { url: "file:data.db" },
  config: {
    data: schema.toJSON(),
    auth: {
      enabled: true,
    },
  },
});

这启用:

  • 密码策略(默认)
  • 自动创建的users实体
  • 基于JWT的会话
  • /api/auth/*端点

步骤2:配置JWT设置

JWT令牌用于API请求认证。为安全配置:

{
  auth: {
    enabled: true,
    jwt: {
      secret: process.env.JWT_SECRET,  // 生产中必需
      alg: "HS256",                     // 算法:HS256、HS384、HS512
      expires: 604800,                  // 过期时间(秒,7天)
      issuer: "my-app",                 // 可选发行者声明
      fields: ["id", "email", "role"],  // 令牌中包含的字段
    },
  },
}

JWT选项:

选项 类型 默认 描述
secret 字符串 "" 签名密钥(生产中至少256位)
alg 字符串 "HS256" 算法:HS256、HS384、HS512
expires 数字 - 令牌过期时间(秒)
issuer 字符串 - 令牌发行者声明(iss)
fields 字符串数组 ["id", "email", "role"] 有效载荷中的用户字段

步骤3:配置Cookie设置

认证Cookie用于浏览器会话存储JWT令牌:

{
  auth: {
    enabled: true,
    jwt: { secret: process.env.JWT_SECRET },
    cookie: {
      secure: true,           // 仅HTTPS(本地开发设为false)
      httpOnly: true,         // 阻止JavaScript访问
      sameSite: "lax",        // CSRF保护:"strict" | "lax" | "none"
      expires: 604800,        // Cookie过期时间(秒,7天)
      path: "/",              // Cookie路径范围
      renew: true,            // 请求时自动延长
      pathSuccess: "/",       // 登录后重定向
      pathLoggedOut: "/",     // 登出后重定向
    },
  },
}

Cookie选项:

选项 类型 默认 描述
secure 布尔值 true 仅HTTPS标志
httpOnly 布尔值 true 阻止JS访问
sameSite 字符串 "lax" CSRF保护
expires 数字 604800 过期时间(秒)
renew 布尔值 true 自动延长过期时间
pathSuccess 字符串 "/" 登录后重定向
pathLoggedOut 字符串 "/" 登出后重定向

步骤4:配置密码策略

设置密码哈希和要求:

{
  auth: {
    enabled: true,
    jwt: { secret: process.env.JWT_SECRET },
    strategies: {
      password: {
        type: "password",
        enabled: true,
        config: {
          hashing: "bcrypt",   // "plain" | "sha256" | "bcrypt"
          rounds: 4,           // bcrypt轮数(1-10)
          minLength: 8,        // 最小密码长度
        },
      },
    },
  },
}

哈希选项:

选项 安全性 性能 使用场景
plain 最快 仅开发,切勿生产
sha256 良好 默认,适合大多数情况
bcrypt 最佳 较慢 生产推荐

步骤5:定义角色

配置角色以进行授权:

{
  auth: {
    enabled: true,
    jwt: { secret: process.env.JWT_SECRET },
    roles: {
      admin: {
        implicit_allow: true,  // 可以做所有事情
      },
      editor: {
        implicit_allow: false,
        permissions: [
          { permission: "data.posts.read", effect: "allow" },
          { permission: "data.posts.create", effect: "allow" },
          { permission: "data.posts.update", effect: "allow" },
        ],
      },
      user: {
        implicit_allow: false,
        is_default: true,  // 新注册的默认角色
        permissions: [
          { permission: "data.posts.read", effect: "allow" },
        ],
      },
    },
    default_role_register: "user",  // 注册时分配的角色
  },
}

步骤6:配置注册

控制用户自助注册:

{
  auth: {
    enabled: true,
    allow_register: true,           // 启用/禁用注册
    default_role_register: "user",  // 新用户角色
    entity_name: "users",           // 用户实体名称(默认:"users")
    basepath: "/api/auth",          // 认证API基础路径
  },
}

完整生产示例

完整的认证设置,遵循安全最佳实践:

import { serve, type BunBkndConfig } from "bknd/adapter/bun";
import { em, entity, text, date } from "bknd";

const schema = em({
  users: entity("users", {
    email: text().required().unique(),
    name: text(),
    avatar: text(),
    created_at: date({ default_value: "now" }),
  }),
  posts: entity("posts", {
    title: text().required(),
    content: text(),
  }),
});

type Database = (typeof schema)["DB"];
declare module "bknd" {
  interface DB extends Database {}
}

const config: BunBkndConfig = {
  connection: { url: process.env.DB_URL || "file:data.db" },
  config: {
    data: schema.toJSON(),
    auth: {
      enabled: true,
      basepath: "/api/auth",
      entity_name: "users",
      allow_register: true,
      default_role_register: "user",

      // JWT配置
      jwt: {
        secret: process.env.JWT_SECRET!,
        alg: "HS256",
        expires: 604800,  // 7天
        issuer: "my-app",
        fields: ["id", "email", "role"],
      },

      // Cookie配置
      cookie: {
        secure: process.env.NODE_ENV === "production",
        httpOnly: true,
        sameSite: "lax",
        expires: 604800,
        renew: true,
        pathSuccess: "/dashboard",
        pathLoggedOut: "/login",
      },

      // 密码策略
      strategies: {
        password: {
          type: "password",
          enabled: true,
          config: {
            hashing: "bcrypt",
            rounds: 4,
            minLength: 8,
          },
        },
      },

      // 角色
      roles: {
        admin: {
          implicit_allow: true,
        },
        editor: {
          implicit_allow: false,
          permissions: [
            { permission: "data.posts.read", effect: "allow" },
            { permission: "data.posts.create", effect: "allow" },
            { permission: "data.posts.update", effect: "allow" },
            { permission: "data.posts.delete", effect: "allow" },
          ],
        },
        user: {
          implicit_allow: false,
          is_default: true,
          permissions: [
            { permission: "data.posts.read", effect: "allow" },
          ],
        },
      },
    },
  },
  options: {
    seed: async (ctx) => {
      // 首次运行时创建初始管理员
      const adminExists = await ctx.em.repo("users").findOne({
        where: { email: { $eq: "admin@example.com" } },
      });

      if (!adminExists) {
        await ctx.app.module.auth.createUser({
          email: "admin@example.com",
          password: process.env.ADMIN_PASSWORD || "changeme123",
          role: "admin",
        });
        console.log("管理员用户已创建");
      }
    },
  },
};

serve(config);

认证端点

设置后,这些端点可用:

方法 路径 描述
POST /api/auth/password/login 使用邮箱/密码登录
POST /api/auth/password/register 注册新用户
GET /api/auth/me 获取当前用户
POST /api/auth/logout 登出(清除Cookie)
GET /api/auth/strategies 列出启用的策略

环境变量

认证推荐的环境变量:

# .env
JWT_SECRET=您的256位密钥,至少32字符长
ADMIN_PASSWORD=安全初始管理员密码

生成安全密钥:

# 生成64字符随机字符串
openssl rand -hex 32

开发 vs 生产

设置 开发 生产
jwt.secret 可使用占位符 必需,强密钥
cookie.secure false true(仅HTTPS)
strategies.password.config.hashing sha256 bcrypt
allow_register true 对于封闭系统考虑false

开发配置快捷方式:

const isDev = process.env.NODE_ENV !== "production";

{
  auth: {
    enabled: true,
    jwt: {
      secret: isDev ? "dev-secret-not-for-production" : process.env.JWT_SECRET!,
      expires: isDev ? 86400 * 30 : 604800,  // 30天开发,7天生产
    },
    cookie: {
      secure: !isDev,
    },
    strategies: {
      password: {
        type: "password",
        config: {
          hashing: isDev ? "sha256" : "bcrypt",
        },
      },
    },
  },
}

常见问题

生产中缺少JWT密钥

问题: Cannot sign JWT without secret 错误

修复: 通过环境变量设置JWT密钥:

{
  auth: {
    jwt: {
      secret: process.env.JWT_SECRET,  // 生产中切勿硬编码
    },
  },
}

Cookie未设置(HTTPS问题)

问题: 浏览器中未设置认证Cookie

修复: 本地开发时设置secure: false

{
  auth: {
    cookie: {
      secure: process.env.NODE_ENV === "production",  // localhost时为false
    },
  },
}

角色未找到

问题: 创建用户时Role "admin" not found

修复: 在引用前定义角色:

{
  auth: {
    roles: {
      admin: { implicit_allow: true },  // 先定义
      user: { implicit_allow: false },
    },
    default_role_register: "user",  // 现在可引用
  },
}

注册已禁用

问题: Registration not allowed 错误

修复: 启用注册:

{
  auth: {
    allow_register: true,  // 默认为true,但检查是否显式禁用
  },
}

弱密码哈希

问题: 生产中使用plainsha256

修复: 生产中使用bcrypt:

{
  auth: {
    strategies: {
      password: {
        config: {
          hashing: "bcrypt",
          rounds: 4,  // 平衡安全性和性能
        },
      },
    },
  },
}

验证

设置后,验证认证是否工作:

1. 检查启用的策略:

curl http://localhost:7654/api/auth/strategies

2. 注册测试用户:

curl -X POST http://localhost:7654/api/auth/password/register \
  -H "Content-Type: application/json" \
  -d '{"email": "test@example.com", "password": "password123"}'

3. 登录:

curl -X POST http://localhost:7654/api/auth/password/login \
  -H "Content-Type: application/json" \
  -d '{"email": "test@example.com", "password": "password123"}'

4. 检查当前用户(带令牌):

curl http://localhost:7654/api/auth/me \
  -H "Authorization: Bearer <来自登录的令牌>"

安全检查清单

部署到生产前:

  • [ ] 设置强jwt.secret(至少256位)
  • [ ] 使用hashing: "bcrypt"作为密码策略
  • [ ] 设置cookie.secure: true(仅HTTPS)
  • [ ] 设置cookie.httpOnly: true(默认)
  • [ ] 设置cookie.sameSite: "lax""strict"
  • [ ] 配置jwt.expires(不要留为无限)
  • [ ] 审查allow_register设置
  • [ ] 通过种子创建管理员用户(非公共注册)
  • [ ] 将密钥存储在环境变量中

该做与不该做

该做:

  • 使用环境变量存储密钥
  • 生产中使用bcrypt哈希
  • 设置JWT过期时间
  • 在分配前定义角色
  • 配置更改后测试认证流程

不该做:

  • 在代码中硬编码JWT密钥
  • 生产中使用plain哈希
  • 生产中跳过设置cookie.secure
  • 如果不需要,保持注册开放
  • 忘记创建初始管理员用户

相关技能

  • bknd创建用户 - 以编程方式创建用户账户
  • bknd登录流程 - 实现登录/登出功能
  • bknd注册 - 设置用户注册流程
  • bkndOAuth设置 - 配置OAuth提供商(Google、GitHub)
  • bknd创建角色 - 定义授权角色
  • bknd会话处理 - 管理用户会话