管理员插件

管理员插件为应用程序中的用户管理提供了一系列管理功能。它允许管理员执行各种操作,如创建用户、管理用户角色、禁止/解禁用户、模拟用户身份等。

安装

将插件添加到认证配置中

要使用管理员插件,请将其添加到你的认证配置中。

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

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

添加客户端插件

接下来,在你的认证客户端实例中包含管理员客户端插件。

auth-client.ts
import { createAuthClient } from "better-auth/client"
import { adminClient } from "better-auth/client/plugins"
 
const authClient = createAuthClient({
    plugins: [
        adminClient()
    ]
})

使用方法

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

创建用户

允许管理员创建新用户。

admin.ts
const newUser = await authClient.admin.createUser({
  name: "Test User",
  email: "[email protected]",
  password: "password123",
  role: "user", // 这也可以是一个数组,用于多个角色(如 ["user", "sale"])
  data: {
    // 用户表上的任何附加字段,包括插件字段和自定义字段
    customField: "customValue",
  },
});

列出用户

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

admin.ts
const users = await authClient.admin.listUsers({
  query: {
    limit: 10,
  },
});

默认情况下,返回100个用户。你可以使用以下查询参数调整限制和偏移:

  • search:应用于用户的搜索查询。它可以是一个具有以下属性的对象:
    • field:要搜索的字段,可以是emailname
    • operator:用于搜索的运算符。可以是containsstarts_withends_with
    • value:要搜索的值。
  • limit:要返回的用户数量。
  • offset:要跳过的用户数量。
  • sortBy:按哪个字段对用户进行排序。
  • sortDirection:对用户进行排序的方向。默认为asc
  • filter:应用于用户的过滤器。它可以是对象数组。
admin.ts
const users = await authClient.admin.listUsers({
    query: {
        searchField: "email",
        searchOperator: "contains",
        searchValue: "@example.com",
        limit: 10,
        offset: 0,
        sortBy: "createdAt",
        sortDirection: "desc"
        filterField: "role",
        filterOperator: "eq",
        filterValue: "admin"
    }
});

分页

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 / limit)

设置用户角色

更改用户的角色。

admin.ts
const updatedUser = await authClient.admin.setRole({
  userId: "user_id_here",
  role: "admin", // 这也可以是一个数组,用于多个角色(如 ["admin", "sale"])
});

禁止用户

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

admin.ts
const bannedUser = await authClient.admin.banUser({
  userId: "user_id_here",
  banReason: "Spamming", // 可选(如果未提供,将使用默认的禁止原因 - 无原因)
  banExpiresIn: 60 * 60 * 24 * 7, // 可选(如果未提供,禁止将永不过期)
});

解禁用户

移除用户的禁止状态,允许他们再次登录。

admin.ts
const unbannedUser = await authClient.admin.unbanUser({
  userId: "user_id_here",
});

列出用户会话

列出用户的所有会话。

admin.ts
const sessions = await authClient.admin.listUserSessions({
  userId: "user_id_here",
});

撤销用户会话

撤销用户的特定会话。

admin.ts
const revokedSession = await authClient.admin.revokeUserSession({
  sessionToken: "session_token_here",
});

撤销用户的所有会话

撤销用户的所有会话。

admin.ts
const revokedSessions = await authClient.admin.revokeUserSessions({
  userId: "user_id_here",
});

模拟用户身份

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

admin.ts
const impersonatedSession = await authClient.admin.impersonateUser({
  userId: "user_id_here",
});

停止模拟用户身份

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

admin.ts
await authClient.admin.stopImpersonating();

删除用户

从数据库中永久删除用户。

admin.ts
const deletedUser = await authClient.admin.removeUser({
  userId: "user_id_here",
});

访问控制

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

角色

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

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

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

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

权限

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

user: create list set-role ban impersonate delete set-password

session: list revoke delete

具有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";
 
const statement = {
    project: ["create", "share", "update", "delete"],
} as const;
 
const ac = createAccessControl(statement);
 
const user = ac.newRole({ 
    project: ["create"], 
}); 
 
const admin = ac.newRole({ 
    project: ["create", "update"], 
}); 
 
const myCustomRole = ac.newRole({ 
    project: ["create", "update", "delete"], 
    user: ["ban"], 
}); 

当你为现有角色创建自定义角色时,这些角色的预定义权限将被覆盖。要将现有权限添加到自定义角色,你需要导入defaultStatements并将其与你的新语句合并,以及将角色的权限集与默认角色合并。

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

将角色传递给插件

一旦你创建了角色,你可以将它们传递给客户端和服务器上的组织插件。

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函数。

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";
auth.api.userHasPermission({
  body: {
    userId: 'id', //用户id
    permissions: {
      project: ["create"], // 这必须与你的访问控制中的结构匹配
    },
  },
});
 
// 你也可以直接传递角色
auth.api.userHasPermission({
  body: {
   role: "admin",
    permissions: {
      project: ["create"], // 这必须与你的访问控制中的结构匹配
    },
  },
});
 
// 你也可以同时检查多个资源权限
auth.api.userHasPermission({
  body: {
   role: "admin",
    permissions: {
      project: ["create"], // 这必须与你的访问控制中的结构匹配
      sale: ["create"]
    },
  },
});

检查角色权限:

一旦你定义了角色和权限,为了避免从服务器检查权限,你可以使用客户端提供的checkRolePermission函数。

auth-client.ts
const canCreateProject = client.admin.checkRolePermission({
  permissions: {
    user: ["delete"],
  },
  role: "admin",
});
 
// 你也可以同时检查多个资源权限
const canCreateProjectAndRevokeSession = client.admin.checkRolePermission({
  permissions: {
    user: ["delete"],
    session: ["revoke"]
  },
  role: "admin",
});

Schema

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

Field NameTypeKeyDescription
rolestring用户的角色。默认为`user`。管理员将具有`admin`角色。
bannedboolean表示用户是否被禁止。
banReasonstring用户被禁止的原因。
banExpiresnumber用户禁止将过期的Unix时间戳。

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

Field NameTypeKeyDescription
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: "自定义被禁止用户消息",
});