Bknd生产配置Skill bknd-production-config

这个技能指导如何为Bknd应用程序进行生产部署配置,包括安全硬化、环境变量设置、JWT认证、授权Guard、CORS配置、媒体存储等关键步骤,确保应用在生产环境中安全、高效运行。关键词:Bknd配置、生产部署、安全设置、JWT认证、DevOps、环境配置、云存储、CORS。

DevOps 0 次安装 0 次浏览 更新于 3/8/2026

name: bknd-production-config description: 用于准备Bknd应用程序进行生产部署。涵盖安全硬化、环境配置、isProduction标志、JWT设置、Guard启用、CORS、媒体存储和生产检查清单。

配置生产环境

准备并保护您的Bknd应用程序进行生产部署。

先决条件

  • 本地测试过的Bknd应用程序
  • 数据库已配置(参见bknd-database-provision
  • 选择的托管平台(参见bknd-deploy-hosting

何时使用UI模式

  • 在管理面板中查看当前配置
  • 验证Guard设置是否激活
  • 检查认证配置

何时使用代码模式

  • 所有生产配置更改
  • 设置环境变量
  • 配置安全设置
  • 设置适配器

代码方法

步骤1:启用生产模式

设置isProduction: true以禁用开发功能:

// bknd.config.ts
export default {
  app: (env) => ({
    connection: { url: env.DB_URL },
    isProduction: true,  // 或 env.NODE_ENV === "production"
  }),
};

isProduction: true的作用:

  • 禁用模式自动同步(防止意外迁移)
  • 隐藏API响应中的详细错误消息
  • 禁用管理面板修改(只读)
  • 启用更严格的安全默认值

步骤2:配置JWT认证

关键: 在生产中永远不要使用默认或弱的JWT秘密。

export default {
  app: (env) => ({
    connection: { url: env.DB_URL },
    isProduction: true,
    auth: {
      jwt: {
        secret: env.JWT_SECRET,  // 必需,至少32个字符
        alg: "HS256",            // 或 "HS384"、"HS512"
        expires: "7d",           // 令牌生命周期
        issuer: "my-app",        // 可选,标识令牌来源
        fields: ["id", "email", "role"],  // 令牌中的声明
      },
      cookie: {
        httpOnly: true,          // 防止XSS访问
        secure: true,            // 仅HTTPS
        sameSite: "strict",      // CSRF保护
        expires: 604800,         // 7天,单位秒
      },
    },
  }),
};

生成安全秘密:

# Node.js
node -e "console.log(require('crypto').randomBytes(32).toString('hex'))"

# OpenSSL
openssl rand -hex 32

步骤3:启用Guard(授权)

export default {
  app: (env) => ({
    connection: { url: env.DB_URL },
    isProduction: true,
    config: {
      guard: {
        enabled: true,  // 强制执行所有权限
      },
    },
  }),
};

未启用Guard时,所有认证用户具有完全访问权限。

步骤4:配置CORS

export default {
  app: (env) => ({
    // ...
    config: {
      server: {
        cors: {
          origin: env.ALLOWED_ORIGINS?.split(",") ?? ["https://myapp.com"],
          credentials: true,  // 允许cookie
          methods: ["GET", "POST", "PUT", "PATCH", "DELETE"],
        },
      },
    },
  }),
};

步骤5:配置媒体存储

在无服务器生产中永远不要使用本地存储。 使用云提供商:

// AWS S3
export default {
  app: (env) => ({
    // ...
    config: {
      media: {
        enabled: true,
        body_max_size: 10 * 1024 * 1024,  // 10MB最大上传
        adapter: {
          type: "s3",
          config: {
            bucket: env.S3_BUCKET,
            region: env.S3_REGION,
            accessKeyId: env.S3_ACCESS_KEY,
            secretAccessKey: env.S3_SECRET_KEY,
          },
        },
      },
    },
  }),
};

// Cloudflare R2
config: {
  media: {
    adapter: {
      type: "r2",
      config: { bucket: env.R2_BUCKET },
    },
  },
}

// Cloudinary
config: {
  media: {
    adapter: {
      type: "cloudinary",
      config: {
        cloudName: env.CLOUDINARY_CLOUD,
        apiKey: env.CLOUDINARY_KEY,
        apiSecret: env.CLOUDINARY_SECRET,
      },
    },
  },
}

完整生产配置

// bknd.config.ts
import type { CliBkndConfig } from "bknd";
import { em, entity, text, relation, enumm } from "bknd";

const schema = em(
  {
    users: entity("users", {
      email: text().required().unique(),
      name: text(),
      role: enumm(["admin", "user"]).default("user"),
    }),
    posts: entity("posts", {
      title: text().required(),
      content: text(),
      published: enumm(["draft", "published"]).default("draft"),
    }),
  },
  ({ users, posts }) => ({
    post_author: relation(posts, users),  // posts.author_id -> users
  })
);

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

export default {
  app: (env) => ({
    // 数据库
    connection: {
      url: env.DB_URL,
      authToken: env.DB_TOKEN,
    },

    // 模式
    schema,

    // 生产模式
    isProduction: env.NODE_ENV === "production",

    // 认证
    auth: {
      enabled: true,
      jwt: {
        secret: env.JWT_SECRET,
        alg: "HS256",
        expires: "7d",
        fields: ["id", "email", "role"],
      },
      cookie: {
        httpOnly: true,
        secure: env.NODE_ENV === "production",
        sameSite: "strict",
        expires: 604800,
      },
      strategies: {
        password: {
          enabled: true,
          hashing: "bcrypt",
          rounds: 12,
          minLength: 8,
        },
      },
      allow_register: true,
      default_role_register: "user",
    },

    // 授权
    config: {
      guard: {
        enabled: true,
      },
      roles: {
        admin: {
          implicit_allow: true,  // 完全访问
        },
        user: {
          implicit_allow: false,
          permissions: [
            "data.posts.read",
            {
              permission: "data.posts.create",
              effect: "allow",
            },
            {
              permission: "data.posts.update",
              effect: "filter",
              condition: { author_id: "@user.id" },
            },
            {
              permission: "data.posts.delete",
              effect: "filter",
              condition: { author_id: "@user.id" },
            },
          ],
        },
        anonymous: {
          implicit_allow: false,
          is_default: true,  // 未认证用户
          permissions: [
            {
              permission: "data.posts.read",
              effect: "filter",
              condition: { published: "published" },
            },
          ],
        },
      },

      // 媒体存储
      media: {
        enabled: true,
        body_max_size: 10 * 1024 * 1024,
        adapter: {
          type: "s3",
          config: {
            bucket: env.S3_BUCKET,
            region: env.S3_REGION,
            accessKeyId: env.S3_ACCESS_KEY,
            secretAccessKey: env.S3_SECRET_KEY,
          },
        },
      },

      // CORS
      server: {
        cors: {
          origin: env.ALLOWED_ORIGINS?.split(",") ?? [],
          credentials: true,
        },
      },
    },
  }),
} satisfies CliBkndConfig;

环境变量模板

创建.env.production或在您的平台中设置:

# 必需
NODE_ENV=production
DB_URL=libsql://your-db.turso.io
DB_TOKEN=your-turso-token
JWT_SECRET=your-64-char-random-secret-here-generate-with-openssl

# CORS
ALLOWED_ORIGINS=https://myapp.com,https://www.myapp.com

# 媒体存储(S3)
S3_BUCKET=my-bucket
S3_REGION=us-east-1
S3_ACCESS_KEY=AKIA...
S3_SECRET_KEY=secret...

# 或 Cloudinary
CLOUDINARY_CLOUD=my-cloud
CLOUDINARY_KEY=123456
CLOUDINARY_SECRET=secret

# OAuth(如果使用)
GOOGLE_CLIENT_ID=...
GOOGLE_CLIENT_SECRET=...
GITHUB_CLIENT_ID=...
GITHUB_CLIENT_SECRET=...

安全检查清单

认证

  • [ ] JWT秘密至少32个字符,随机生成
  • [ ] JWT秘密存储在环境变量中,而非代码中
  • [ ] Cookie httpOnly: true 设置
  • [ ] Cookie secure: true 在生产中(HTTPS)
  • [ ] Cookie sameSite: "strict""lax"
  • [ ] 密码哈希使用bcrypt,轮数 >= 10
  • [ ] 强制最小密码长度(8+字符)

授权

  • [ ] Guard启用(guard.enabled: true
  • [ ] 为匿名用户定义默认角色
  • [ ] 管理员角色除非有意,否则不使用 implicit_allow
  • [ ] 敏感实体有明确权限
  • [ ] 行级安全过滤器用户拥有的数据

数据

  • [ ] isProduction: true 设置
  • [ ] 数据库凭证在环境变量中
  • [ ] 生产中无测试/种子数据
  • [ ] 配置数据库备份

媒体

  • [ ] 配置云存储(非本地文件系统)
  • [ ] 存储凭证在环境变量中
  • [ ] 存储桶上配置CORS
  • [ ] 限制最大上传大小(body_max_size

网络

  • [ ] CORS来源明确列出(无通配符 *
  • [ ] 强制HTTPS(通过平台/代理)
  • [ ] 配置API速率限制(如果需要)

平台特定安全

Cloudflare Workers

// 通过wrangler设置秘密
// wrangler secret put JWT_SECRET
// wrangler secret put DB_TOKEN

export default hybrid<CloudflareBkndConfig>({
  app: (env) => ({
    connection: d1Sqlite({ binding: env.DB }),
    isProduction: true,
    auth: {
      jwt: { secret: env.JWT_SECRET },
      cookie: {
        httpOnly: true,
        secure: true,
        sameSite: "strict",
      },
    },
  }),
});

Vercel

# 通过Vercel CLI或仪表板设置
vercel env add JWT_SECRET production
vercel env add DB_URL production
vercel env add DB_TOKEN production

Docker

# docker-compose.yml
services:
  bknd:
    environment:
      - NODE_ENV=production
      - JWT_SECRET=${JWT_SECRET}  # 来自.env或主机
    # 永远不要在docker-compose.yml中直接放置秘密

本地测试生产配置

部署前使用类似生产设置进行测试:

# 创建.env.production.local(git忽略)
NODE_ENV=production
DB_URL=libsql://test-db.turso.io
DB_TOKEN=test-token
JWT_SECRET=test-secret-min-32-characters-here

# 使用生产环境运行
NODE_ENV=production bun run index.ts

# 或源文件
source .env.production.local && bun run index.ts

验证:

  1. 管理面板为只读(无模式更改)
  2. API错误不暴露堆栈跟踪
  3. 认证需要有效JWT
  4. Guard强制执行权限

常见陷阱

“JWT_SECRET required” 错误

问题: 认证在启动时失败

修复: 确保JWT_SECRET已设置且可访问:

# 检查环境已加载
echo $JWT_SECRET

# Cloudflare:设置秘密
wrangler secret put JWT_SECRET

# Docker:传递环境
docker run -e JWT_SECRET="your-secret" ...

Guard未强制执行权限

问题: 用户可以访问所有内容

修复: 确保Guard已启用:

config: {
  guard: {
    enabled: true,  // 必须为true!
  },
}

Cookie未设置(CORS问题)

问题: 认证在Postman中工作,但在浏览器中不工作

修复:

auth: {
  cookie: {
    sameSite: "lax",  // "strict"可能阻止OAuth重定向
    secure: true,
  },
},
config: {
  server: {
    cors: {
      origin: ["https://your-frontend.com"],  // 明确,非"*"
      credentials: true,
    },
  },
}

管理面板允许更改

问题: 模式可以在生产中修改

修复: 设置 isProduction: true

isProduction: true,  // 锁定管理员为只读

暴露详细错误

问题: API返回堆栈跟踪

修复: isProduction: true 隐藏内部错误。同时检查自定义错误处理程序是否暴露细节。


做与不做

做:

  • 在生产中设置 isProduction: true
  • 生成加密安全的JWT秘密(32+字符)
  • 启用Guard进行授权
  • 使用云存储处理媒体
  • 设置明确的CORS来源
  • 对所有秘密使用环境变量
  • 首先本地测试生产配置
  • 启用HTTPS(通过平台/代理)
  • 设置cookie secure: truehttpOnly: true

不做:

  • 使用默认或弱的JWT秘密
  • 将秘密提交到版本控制
  • 使用通配符(*)CORS来源
  • 在生产中保持Guard禁用
  • 在无服务器中使用本地文件系统存储
  • 暴露详细错误消息
  • 跳过安全检查清单
  • 使用 sha256 密码哈希(使用 bcrypt
  • 在非管理员角色上设置 implicit_allow: true

相关技能

  • bknd-deploy-hosting - 部署到托管平台
  • bknd-database-provision - 设置生产数据库
  • bknd-env-config - 环境变量设置
  • bknd-setup-auth - 认证配置
  • bknd-create-role - 定义授权角色
  • bknd-storage-config - 媒体存储设置