名称: bknd-create-user 描述: 在Bknd中编程创建新用户账户时使用。涵盖种子函数中的auth.createUser()、通过SDK/REST API注册、通过数据API创建用户、管理员面板用户创建和角色分配。
创建用户
通过种子函数、SDK、REST API或管理员面板在Bknd中创建新用户账户。
先决条件
- Bknd项目运行中(本地或部署)
- 认证模块启用(
auth.enabled: true) - 密码策略配置(默认启用)
- 对于角色分配:在认证配置中定义角色
何时使用UI模式
- 在开发过程中创建管理员/测试用户
- 非技术管理员手动管理用户
- 一次性用户创建
UI步骤: 管理员面板 > 认证 > 用户 > 点击“+” > 填写邮箱/密码 > 选择角色 > 保存
何时使用代码模式
- 首次部署时种子初始管理员用户
- 在服务器代码中编程创建用户
- 前端用户注册流程
- 自动化用户供应
代码方法
方法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,
jwt: { secret: process.env.JWT_SECRET || "dev-secret" },
roles: {
admin: { implicit_allow: true },
user: { implicit_allow: false },
},
},
},
options: {
seed: async (ctx) => {
// 在首次运行时创建管理员用户
await ctx.app.module.auth.createUser({
email: "admin@example.com",
password: "securepassword123",
role: "admin",
});
console.log("管理员用户已创建");
},
},
});
种子函数注意点:
- 仅在数据库为空时首次启动运行
- 拥有对
ctx.app.module.auth的完全访问权限 - 适合创建初始管理员账户
方法2:服务器端createUser()
在服务器代码中编程创建用户(插件、流程、自定义端点):
import { getApi } from "bknd";
// 在插件、流程或自定义端点处理程序中
async function createAdminUser(app) {
const user = await app.module.auth.createUser({
email: "newadmin@example.com",
password: "securepassword123",
role: "admin",
});
console.log("创建的用户:", user.id, user.email);
return user;
}
// 包含额外字段(如果用户实体有自定义字段)
async function createUserWithProfile(app) {
const user = await app.module.auth.createUser({
email: "user@example.com",
password: "password123",
role: "user",
name: "John Doe", // 自定义字段
avatar: "https://...", // 自定义字段
});
return user;
}
createUser() 签名:
type CreateUserPayload = {
email: string; // 必需:用户邮箱
password: string; // 必需:明文密码(将被哈希)
role?: string; // 可选:必须在auth.roles中存在
[key: string]: any; // 用户实体的额外字段
};
// 返回创建的用户记录
async createUser(payload: CreateUserPayload): Promise<User>
方法3:SDK注册(客户端)
通过前端用户自注册:
import { Api } from "bknd";
const api = new Api({
host: "http://localhost:7654",
storage: localStorage, // 用于令牌持久化
});
// 注册新用户
const { ok, data, error } = await api.auth.register("password", {
email: "newuser@example.com",
password: "securepassword123",
});
if (ok) {
console.log("已注册:", data.user);
console.log("令牌:", data.token);
// 用户现已登录,令牌存储在localStorage
} else {
console.error("注册失败:", error);
}
注册注意点:
- 需要
auth.allow_register: true(默认) - 自动分配
auth.default_role_register角色 - 返回JWT令牌(注册后用户登录)
- 仅接受邮箱/密码;额外字段需要单独更新
方法4:REST API注册
# 通过REST API注册
curl -X POST http://localhost:7654/api/auth/password/register \
-H "Content-Type: application/json" \
-d '{"email": "user@example.com", "password": "securepassword123"}'
响应:
{
"user": {
"id": 1,
"email": "user@example.com",
"role": "user"
},
"token": "eyJhbGciOiJIUzI1NiIs..."
}
方法5:数据API(管理员创建用户)
管理员可通过数据API直接创建用户(需要认证+管理员角色):
// 作为认证管理员
const { ok, data } = await api.data.createOne("users", {
email: "managed@example.com",
strategy: "password",
strategy_value: "HASHED_PASSWORD", // 必须预哈希!
role: "user",
});
警告: 数据API需要预哈希密码。使用createUser()或注册以进行正确的密码处理。
React集成
注册表单
import { useApp } from "bknd/react";
import { useState } from "react";
function RegisterForm() {
const { api } = useApp();
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
const [error, setError] = useState<string | null>(null);
const [loading, setLoading] = useState(false);
async function handleSubmit(e: React.FormEvent) {
e.preventDefault();
setLoading(true);
setError(null);
const { ok, data, error: apiError } = await api.auth.register("password", {
email,
password,
});
setLoading(false);
if (ok) {
console.log("已注册:", data.user);
// 重定向到仪表板或显示成功
} else {
setError(apiError?.message || "注册失败");
}
}
return (
<form onSubmit={handleSubmit}>
<input
type="email"
value={email}
onChange={(e) => setEmail(e.target.value)}
placeholder="邮箱"
required
/>
<input
type="password"
value={password}
onChange={(e) => setPassword(e.target.value)}
placeholder="密码"
minLength={8}
required
/>
<button type="submit" disabled={loading}>
{loading ? "创建账户中..." : "注册"}
</button>
{error && <p className="error">{error}</p>}
</form>
);
}
使用useAuth钩子
import { useAuth } from "@bknd/react";
function AuthStatus() {
const { user, isLoading, register, logout } = useAuth();
if (isLoading) return <div>加载中...</div>;
if (!user) {
return (
<button onClick={() => register("password", {
email: "new@example.com",
password: "password123"
})}>
创建账户
</button>
);
}
return (
<div>
<p>欢迎, {user.email}</p>
<button onClick={logout}>登出</button>
</div>
);
}
配置选项
启用/禁用注册
{
auth: {
enabled: true,
allow_register: true, // 设置为false以禁用自注册
default_role_register: "user", // 注册时分配的角色
},
}
密码要求
{
auth: {
strategies: {
password: {
type: "password",
enabled: true,
config: {
hashing: "bcrypt", // "plain" | "sha256" | "bcrypt"
rounds: 4, // bcrypt轮数(1-10)
minLength: 8, // 最小密码长度
},
},
},
},
}
定义角色用于分配
{
auth: {
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,
permissions: [
{ permission: "data.posts.read", effect: "allow" },
],
},
},
default_role_register: "user", // 新注册获得此角色
},
}
扩展用户实体
向用户添加自定义字段:
import { em, entity, text, date } from "bknd";
const schema = em({
users: entity("users", {
email: text().required().unique(),
name: text(),
avatar: text(),
bio: text(),
created_at: date({ default_value: "now" }),
}),
});
// 在配置中
{
config: {
data: schema.toJSON(),
auth: { enabled: true, /* ... */ },
},
}
注意: strategy和strategy_value字段由认证系统管理 - 不要直接修改。
常见问题
注册被禁用
问题: 注册不被允许错误
修复: 启用注册:
{ auth: { allow_register: true } }
角色未找到
问题: 角色"admin"未找到错误
修复: 在分配前在配置中定义角色:
{
auth: {
roles: {
admin: { implicit_allow: true },
},
},
}
用户已存在
问题: 用户已存在或唯一约束失败
修复: 创建前检查或处理错误:
// SDK注册自动处理此问题
const { ok, error } = await api.auth.register("password", { email, password });
if (!ok && error?.message?.includes("exists")) {
console.log("邮箱已注册");
}
// 服务器端:先检查
const { data: exists } = await api.data.exists("users", {
email: { $eq: email },
});
if (!exists.exists) {
await app.module.auth.createUser({ email, password });
}
弱JWT密钥
问题: 没有密钥无法签名JWT或安全警告
修复: 设置强JWT密钥:
{
auth: {
jwt: {
secret: process.env.JWT_SECRET, // 使用环境变量,至少256位
},
},
}
密码未哈希(数据API)
问题: 通过数据API创建后用户无法登录
原因: 数据API不自动哈希密码
修复: 使用createUser()或注册代替:
// 错误 - 密码未哈希
await api.data.createOne("users", { email, password: "plain" });
// 正确 - 使用认证模块
await app.module.auth.createUser({ email, password: "plain" });
额外字段未保存
问题: 注册后自定义字段未包含
原因: 注册仅接受邮箱/密码
修复: 注册后更新用户:
const { data } = await api.auth.register("password", { email, password });
// 更新额外字段
await api.data.updateOne("users", data.user.id, {
name: "John Doe",
avatar: "https://...",
});
验证
创建用户后,验证:
// SDK - 注册后检查当前用户
const { data } = await api.auth.me();
console.log("当前用户:", data?.user);
// 服务器端 - 读取
const { data: user } = await api.data.readOneBy("users", {
where: { email: { $eq: "user@example.com" } },
});
console.log("创建的用户:", user);
或通过管理员面板:管理员面板 > 认证 > 用户 > 在列表中查找新用户。
注意事项
应做:
- 使用
createUser()或注册以进行正确的密码哈希 - 使用种子函数用于初始管理员用户
- 在生产环境中设置强JWT密钥
- 在分配前定义角色
- 在生产环境中使用bcrypt哈希
不应做:
- 通过数据API创建用户(密码不会被正确哈希)
- 存储明文密码
- 在生产环境中使用弱JWT密钥
- 分配不存在的角色
- 直接修改
strategy或strategy_value字段
相关技能
- bknd-setup-auth - 初始化认证系统
- bknd-login-flow - 实现登录/登出功能
- bknd-registration - 设置用户注册流程
- bknd-create-role - 定义用于用户分配的角色
- bknd-assign-permissions - 配置角色权限