管理员

Admin 插件为您的应用程序提供了一套用户管理相关的管理功能。它允许管理员执行各种操作,例如创建用户、管理用户角色、封禁/解封用户、模拟用户登录等。

安装

将插件添加到您的 auth 配置中

要使用 Admin 插件,请将其添加到您的 auth 配置中。

auth.ts
import { betterAuth } from "better-auth"
import { admin } from "better-auth/plugins"

export const auth = betterAuth({
    // ... 其他配置选项
    plugins: [
        admin() 
    ]
})

迁移数据库

运行迁移或生成模式(schema),以向数据库添加必要的字段和表。

npx @better-auth/cli migrate
npx @better-auth/cli generate

请参阅 Schema 部分以手动添加字段。

添加客户端插件

接下来,在您的认证客户端实例中包含 admin 客户端插件。

auth-client.ts
import { createAuthClient } from "better-auth/client"
import { adminClient } from "better-auth/client/plugins"

export const authClient = createAuthClient({
    plugins: [
        adminClient()
    ]
})

使用方法

在执行任何管理员操作之前,用户必须使用管理员帐户进行身份验证。管理员是指被分配了 admin 角色的任何用户,或其 ID 包含在 adminUserIds 选项中的任何用户。

创建用户

允许管理员创建新用户。

POST
/admin/create-user
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" },});
属性描述类型
email
用户的邮箱地址。
string
password
用户的密码。
string
name
用户的姓名。
string
role?
一个字符串或字符串数组,表示要应用到新用户的角色。
string | string[]
data?
用户的额外字段,包括自定义的附加字段。
Record<string, any>

列出用户

允许管理员列出数据库中的所有用户。

GET
/admin/list-users
备注

所有属性都是可选配置。默认返回100行数据,可通过 limit 属性进行配置。

const { data: users, error } = await authClient.admin.listUsers({    searchValue: "some name",    searchField: "name",    searchOperator: "contains",    limit: 100,    offset: 100,    sortBy: "name",    sortDirection: "desc",    filterField: "email",    filterValue: "hello@example.com",    filterOperator: "eq",});
属性描述类型
searchValue?
要搜索的值。
string
searchField?
要搜索的字段,默认为 email。可以是 emailname
"email" | "name"
searchOperator?
搜索使用的运算符。可以是 contains(包含)、starts_with(开头为)或 ends_with(结尾为)。
"contains" | "starts_with" | "ends_with"
limit?
要返回的用户数量。默认为100。
string | number
offset?
起始偏移量。
string | number
sortBy?
排序依据的字段。
string
sortDirection?
排序方向。
"asc" | "desc"
filterField?
筛选依据的字段。
string
filterValue?
筛选依据的值。
string | number | boolean
filterOperator?
筛选使用的运算符。
"eq" | "ne" | "lt" | "lte" | "gt" | "gte"

查询筛选

listUsers 函数支持多种筛选运算符,包括 eq(等于)、contains(包含)、starts_with(开头为)和 ends_with(结尾为)。

分页

listUsers 函数支持分页功能,返回用户列表的同时还会返回元数据。响应包含以下字段:

{
  users: User[],   // 返回的用户数组
  total: number,   // 应用过滤器和搜索查询后的用户总数
  limit: number | undefined,   // 查询中提供的限制数量
  offset: number | undefined   // 查询中提供的偏移量
}
如何实现分页

要对结果进行分页,请使用 totallimitoffset 值来计算:

  • 总页数: Math.ceil(total / limit)
  • 当前页: (offset / limit) + 1
  • 下一页偏移量: Math.min(offset + limit, (total - 1)) – 用作下一页的 offset 值,确保不超过总页数。
  • 上一页偏移量: Math.max(0, offset - limit) – 用作上一页的 offset 值(确保不低于零)。
使用示例

获取第二页,每页显示 10 个用户:

admin.ts
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)

设置用户角色

更改用户的角色。

POST
/admin/set-role
const { data, error } = await authClient.admin.setRole({    userId: "user-id",    role: "admin", // required});
属性描述类型
userId?
要设置角色的用户 ID。
string
role
要设置的角色,可以是字符串或字符串数组。
string | string[]

设置用户密码

修改用户的密码。

POST
/admin/set-user-password
const { data, error } = await authClient.admin.setUserPassword({    newPassword: 'new-password', // required    userId: 'user-id', // required});
属性描述类型
newPassword
新密码。
string
userId
要为其设置密码的用户 ID。
string

封禁用户

封禁用户,阻止其登录并撤销其所有现有会话。

POST
/admin/ban-user
await authClient.admin.banUser({    userId: "user-id", // required    banReason: "Spamming",    banExpiresIn: 60 * 60 * 24 * 7,});
属性描述类型
userId
要封禁的用户 ID。
string
banReason?
封禁原因。
string
banExpiresIn?
封禁到期的秒数。如果未提供,则封禁将永久有效。
number

解除用户封禁

解除对用户的封禁,允许其再次登录。

POST
/admin/unban-user
await authClient.admin.unbanUser({    userId: "user-id", // required});
属性描述类型
userId
要解除封禁的用户 ID。
string

列出用户会话

列出用户的所有会话。

POST
/admin/list-user-sessions
const { data, error } = await authClient.admin.listUserSessions({    userId: "user-id", // required});
属性描述类型
userId
用户 ID。
string

撤销用户会话

撤销用户的特定会话。

POST
/admin/revoke-user-session
const { data, error } = await authClient.admin.revokeUserSession({    sessionToken: "session_token_here", // required});
属性描述类型
sessionToken
要撤销的会话令牌。
string

撤销用户所有会话

撤销指定用户的所有会话。

POST
/admin/revoke-user-sessions
const { data, error } = await authClient.admin.revokeUserSessions({    userId: "user-id", // required});
属性描述类型
userId
需要撤销所有会话的用户 ID
string

用户模拟

此功能允许管理员创建一个模拟指定用户的会话。该会话将保持活动状态,直到浏览器会话结束或达到 1 小时。您可以通过设置 impersonationSessionDuration 选项来更改此持续时间。

POST
/admin/impersonate-user
const { data, error } = await authClient.admin.impersonateUser({    userId: "user-id", // required});
属性描述类型
userId
需要模拟的用户 ID
string

停止用户模拟

要停止模拟用户并继续使用管理员账户,您可以使用 stopImpersonating

POST
/admin/stop-impersonating
await authClient.admin.stopImpersonating();

删除用户

从数据库中硬删除用户。

POST
/admin/remove-user
const { data: deletedUser, error } = await authClient.admin.removeUser({    userId: "user-id", // required});
属性描述类型
userId
需要删除的用户 ID
string

访问控制

管理员插件提供了一个高度灵活的访问控制系统,允许您根据用户角色管理权限。您可以定义自定义权限集以满足您的需求。

角色

默认情况下,有两种角色:

admin:具有管理员角色的用户对其他用户拥有完全控制权。

user:具有用户角色的用户对其他用户没有控制权。

一个用户可以拥有多个角色。多个角色以逗号(",")分隔的字符串形式存储。

权限

默认情况下,有两个资源,最多有六个权限。

用户管理: 创建 列表 设置角色 封禁 模拟登录 删除 设置密码

会话管理: 列表 撤销 删除

拥有管理员角色的用户对所有资源和操作拥有完全控制权。拥有用户角色的用户则无法执行上述任何操作。

自定义权限

该插件提供了一种简单的方法来为每个角色定义您自己的权限集。

创建访问控制

您首先需要通过调用 createAccessControl 函数并传递 statement 对象来创建访问控制器。statement 对象应以资源名称为键,以操作数组为值。

permissions.ts
import { createAccessControl } from "better-auth/plugins/access";

/**
 * 确保使用 `as const` 以便 TypeScript 能正确推断类型
 */
const statement = { 
    project: ["create", "share", "update", "delete"], 
} as const; 

const ac = createAccessControl(statement); 

创建角色

创建访问控制器后,您可以使用已定义的权限创建角色。

permissions.ts
import { createAccessControl } from "better-auth/plugins/access";

export const statement = {
    project: ["create", "share", "update", "delete"], // <-- 为创建角色可用的权限
} as const;

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 合并,同时将角色的权限集与默认角色合并。

permissions.ts
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, 
});

将角色传递给插件

创建角色后,您可以将它们传递给客户端和服务器上的 admin 插件。

auth.ts
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
            }
        }),
    ],
});

您还需要将访问控制器和角色传递给客户端插件。

auth-client.ts
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 函数。

POST
/admin/has-permission
const { data, error } = await authClient.admin.hasPermission({    userId: "user-id",    permission: { "project": ["create", "update"] } /* 必须使用此参数或permissions参数 */,    permissions,});
属性描述类型
userId?
需要检查权限的用户ID
string
permission?
可选检查单个权限是否被授予。必须使用此参数或permissions参数
Record<string, string[]>
permissions?
可选检查多个权限是否被授予。必须使用此参数或permission参数
Record<string, string[]>

使用示例:

auth-client.ts
const canCreateProject = await authClient.admin.hasPermission({
  permissions: {
    project: ["create"],
  },
});

// 您也可以同时检查多个资源权限
const canCreateProjectAndCreateSale = await authClient.admin.hasPermission({
  permissions: {
    project: ["create"],
    sale: ["create"]
  },
});

如果您想在服务器端检查用户权限,可以使用 api 提供的 userHasPermission 操作来检查用户权限。

api.ts
import { auth } from "@/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

auth-client.ts
const canCreateProject = authClient.admin.checkRolePermission({
  permissions: {
    user: ["delete"],
  },
  role: "admin",
});

// 您也可以同时检查多个资源权限
const canDeleteUserAndRevokeSession = authClient.admin.checkRolePermission({
  permissions: {
    user: ["delete"],
    session: ["revoke"]
  },
  role: "admin",
});

数据表结构

此插件向 user 表添加了以下字段:

字段名称类型Key描述
rolestring用户的角色。默认为 `user`。管理员将拥有 `admin` 角色。
bannedboolean指示用户是否被禁用。
banReasonstring用户被禁用的原因。
banExpiresdate用户禁用将到期的日期。

并在 session 表中添加了一个字段:

字段名称类型Key描述
impersonatedBystring正在模拟此会话的管理员 ID。

选项

默认角色

用户的默认角色。默认为 user

auth.ts
admin({
  defaultRole: "regular",
});

管理员角色

被视为管理员角色的列表。默认为 ["admin"]

auth.ts
admin({
  adminRoles: ["admin", "superadmin"],
});

任何不在 adminRoles 列表中的角色,即使拥有权限,也不会被视为管理员。

管理员用户 ID

您可以传递一个应被视为管理员的用户 ID 数组。默认为 []

auth.ts
admin({
    adminUserIds: ["user_id_1", "user_id_2"]
})

如果用户在 adminUserIds 列表中,他们将能够执行任何管理员操作。

模拟会话持续时间

模拟会话的持续时间(以秒为单位)。默认为 1 小时。

auth.ts
admin({
  impersonationSessionDuration: 60 * 60 * 24, // 1 天
});

默认封禁原因

由管理员创建的用户默认封禁原因。默认为 No reason(无原因)。

auth.ts
admin({
  defaultBanReason: "Spamming",
});

默认封禁过期时间

由管理员创建的用户默认封禁过期时间(以秒为单位)。默认为 undefined(表示封禁永不过期)。

auth.ts
admin({
  defaultBanExpiresIn: 60 * 60 * 24, // 1 天
});

被封禁用户消息

当被封禁用户尝试登录时显示的消息。默认为 "You have been banned from this application. Please contact support if you believe this is an error."(您已被禁止使用此应用程序。如果您认为这是一个错误,请联系支持。)

auth.ts
admin({
  bannedUserMessage: "Custom banned user message",
});