---
title: "8.2 认证方式与方案选择"
description: "Session vs Token、OAuth、Passkeys、Magic Link——理解认证方式的区别，选择适合你的认证库"
chapter: "第八章"
---

# 8.2 认证方式与方案选择

> **本节目标**：理解主流认证方式的原理和适用场景，选择适合项目的认证库，让 AI 帮你完成配置。

---

## 登录之后，服务器怎么记住我？

小明在 8.0 跑通了用户系统——注册、登录、受保护页面都能用。但他好奇一个问题：每次访问 `/dashboard`，服务器怎么知道"这个请求来自小明，而不是别人"？难道每次都要重新输密码？

这就涉及到**会话管理**——用户登录一次之后，后续请求怎么证明"我还是我"。你每次打开一个新页面、点一个按钮、提交一个表单，浏览器都会向服务器发一个新的 HTTP 请求。HTTP 协议本身是"无状态"的——服务器处理完一个请求就忘了，下一个请求来了它不知道这是同一个人。所以需要一种机制，让服务器在多次请求之间"记住"你是谁。业界有两种主流方案。

**第一种是 Session（会话）。** 你去图书馆借书，第一次去，前台给你办了一张借书卡，卡上只有一个编号。图书馆的电脑里记录了这个编号对应的姓名、借书记录等信息。以后每次去，你出示借书卡，前台查编号就知道你是谁。Session 的工作方式一样：登录成功后，服务器在数据库里创建一条 session 记录（就像图书馆电脑里的那条记录），把 session ID 通过 Cookie 发给浏览器（就像那张借书卡）。之后浏览器每次请求都会自动带上这个 Cookie——你不需要手动做任何事，浏览器帮你处理了。就像你进了一家商场，门口给你贴了个会员贴纸，之后你走到每一层每一家店，店员都能看到这个贴纸——你不需要每次都重新出示会员卡。服务器收到请求后，根据 Cookie 里的 session ID 去数据库查到对应的用户信息，就知道"这是小明"。优点是服务器完全掌控——想让某个用户立刻下线，删掉数据库里的 session 记录就行，下次请求来了查不到记录，用户就被踢出去了。缺点是每次请求都要查数据库，如果你的应用部署在多台服务器上，还需要让它们共享 session 存储（否则用户在 A 服务器登录，请求被分配到 B 服务器时就找不到 session 了）。

**第二种是 Token（令牌，通常是 JWT）。** 换个场景：你去游乐园，入口给你盖了个手背章，章上用隐形墨水写了你的姓名和票种（普通票/VIP）。每个项目入口的工作人员用紫外灯一照就能看到信息，不需要打电话回入口确认。Token 的工作方式类似：登录成功后，服务器把用户信息（用户名、角色、过期时间等）加密签名生成一个 token 字符串，发给浏览器。之后浏览器每次请求带上这个 token，服务器收到后解密验证签名，确认没被篡改，就直接从 token 里读取用户信息——不需要查数据库。优点是不需要服务端存储，天然支持多服务器（因为信息都在 token 里，任何服务器都能验证）。缺点是签发后无法撤销——token 一旦发出去，在过期之前一直有效，就像游乐园的手背章，盖上去就擦不掉，工作人员没法远程让它失效，只能等它自然褪色。即使你想让某个用户立刻下线也做不到（除非维护一个"黑名单"，但那又回到了需要服务端存储的老路）。

对于大多数 Web 应用，你不需要做这个选择——**认证库会帮你处理**。Better Auth 默认使用数据库 Session（兼具安全性和可控性），你不需要手动管理 Cookie 或 Token。了解这两种方式的意义在于：当你看到 AI 生成的代码里出现 `session`、`token`、`JWT`、`Cookie` 这些词时，你知道它们在说什么，不会一头雾水。

<AuthMethodComparison />

## 不只是密码：现代认证方式

密码登录是最基础的方式，但不是唯一的。小明的朋友们试用"个人豆瓣"时，第一个反馈就是："又要注册？我已经记不住多少个密码了。能不能用 GitHub 账号直接登录？"这个需求引出了现代认证的几种主流方式。

<ModernAuthMethods />


**OAuth 2.0** 就是你在各种网站上看到的"用 Google/GitHub 登录"按钮背后的技术。点一下，跳到 Google 的页面确认授权，然后自动回到原网站，已经登录了。原理就像酒店入住：你不需要向酒店证明你的身份证是真的——你出示身份证，酒店去公安系统验证。OAuth 里，Google/GitHub 就是那个"公安系统"，你的网站是"酒店"。用户不把密码告诉你的网站，而是让 Google 替他担保——"这个人确实是 xxx@gmail.com 的主人"。对用户来说，不用记新密码，一键登录；对开发者来说，不用处理密码存储和加密，安全责任转移给了 Google/GitHub。小明给"个人豆瓣"加了 GitHub OAuth 登录后，注册率明显提高了——朋友们不用再填邮箱、想密码、收验证邮件，点一下就进来了。

> 跟 AI 说："在 Better Auth 配置中添加 GitHub OAuth 登录。"你需要先去 GitHub 创建 OAuth App，获取 Client ID 和 Secret，填到 `.env` 里。

**Passkeys** 是无密码认证的未来趋势。你用手机解锁时，按一下指纹或看一眼屏幕就解锁了。Passkeys 把这个体验带到了网站登录——用指纹、面容或硬件密钥代替密码。为什么说它是未来？因为它从根本上消灭了密码带来的所有问题：没有密码就没有密码泄露，没有钓鱼攻击（因为 Passkey 绑定了具体的网站域名，假网站 `g00gle.com` 无法触发 `google.com` 的 Passkey），也不需要用户记住任何东西。Better Auth 内置了 Passkeys 支持，如果你的用户群体主要用现代设备（2022 年之后的手机和电脑基本都支持），值得考虑加上这个选项。

**Magic Link** 是另一种无密码方案：输入邮箱，收到一封邮件，点击链接直接登录。不需要记密码，不需要注册流程。小明后来做了一个内部工具给几个朋友用，用的就是 Magic Link——朋友们可能几周才登录一次，记不住密码很正常。输入邮箱、点链接、进来了，整个过程不超过 30 秒。适合低频使用的工具类产品，缺点是依赖邮箱——如果邮件延迟或进了垃圾箱，用户体验就打折扣了。

还有 **SSO（企业单点登录）**——一个账号登录公司所有系统。你登录了企业邮箱，打开 OA、CRM、项目管理工具都不用再登录。这是企业场景的标配，个人项目一般用不到。

## 为什么不自己写认证

小明在 8.0 跑通了 Better Auth 的用户系统，但他好奇：为什么不自己写？登录不就是"查数据库比对密码"吗？他试着自己实现了一个简单版本：用户注册时把密码存进数据库，登录时查出来比对。能用，而且代码也不复杂，几十行就搞定了。他甚至有点得意——这不挺简单的吗，为什么大家都说不要自己写？

但老师傅看了一眼代码，给他列了一串他没想到的问题：密码是明文存的吗？数据库被攻破，所有用户密码直接泄露。密码哈希用的什么算法？MD5 早就不安全了，bcrypt 的 cost factor 设了多少？Session 存在哪里？Cookie 的 `HttpOnly`、`Secure`、`SameSite` 属性设了吗？这三个属性各守一道门：`HttpOnly` 防止 JavaScript 读取 Cookie（防 XSS 偷 Cookie），`Secure` 确保只在 HTTPS 下传输（防网络窃听），`SameSite` 限制跨站请求携带 Cookie（防 CSRF 冒充你发请求）——少设一个都是安全隐患。密码重置的链接有过期时间吗？Token 是一次性的吗？如果重置链接永不过期，攻击者截获一次就能反复使用。登录失败次数有限制吗？不限制的话，攻击者可以写脚本每秒尝试几千个密码组合，暴力破解只是时间问题。CSRF 防护做了吗？

小明听完就放弃了自己写的念头。这些细节，每一个都是安全隐患，而且互相关联——漏掉一个就可能被攻破。他写的那几十行代码看起来"能用"，但在安全专家眼里漏洞百出。**不要自己写认证逻辑**——这是编程界的铁律。不是因为你写不出来，而是因为安全领域的"魔鬼在细节里"，成熟的认证库已经帮你踩过了所有的坑，经过了无数项目的实战检验。

## 认证库怎么选

主流方案有几个：**Better Auth** 开源免费，数据存在你自己的数据库里，TypeScript 原生支持，和 Drizzle 无缝集成，内置 2FA、Passkeys、组织管理等高级功能，适合需要完整控制的项目。**NextAuth（Auth.js）** 也是开源免费的，但类型支持一般，Drizzle 需要额外适配器，适合只需要简单第三方登录的场景。**Clerk** 提供完整的 UI 组件开箱即用，但不是开源的（有免费额度），数据存在第三方，适合快速上线且不在意成本的项目。**Supabase Auth** 和 Supabase 深度集成，支持 RLS（行级安全），但不支持 Drizzle，适合已经在用 Supabase 的项目。

本教程选 Better Auth 的核心理由是**数据自主**——用户表、会话表都在你自己的 PostgreSQL 中，不依赖第三方服务，不被平台捆绑。而且 Auth.js（NextAuth）团队现已加入 Better Auth，后者被官方推荐为新项目的首选。Auth.js 进入维护模式——仍会收到安全补丁，但不再有重大新功能。**新项目直接用 Better Auth**，现有 Auth.js 项目如果运行稳定，不用急着迁移。

::: tip 加载 Skill 让 AI 更懂 Better Auth
在让 AI 配置认证系统之前，加载 `better-auth-best-practices` Skill。加载后，AI 会自动遵循 Better Auth 的最佳实践——Session 管理策略、插件配置顺序、安全默认值等都会更规范。加了 Skill 的 AI 不太可能犯"忘了配置 CSRF 保护"或"Session 过期时间设太长"这类错误。
:::

配置认证系统时，这样给 AI 下指令：

> "在我的 Next.js 项目中集成 Better Auth，使用 Drizzle ORM 和 PostgreSQL。需要：邮箱密码注册/登录、GitHub OAuth 登录、登出功能、受保护的 /dashboard 页面（未登录跳转到 /login）。"

AI 会帮你完成：安装依赖、配置环境变量、创建数据库表、配置 Middleware 路由保护、生成登录/注册页面。你只需要把 OAuth 密钥填到 `.env` 里。如果后续需要更多功能，比如双因素认证（2FA）或组织管理，Better Auth 的插件系统让这些扩展变得很简单——告诉 AI 你要什么，它会帮你配置对应的插件。

## 开发中的另一种认证：公私钥

除了 Web 登录，你在开发中还会遇到另一种认证方式：**公私钥**。你用 `git push` 推代码到 GitHub 时，GitHub 怎么知道是你？就是通过 SSH 密钥——你的电脑上有私钥（只有你知道），GitHub 上存着对应的公钥，推代码时两把钥匙配对成功就证明了你的身份。还有 GPG 签名，给 Git 提交加一个"防伪标签"，别人看到你的提交上有 `Verified` 标记，就知道这确实是你提交的，不是有人冒充。这些在你配置开发环境时就会用到，如果还没配置，告诉 AI："帮我生成 SSH 密钥并配置到 GitHub。"

---

::: tip 本节核心要点
- Session 存服务端可控性强，Token 不存服务端扩展性好——认证库会帮你选择
- OAuth 让用户用已有账号登录，Passkeys 是无密码的未来趋势
- 不要自己写认证逻辑，用成熟的认证库
- 新项目首选 Better Auth，加载 `better-auth-best-practices` Skill 提升 AI 输出质量
:::

::: info 下一步
认证方式选好了，接下来去 [路由保护与权限控制](./03-route-protection.md)——学会用 Middleware 守住每一个路由，确保未授权的请求进不来。
:::
