名称: 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,但检查是否显式禁用
},
}
弱密码哈希
问题: 生产中使用plain或sha256
修复: 生产中使用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会话处理 - 管理用户会话