上手实践
认证与管理员角色
使用数据库管理只读/读写管理员角色,保护管理 API,并介绍本模板的认证流程。
认证与管理员角色
本模板基于 Better Auth + Drizzle。RBAC 仅存储在数据库中,不支持通过环境变量临时提升权限。
基础:认证 vs 授权
- 认证(Authentication):证明“你是谁”(注册/登录),最终得到一个会话(session)。
- 授权(Authorization):决定“你能做什么”。在本模板中由
users表的role字段控制。
我们使用的角色:
user— 默认角色。admin_ro— 只读管理员(可查看管理数据,不能写)。admin_rw— 读写管理员(含授予积分等管理操作)。
一个简单的类比
在机场安检出示身份证是“认证”(证明身份);登机口出示登机牌是“授权”(验证你是否被允许登机)。
快速对比
| 维度 | 认证 | 授权 |
|---|---|---|
| 目的 | 验证身份(是谁) | 验证权限(能做什么) |
| 时机 | 在授权之前 | 成功认证之后 |
| 常见输入 | 凭证(密码、邮件链接、社交登录) | 策略/角色(如 user、admin_ro、admin_rw) |
| 产物 | 会话/身份 | 被允许的操作与受保护路由 |
| 常见标准(一般情况) | OIDC、ID Token | OAuth 2.0、scopes |
在本模板中:使用服务器端的 HttpOnly 会话 Cookie(不在前端持有 token),并用数据库中的 role 做授权判断。
什么是 Better Auth?
一个面向 TypeScript、轻量的 Next.js 认证库。
- 存储:通过
src/lib/auth.ts使用 Drizzle ORM + Postgres。 - 数据模型:
users、sessions、accounts、verifications(见src/db/schema.ts)。 - 会话:写入 HttpOnly Cookie 并在
sessions表中持久化;服务端路由通过auth.api.getSession({ headers })读取会话。 - 扩展字段:通过
additionalFields将uuid与role暴露给会话,便于服务端检查。
密钥与 URL(入门)
BETTER_AUTH_SECRET:仅服务器可见的、足够随机的密钥,用于签名/校验认证相关的 Cookie/令牌。只生成一次并妥善保管。- 生成:
openssl rand -base64 32 - 轮换提示:修改该值会让所有用户掉线(旧会话不再可验证)。
- 生成:
BETTER_AUTH_URL:认证库使用的服务端基址(通常即站点 URL)。NEXT_PUBLIC_AUTH_BASE_URL:客户端使用的基址(大多数情况下等于站点 URL)。- Cookie 在生产环境下使用
HttpOnly且secure(见src/lib/auth.ts中advanced.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方式增加积分。
配置步骤
- 数据库迁移:
pnpm drizzle-kit migrate --config src/db/config.ts- 使用管理员账号验证以上接口。
修改角色
以数据库为唯一来源:
- 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.ts的socialProviders.google中配置,并从.env读取GOOGLE_CLIENT_ID/GOOGLE_CLIENT_SECRET。 - 在
.env中设置:GOOGLE_CLIENT_ID、GOOGLE_CLIENT_SECRET,并确保BETTER_AUTH_URL与NEXT_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 流程。