上手实践

认证与管理员角色

使用数据库管理只读/读写管理员角色,保护管理 API,并介绍本模板的认证流程。

认证与管理员角色

本模板基于 Better Auth + Drizzle。RBAC 仅存储在数据库中,不支持通过环境变量临时提升权限。

基础:认证 vs 授权

  • 认证(Authentication):证明“你是谁”(注册/登录),最终得到一个会话(session)。
  • 授权(Authorization):决定“你能做什么”。在本模板中由 users 表的 role 字段控制。

我们使用的角色:

  • user — 默认角色。
  • admin_ro — 只读管理员(可查看管理数据,不能写)。
  • admin_rw — 读写管理员(含授予积分等管理操作)。

一个简单的类比

在机场安检出示身份证是“认证”(证明身份);登机口出示登机牌是“授权”(验证你是否被允许登机)。

快速对比

维度认证授权
目的验证身份(是谁)验证权限(能做什么)
时机在授权之前成功认证之后
常见输入凭证(密码、邮件链接、社交登录)策略/角色(如 useradmin_roadmin_rw
产物会话/身份被允许的操作与受保护路由
常见标准(一般情况)OIDC、ID TokenOAuth 2.0、scopes

在本模板中:使用服务器端的 HttpOnly 会话 Cookie(不在前端持有 token),并用数据库中的 role 做授权判断。

什么是 Better Auth?

一个面向 TypeScript、轻量的 Next.js 认证库。

  • 存储:通过 src/lib/auth.ts 使用 Drizzle ORM + Postgres。
  • 数据模型:userssessionsaccountsverifications(见 src/db/schema.ts)。
  • 会话:写入 HttpOnly Cookie 并在 sessions 表中持久化;服务端路由通过 auth.api.getSession({ headers }) 读取会话。
  • 扩展字段:通过 additionalFieldsuuidrole 暴露给会话,便于服务端检查。

密钥与 URL(入门)

  • BETTER_AUTH_SECRET:仅服务器可见的、足够随机的密钥,用于签名/校验认证相关的 Cookie/令牌。只生成一次并妥善保管。
    • 生成:openssl rand -base64 32
    • 轮换提示:修改该值会让所有用户掉线(旧会话不再可验证)。
  • BETTER_AUTH_URL:认证库使用的服务端基址(通常即站点 URL)。
  • NEXT_PUBLIC_AUTH_BASE_URL:客户端使用的基址(大多数情况下等于站点 URL)。
  • Cookie 在生产环境下使用 HttpOnlysecure(见 src/lib/auth.tsadvanced.defaultCookieAttributes)。

如果出现“只登录一次就失效”或“重启后会话丢失”的情况,请检查以上环境变量并重启开发服务器。

角色

  • user:默认角色。
  • admin_ro:只读管理员;可查看用户列表/订单列表及用户积分汇总。
  • admin_rw:读写管理员;在只读权限基础上可为用户增加积分(system_add)。

迁移 0001_add_user_role.sql 会为 users 表新增 role 字段,默认 user

授权

  • src/lib/authz.ts 仅从会话/数据库获取角色:
    • requireAdminRead() → 允许 admin_ro/admin_rw
    • requireAdminWrite() → 仅允许 admin_rw

管理 API

  • GET /api/admin/users → 用户列表(分页:page, limit)。
  • GET /api/admin/orders → 已支付订单(分页)。
  • GET /api/admin/users/:uuid/credits → 用户积分汇总。
  • POST /api/admin/credits/grant → 以 system_add 方式增加积分。

配置步骤

  1. 数据库迁移:
pnpm drizzle-kit migrate --config src/db/config.ts
  1. 使用管理员账号验证以上接口。

修改角色

以数据库为唯一来源:

  • SQL:
    update users set role = 'admin_rw' where uuid = '<uuid>';
  • 代码助手:
    • updateUserRole(uuid, 'admin_ro' | 'admin_rw' | 'user') 位于 src/models/user.ts

说明

  • 管理账号建议开启 MFA/SSO,并缩短会话有效期。
  • 管理页面需在服务端做权限校验,未授权则重定向。
  • 角色不通过 .env 控制;请在数据库中进行赋权/回收。

社交登录(Google)

  • 提供商在 src/lib/auth.tssocialProviders.google 中配置,并从 .env 读取 GOOGLE_CLIENT_ID / GOOGLE_CLIENT_SECRET
  • .env 中设置:GOOGLE_CLIENT_IDGOOGLE_CLIENT_SECRET,并确保 BETTER_AUTH_URLNEXT_PUBLIC_AUTH_BASE_URL 正确(开发可用 http://localhost:3000)。
  • 在 Google Cloud Console → Credentials 添加授权回调 URI:
    • http://localhost:3000/api/auth/callback/google(开发)
    • https://你的域名.com/api/auth/callback/google(生产)
  • /[locale]/login 登录页包含“使用 Google 继续”按钮,点击后进入 OAuth 流程。