管理员
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
session:
list revoke delete
拥有管理员角色的用户对所有资源和操作拥有完全控制权。拥有用户角色的用户对任何操作均无控制权。
自定义权限
该插件提供简便的方式为每个角色定义自己的权限集。
创建访问控制
首先,通过调用 createAccessControl 并传入声明对象来创建访问控制器。声明对象以资源名称为键,动作数组为值。
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 并与您的新声明合并,同时合并角色的权限集合与默认角色。
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
}
})
]
})访问控制用法
拥有权限:
使用客户端提供的 hasPermission 函数检查用户权限。
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"]
},
},
});检查角色权限:
客户端使用 checkRolePermission 函数检查某个 角色 是否拥有指定的 权限。这在定义角色权限后很有用,允许无需请求服务器即可进行权限检查。
注意此函数不会直接检查当前登录用户权限,而是检查指定角色拥有哪些权限。该函数为同步函数,调用时无需 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: "自定义禁止用户提示信息",
});