身份验证 Authentication
本教程共 56 篇 · 第 53 篇 · 更新于 2026-08-07 · 约 13 分钟阅读
本节目标:理解 Astro 本身不提供登录注册功能,但能用「按需渲染 + 中间件 + cookie/会话 + 适配器」这套通用组合,搭出一套安全可靠的认证流程。
先说一个容易误会的点:Astro 没有内置的用户系统,也没有什么官方认证库。你想做「登录才能看」的功能,得自己把几块基础能力拼起来。好消息是,Astro 提供的按需渲染、中间件、会话等能力,刚好能拼出一条标准、通用的认证链路。这一章不绑定任何第三方库,只讲底层思路。
认证与授权是两件事
文档里把 Authentication(认证)和 Authorization(授权)分开讲,这两个词中文都常被翻译成「鉴权」,但含义不同。
认证解决「你是谁」:用户提交账号密码或走第三方登录,系统确认这人确实是他本人。授权解决「你能干什么」:确认身份之后,系统决定你能不能进后台、能不能删文章。
本章主要讲认证这一层——确认身份。授权通常是在确认身份之后,再比对权限规则,本质上是一回事的延续,思路可以互通。
为什么 Astro 需要按需渲染
认证的核心动作——校验密码、查数据库、签发登录态——全都是服务端才能安全做的事。Astro 默认是静态站,页面在构建时就生成好了,没有运行时服务端去判断「这个人登录了没」。
所以做认证,前提是把相关页面或接口改成按需渲染(第 36 章)。在 astro.config 里设 output: 'server' 或 'hybrid',那些需要鉴权的页面就用 export const prerender = false 关掉预渲染。这样每次请求都会真的跑到服务端代码,才能去读 cookie、查库、判断身份。
// astro.config.mjs
import { defineConfig } from "astro/config";
import node from "@astrojs/node";
export default defineConfig({
output: "server", // 开启按需渲染
adapter: node({ mode: "standalone" }),
});
通用认证流程
不管你用哪家认证库,思路都逃不出下面这条主线:
- 访客在登录页填用户名密码(或点第三方登录按钮)。
- 凭据提交到服务端的一个接口或端点。
- 服务端校验凭据:比对数据库里的密码哈希,或向 OAuth 服务商换 token。
- 校验通过后,服务端在响应里种下一个 cookie 或会话标识,标记「此人已登录」。
- 之后每次请求,中间件读取这个标识,判断要不要放行;没登录却想进受保护页面,就重定向到登录页。
这个流程里,第 1 步的表单在浏览器里跑,第 2~5 步全在服务端跑。记住这条线,你就不会把密码逻辑不小心写到前端去。
登录页提交凭据
登录页是个普通 Astro 页面,里面放一个表单。关键点:表单的 action 指向你服务端的一个端点(比如 /api/login),并且提交走 POST。Astro 的端点(第 34 章)就是处理这类请求的 API 路由,写在 src/pages/api/ 下的 .ts 文件里。
---
// src/pages/login.astro
// 这是静态展示用的登录页,不需要服务端逻辑
---
<form method="POST" action="/api/login">
<input type="text" name="username" />
<input type="password" name="password" />
<button type="submit">登录</button>
</form>
这里没有任何密码比对的代码——比对只发生在服务端端点里,前端只是把表单发过去。
服务端校验并签发登录态
端点接收 POST 请求,取出用户名密码,去数据库比对(真实项目里密码要存哈希,绝不能明文)。比对成功后,把登录态写进 cookie 或会话。
// src/pages/api/login.ts
import type { APIRoute } from "astro";
export const prerender = false; // 必须按需渲染
export const POST: APIRoute = async ({ request, cookies }) => {
const form = await request.formData();
const username = form.get("username");
const password = form.get("password");
// 伪代码:到这里才做真正的密码校验
const user = await verifyUser(username, password);
if (!user) {
return new Response("登录失败", { status: 401 });
}
// 把用户标识写进 cookie(真实项目应签名/加密)
cookies.set("session", user.id, {
httpOnly: true,
path: "/",
sameSite: "lax",
});
return new Response("登录成功", { status: 200 });
};
注意 cookies.set 的几个安全选项:httpOnly 让前端 JS 读不到这个 cookie,降低被 XSS 偷走的风险;sameSite 能挡掉很大一部分跨站伪造请求。具体怎么安全地存登录态,第 54 章的会话会讲得更细。
用中间件保护路由
光有登录态还不够,你还得在每次请求时检查它,拦住没登录的人。这就是中间件(第 37 章)的活。中间件位于请求的最前线,在页面渲染之前就能重定向。
// src/middleware.ts
import { defineMiddleware } from "astro:middleware";
export const onRequest = defineMiddleware(async (context, next) => {
const session = context.cookies.get("session");
// 想进 /dashboard 但没登录态,打回首页
if (context.url.pathname.startsWith("/dashboard") && !session) {
return context.redirect("/login");
}
return next();
});
中间件的判断逻辑很灵活:你可以按路径前缀保护一整片后台,也可以读取会话里的用户角色做更细的授权。重点是——保护逻辑在服务端,前端无论怎么改 URL 都绕不过去。
敏感逻辑必须放服务端
这是认证里最容易踩、也最危险的坑。以下几样东西,绝对不能出现在前端代码(Astro 的 frontmatter 之外的 <script>、或发给浏览器的 JS)里:
- 数据库的连接字符串和密码;
- 密码比对、token 签发这类核心逻辑;
- 任何「谁能进哪个页面」的判断规则。
原因很简单:浏览器里跑的代码,用户用开发者工具就能看到、能改。把校验放前端,等于把锁装在门外面。Astro 的 frontmatter 本身是服务端代码,这点天然安全;但一旦写进 <script> 标签,就跑到客户端了,要特别当心。
别忘了 CSRF 防护
CSRF(跨站请求伪造)指的是:恶意网站偷偷让你的浏览器带着你的登录 cookie,去请求你已登录的站点。比如你登录了 A 站,又访问了恶意 B 站,B 站里藏了一个自动发往 A 站的表单,你的 cookie 就跟着过去了。
Astro 本身不强制你做 CSRF 防护,但有几样常用手段:
- cookie 设
sameSite: "lax"或"strict",多数跨站请求不会被带上 cookie; - 关键写操作(改密码、删数据)要求附带一个服务端签发的一次性 token,恶意站点拿不到这个 token;
- 对敏感接口校验请求来源(Origin / Referer 头)。
这些不是 Astro 独有的知识,但做认证时务必纳入考量,否则登录态形同虚设。
第三方库能省什么
Astro 生态里有 Better Auth、Clerk、Lucia、Scalekit 等认证方案,它们帮你把「签发 token、管理会话、对接 OAuth 服务商」这些繁琐又容易写错的部分封装好了。比如 Better Auth 提供 auth.api.getSession() 在服务器端拿会话,Clerk 提供 <SignedIn>、<SignedOut> 这类组件按登录状态控制显示。
但这些库底层依然是同一套思路:在按需渲染页读会话、在中间件里判断、靠 cookie 或会话保存状态。所以先把本章的通用流程吃透,再去看任一家的文档都不会懵。Astro 官方也提供了 Supabase、Firebase、Scalekit 等后端服务的对接指南,需要时按需查阅即可。
小结
Astro 不内置认证,但给了拼出认证的全部零件:按需渲染让服务端逻辑跑得起来,端点接收登录请求,中间件守住房门,cookie 或会话记住登录态。真正要紧的不是用哪家库,而是守住两条底线——敏感逻辑只在服务端、登录态要防 CSRF。下一章我们就专门看「会话」这块该怎么安全地存。