管理员
Better Auth 的管理员插件
管理员插件为您的应用程序提供了一套用户管理的管理功能。它允许管理员执行各种操作,如创建用户、管理用户角色、禁止/解禁用户、模拟用户身份等。
安装
在您的 auth 配置中添加插件
使用管理员插件时,将其添加到您的 auth 配置中。
import { betterAuth } from "better-auth"
import { admin } from "better-auth/plugins"
export const auth = betterAuth({
// ... 其他配置选项
plugins: [
admin()
]
})添加客户端插件
接下来,在您的身份验证客户端实例中包含管理员客户端插件。
import { createAuthClient } from "better-auth/client"
import { adminClient } from "better-auth/client/plugins"
export const authClient = createAuthClient({
plugins: [
adminClient()
]
})使用
在执行任何管理员操作之前,用户必须使用管理员帐户进行身份验证。管理员是指分配了 admin 角色的任何用户,或者其 ID 包含在 adminUserIds 选项中的任何用户。
创建用户
允许管理员创建新用户。
const { data: newUser, error } = await authClient.admin.createUser({ email: "user@example.com", // required password: "some-secure-password", // required name: "James Smith", // required role: "user", data: { customField: "customValue" },});emailstringrequired用户的电子邮件。
passwordstringrequired用户的密码。
namestringrequired用户的名称。
rolestring | string[]表示要应用于新用户的角色的字符串或字符串数组。
dataRecord<string, any>用户的额外字段。包括自定义附加字段。
列出用户
允许管理员列出数据库中所有用户。
所有属性均可选配置。默认返回 100 行,可通过 limit 属性进行配置。
const { data: users, error } = await authClient.admin.listUsers({ query: { searchValue: "some name", searchField: "name", searchOperator: "contains", limit: 100, offset: 100, sortBy: "name", sortDirection: "desc", filterField: "email", filterValue: "hello@example.com", filterOperator: "eq", },});searchValuestring要搜索的值。
searchField"email" | "name"要搜索的字段,默认是 email。可以是 email 或 name。
searchOperator"contains" | "starts_with" | "ends_with"搜索要使用的操作符。可以是 contains、starts_with 或 ends_with。
limitstring | number要返回的用户数量。默认是 100。
offsetstring | number起始偏移量。
sortBystring用于排序的字段。
sortDirection"asc" | "desc"排序方向。
filterFieldstring用于过滤的字段。
filterValuestring | number | boolean | string[] | number[]用于过滤的值。
filterOperator"eq" | "ne" | "lt" | "lte" | "gt" | "gte" | "in" | "not_in" | "contains" | "starts_with" | "ends_with"过滤时使用的操作符。
查询过滤
listUsers 支持多种过滤操作符,包括 eq、contains、starts_with 和 ends_with。
分页
listUsers 函数支持分页,响应中会返回用户列表和元数据。返回字段如下:
{
users: User[], // 返回的用户数组
total: number, // 过滤和搜索后用户总数
limit: number | undefined, // 查询中提供的 limit
offset: number | undefined // 查询中提供的 offset
}如何实现分页
使用 total、limit 和 offset 计算:
- 总页数:
Math.ceil(total / limit) - 当前页:
(offset / limit) + 1 - 下一页偏移量:
Math.min(offset + limit, (total - 1))– 下一页要使用的offset值,确保它不超过总页数。 - 上一页偏移量:
Math.max(0, offset - limit)– 上一页要使用的offset值(确保它不会小于零)。
示例用法
获取第二页,每页 10 个用户:
import { authClient } from "@/lib/auth-client";
const pageSize = 10;
const currentPage = 2;
const users = await authClient.admin.listUsers({
query: {
limit: pageSize,
offset: (currentPage - 1) * pageSize
}
});
const totalUsers = users.total;
const totalPages = Math.ceil(totalUsers / pageSize)获取用户
通过 ID 获取用户信息。
const { data, error } = await authClient.admin.getUser({ query: { id: "user-id", // required },});idstringrequired您要获取的用户的 ID。
返回值
成功时,data 包含用户对象。失败时,error 会包含 code、message、status 和 statusText。
type GetUserResponse = {
data: User | null;
error: null | {
message: string;
status: number; // HTTP 状态码
statusText: string;
code: string;
}设置用户角色
更改用户角色。
const { data, error } = await authClient.admin.setRole({ userId: "user-id", role: "admin", // required});userIdstring您要为其设置角色的用户 ID。
rolestring | string[]required要设置的角色,这可以是字符串或字符串数组。
设置用户密码
为用户设置密码。如果用户还没有凭据账户,则会创建一个。
const { data, error } = await authClient.admin.setUserPassword({ newPassword: 'new-password', // required userId: 'user-id', // required});newPasswordstringrequired新密码。
userIdstringrequired您要为其设置密码的用户 ID。
更新用户
更新用户详情。
const { data, error } = await authClient.admin.updateUser({ userId: "user-id", // required data: { name: "John Doe" }, // required});userIdstringrequired您要更新的用户 ID。
dataRecord<string, any>required要更新的数据。
禁止用户
await authClient.admin.banUser({ userId: "user-id", // required banReason: "Spamming", banExpiresIn: 60 * 60 * 24 * 7,});userIdstringrequired您要禁止的用户 ID。
banReasonstring禁止原因。
banExpiresInnumber距离封禁过期还有多少秒。如果未提供,则封禁永不过期。
解禁用户
解除用户禁止状态,允许用户重新登录。
await authClient.admin.unbanUser({ userId: "user-id", // required});userIdstringrequired您要解禁的用户 ID。
列出用户会话
列出用户所有会话。
const { data, error } = await authClient.admin.listUserSessions({ userId: "user-id", // required});userIdstringrequired用户 ID。
撤销用户会话
const { data, error } = await authClient.admin.revokeUserSession({ sessionToken: "session_token_here", // required});sessionTokenstringrequired您要撤销的会话令牌。
撤销用户所有会话
撤销用户的所有会话。
const { data, error } = await authClient.admin.revokeUserSessions({ userId: "user-id", // required});userIdstringrequired您要撤销其所有会话的用户 ID。
模拟用户
此功能允许管理员创建一个模拟指定用户的会话。该会话将在浏览器会话结束或达到 1 小时后失效。您可以通过设置 impersonationSessionDuration 选项更改此时长。
const { data, error } = await authClient.admin.impersonateUser({ userId: "user-id", // required});userIdstringrequired您要模拟的用户 ID。
默认情况下,管理员不能模拟其他管理员用户。若要允许此操作,请为角色授予 impersonate-admins 权限:
const superAdmin = ac.newRole({
...adminAc.statements,
user: ["impersonate-admins", ...adminAc.statements.user],
});旧版选项 allowImpersonatingAdmins 仍受支持,但已弃用,将在未来版本中移除。
停止模拟用户
停止模拟用户,继续使用管理员账号。
await authClient.admin.stopImpersonating();删除用户
从数据库中彻底删除用户。
const { data: deletedUser, error } = await authClient.admin.removeUser({ userId: "user-id", // required});userIdstringrequired您要删除的用户 ID。
访问控制
管理员插件提供了高度灵活的访问控制系统,允许您基于用户角色管理权限。您可以定义自定义权限集以满足需求。
角色
默认情况下,有两个角色:
admin:具有管理员角色的用户对其他用户拥有完全控制权。
user:具有用户角色的用户对其他用户无任何控制权。
一个用户可以拥有多个角色。多个角色以逗号(",")分隔字符串存储。
权限
默认情况下,有两个资源及其对应权限。
user:
create list set-role ban impersonate impersonate-admins delete set-password set-email get update
session:
list revoke delete
拥有管理员角色的用户对所有资源和操作拥有完全控制权。拥有用户角色的用户对任何操作均无控制权。
自定义权限
该插件提供简便的方式为每个角色定义自己的权限集。
创建访问控制
您首先需要通过调用 createAccessControl 函数并传入 statement 对象来创建访问控制器。statement 对象应以资源名称作为键,以操作数组作为值。
import { createAccessControl } from "better-auth/plugins/access";
/**
* 确保使用 `as const`,以便 TypeScript 正确推断类型
*/
const statement = {
project: ["create", "share", "update", "delete"],
} as const;
const ac = createAccessControl(statement); 为减小包体积,请确保从 better-auth/plugins/access 导入,而非 better-auth/plugins。
创建角色
创建完访问控制器后,即可为定义的权限创建角色。
import { createAccessControl } from "better-auth/plugins/access";
export const statement = {
project: ["create", "share", "update", "delete"], // <-- 创建角色可用的权限
} as const;
export const ac = createAccessControl(statement);
export const user = ac.newRole({
project: ["create"],
});
export const admin = ac.newRole({
project: ["create", "update"],
});
export const myCustomRole = ac.newRole({
project: ["create", "update", "delete"],
user: ["ban"],
}); 当您为已有角色创建自定义角色时,这些角色预定义的权限将被覆盖。若要将已有权限添加到自定义角色中,您需要导入 defaultStatements 并将其与新 statement 合并,同时将这些角色的权限集与默认角色合并。
import { createAccessControl } from "better-auth/plugins/access";
import { defaultStatements, adminAc } from "better-auth/plugins/admin/access";
const statement = {
...defaultStatements,
project: ["create", "share", "update", "delete"],
} as const;
const ac = createAccessControl(statement);
const admin = ac.newRole({
project: ["create", "update"],
...adminAc.statements,
});将角色传递给插件
创建角色后,您可以在客户端和服务端都将它们传递给管理员插件。
import { betterAuth } from "better-auth"
import { admin as adminPlugin } from "better-auth/plugins"
import { ac, admin, user } from "@/auth/permissions"
export const auth = betterAuth({
plugins: [
adminPlugin({
ac,
roles: {
admin,
user,
myCustomRole
}
}),
],
});您还需要将访问控制器和角色传递给客户端插件。
import { createAuthClient } from "better-auth/client"
import { adminClient } from "better-auth/client/plugins"
import { ac, admin, user, myCustomRole } from "@/auth/permissions"
export const client = createAuthClient({
plugins: [
adminClient({
ac,
roles: {
admin,
user,
myCustomRole
}
})
]
})访问控制用法
拥有权限:
const { data, error } = await authClient.admin.hasPermission({ userId: "user-id", permission: { "project": ["create", "update"] } /* 必须使用此字段或 permissions */, permissions,});userIdstring您要检查其权限的用户 ID。
permissionRecord<string, string[]>可选地检查是否授予单个权限。必须使用此字段或 permissions。
permissionsRecord<string, string[]>可选地检查是否授予多个权限。必须使用此字段或 permission。
示例用法:
import { authClient } from "@/lib/auth-client";
const canCreateProject = await authClient.admin.hasPermission({
permissions: {
project: ["create"],
},
});
// 也可以同时检查多个资源权限
const canCreateProjectAndCreateSale = await authClient.admin.hasPermission({
permissions: {
project: ["create"],
sale: ["create"]
},
});如果您想在服务端检查用户权限,可以使用 api 提供的 userHasPermission 操作来检查用户权限。
import { auth } from "@/lib/auth"
await auth.api.userHasPermission({
body: {
userId: 'id', // 用户 id
permissions: {
project: ["create"], // 必须匹配访问控制中的结构
},
},
});
// 也可以直接传递角色
await auth.api.userHasPermission({
body: {
role: "admin",
permissions: {
project: ["create"], // 必须匹配访问控制中的结构
},
},
});
// 也可以同时检查多个资源权限
await auth.api.userHasPermission({
body: {
role: "admin",
permissions: {
project: ["create"], // 必须匹配访问控制中的结构
sale: ["create"]
},
},
});检查角色权限:
注意此函数不会直接检查当前登录用户权限,而是检查指定角色拥有哪些权限。该函数为同步函数,调用时无需 await。
import { authClient } from "@/lib/auth-client";
const canCreateProject = authClient.admin.checkRolePermission({
permissions: {
user: ["delete"],
},
role: "admin",
});
// 也可以同时检查多个资源权限
const canDeleteUserAndRevokeSession = authClient.admin.checkRolePermission({
permissions: {
user: ["delete"],
session: ["revoke"]
},
role: "admin",
});架构
该插件为 user 表添加以下字段:
并在 session 表中添加一个字段:
电子邮件枚举保护
如果您使用 电子邮件枚举保护(requireEmailVerification 或 autoSignIn: false),您需要配置 customSyntheticUser,在伪造的注册响应中包含管理员插件字段:
export const auth = betterAuth({
emailAndPassword: {
enabled: true,
requireEmailVerification: true,
customSyntheticUser: ({ coreFields, additionalFields, id }) => ({
...coreFields,
// 管理员插件字段(按架构顺序)
role: "user", // 或您配置的 defaultRole
banned: false,
banReason: null,
banExpires: null,
...additionalFields,
id,
}),
},
plugins: [admin()],
});选项
默认角色
用户的默认角色。默认为 user。
admin({
defaultRole: "regular",
});管理员角色
指定哪些角色被视为管理员角色。默认为 ["admin"]。自定义角色(例如 superadmin)必须在自定义访问控制中定义。
admin({
// 需要在自定义访问控制中的 `roles` 里定义 `superadmin`
adminRoles: ["admin", "superadmin"],
});注意: 使用自定义访问控制(通过 ac 和 roles)时,不需要 adminRoles 选项。当您定义带有特定权限的自定义角色时,这些角色将仅具有您通过访问控制系统授予它们的权限。
警告: 在未使用自定义访问控制时,仅有 admin 和 user 是有效角色。任何不在 adminRoles 列表中的角色都将无法执行管理员操作。
管理员用户 ID
您可以传入一个管理员用户 ID 数组,默认为 []
admin({
adminUserIds: ["user_id_1", "user_id_2"]
})如果用户 ID 在 adminUserIds 列表中,他们将能执行任何管理员操作。
impersonationSessionDuration
模拟会话的持续时长,单位秒。默认为 1 小时。
admin({
impersonationSessionDuration: 60 * 60 * 24, // 1 天
});默认禁止原因
管理员创建用户时的默认禁止原因。默认为 No reason。
admin({
defaultBanReason: "Spamming",
});默认禁止过期时间
管理员创建用户时的默认禁止过期时间,单位秒。默认为 undefined(即禁止永不过期)。
admin({
defaultBanExpiresIn: 60 * 60 * 24, // 1 天
});禁止用户消息
禁止用户尝试登录时显示的消息。默认为 "You have been banned from this application. Please contact support if you believe this is an error."
admin({
bannedUserMessage: "自定义禁止用户提示信息",
});