名称: bknd-registration 描述: 在 Bknd 应用程序中设置用户注册流程时使用。涵盖注册配置、启用/禁用注册、默认角色、密码验证、注册表单和自定义字段。
用户注册设置
在 Bknd 应用程序中配置和实现用户自注册。
前提条件
- 启用身份验证的 Bknd 项目 (
bknd-setup-auth) - 配置密码策略
- 对于 SDK:安装
bknd包
何时使用 UI 模式
- 通过管理面板测试注册端点
- 查看已注册用户
UI 步骤: 管理面板 > 身份验证 > 测试密码/注册端点
何时使用代码模式
- 在前端构建注册表单
- 配置注册设置
- 添加验证和错误处理
注册配置
启用/禁用注册
import { serve } from "bknd/adapter/bun";
serve({
connection: { url: "file:data.db" },
config: {
auth: {
enabled: true,
allow_register: true, // 启用自注册(默认:true)
default_role_register: "user", // 注册时分配的角色
strategies: {
password: {
type: "password",
config: {
hashing: "bcrypt", // "plain" | "sha256" | "bcrypt"
minLength: 8, // 最小密码长度
},
},
},
roles: {
user: { implicit_allow: false },
},
},
},
});
配置选项:
| 选项 | 类型 | 默认值 | 描述 |
|---|---|---|---|
allow_register |
boolean | true |
启用自注册 |
default_role_register |
string | - | 新用户的角色 |
minLength |
number | 8 | 最小密码长度 |
SDK 注册
import { Api } from "bknd";
const api = new Api({
host: "http://localhost:7654",
storage: localStorage, // 持久化令牌
});
async function register(email: string, password: string) {
const { ok, data, status } = await api.auth.register("password", {
email,
password,
});
if (ok) {
// 令牌自动存储 - 用户已登录
return data.user;
}
if (status === 409) throw new Error("邮箱已注册");
if (status === 400) throw new Error("无效的邮箱或密码");
throw new Error("注册失败");
}
响应:
{
ok: boolean;
data?: {
user: { id: number; email: string; role?: string };
token: string;
};
status: number;
}
REST API 注册
curl -X POST http://localhost:7654/api/auth/password/register \
-H "Content-Type: application/json" \
-d '{"email": "user@example.com", "password": "securepassword123"}'
响应:
| 状态码 | 含义 |
|---|---|
| 201 | 成功 - 返回用户 + 令牌 |
| 400 | 无效邮箱/密码或太短 |
| 403 | 注册已禁用 |
| 409 | 邮箱已注册 |
React 集成
注册表单
import { useState } from "react";
import { useApp } from "bknd/react";
function RegisterForm({ onSuccess }: { onSuccess?: () => void }) {
const { api } = useApp();
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
const [confirmPassword, setConfirmPassword] = useState("");
const [error, setError] = useState<string | null>(null);
const [loading, setLoading] = useState(false);
async function handleSubmit(e: React.FormEvent) {
e.preventDefault();
setError(null);
if (password !== confirmPassword) {
setError("密码不匹配");
return;
}
if (password.length < 8) {
setError("密码必须至少8个字符");
return;
}
setLoading(true);
const { ok, status } = await api.auth.register("password", {
email,
password,
});
setLoading(false);
if (ok) {
onSuccess?.();
} else if (status === 409) {
setError("邮箱已注册");
} else {
setError("注册失败");
}
}
return (
<form onSubmit={handleSubmit}>
{error && <p className="error">{error}</p>}
<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
/>
<input
type="password"
value={confirmPassword}
onChange={(e) => setConfirmPassword(e.target.value)}
placeholder="确认密码"
required
/>
<button disabled={loading}>
{loading ? "创建中..." : "创建账户"}
</button>
</form>
);
}
使用 useAuth 钩子
import { useAuth } from "@bknd/react";
function RegisterPage() {
const { user, isLoading, register } = useAuth();
if (isLoading) return <div>加载中...</div>;
if (user) return <Navigate to="/dashboard" />;
async function handleRegister(email: string, password: string) {
await register("password", { email, password });
}
return <RegisterForm onSuccess={() => navigate("/dashboard")} />;
}
注册后自定义字段
注册仅接受 email 和 password。注册后添加自定义字段:
// 1. 扩展用户实体
const schema = em({
users: entity("users", {
email: text().required().unique(),
name: text(),
avatar: text(),
}),
});
// 2. 注册后更新用户
const { data } = await api.auth.register("password", { email, password });
await api.data.updateOne("users", data.user.id, {
name: "John Doe",
avatar: "https://...",
});
仅邀请应用
禁用公共注册:
{
auth: {
allow_register: false, // 禁用自注册
},
}
// 管理员通过种子或插件创建用户
await app.module.auth.createUser({
email: "invited@example.com",
password: tempPassword,
role: "user",
});
常见陷阱
注册已禁用
问题: 注册不允许 (403)
修复: { auth: { allow_register: true } }
角色未找到
问题: 角色 "user" 未找到
修复: 使用前定义角色:
{
auth: {
roles: { user: { implicit_allow: false } },
default_role_register: "user",
},
}
用户已存在
问题: 409 错误
修复: 优雅处理:
if (status === 409) {
setError("邮箱已注册。尝试登录代替。");
}
令牌未存储
问题: 注册后用户未登录
修复: 提供存储:
const api = new Api({
host: "http://localhost:7654",
storage: localStorage, // 持久化所需
});
自定义字段被忽略
问题: 传递给注册的额外字段未保存
原因: 注册仅接受邮箱/密码
修复: 注册后更新用户(参见自定义字段部分)
验证
# 1. 测试注册
curl -X POST http://localhost:7654/api/auth/password/register \
-H "Content-Type: application/json" \
-d '{"email": "test@example.com", "password": "password123"}'
# 2. 验证令牌有效
curl http://localhost:7654/api/auth/me \
-H "Authorization: Bearer <token>"
应该做和不应该做
应该做:
- 生产中使用 bcrypt 哈希
- 客户端验证密码长度以匹配服务器配置
- 用登录建议处理 409 错误
- 使用
storage: localStorage存储令牌 - 使用
default_role_register前定义角色
不应该做:
- 生产中使用
hashing: "plain" - 期望注册有效载荷中的自定义字段
- 忘记处理注册错误
- 禁用注册而没有替代的用户创建方法
相关技能
- bknd-setup-auth - 配置身份验证系统
- bknd-create-user - 编程式用户创建(管理员/种子)
- bknd-login-flow - 登录/注销功能
- bknd-password-reset - 密码重置流程