Wasp 教程:为全栈应用添加用户名密码认证(Auth)完整实战 📅 发布时间:2026/9/15 18:37:18 👁 浏览次数: Wasp 教程为全栈应用添加用户名密码认证Auth完整实战【免费下载链接】waspThe batteries-included full-stack framework for the AI era. Develop JS/TS web apps (React, Node.js, and Prisma) using declarative code that abstracts away complex full-stack features like auth, background jobs, RPC, email sending, end-to-end type safety, single-command deployment, and more.项目地址: https://gitcode.com/GitHub_Trending/wa/wasp本篇技术指南以 Wasp 官方教程的第 7 步Adding Authentication为核心围绕当前仓库中的 TodoApp 示例 展开讲解如何用 Wasp 的声明式配置为全栈应用一键接入完整的用户名密码认证体系从创建UserEntity、配置app.auth、生成登录/注册页面到用authRequired保护路由、实现按用户隔离的任务数据最后加入登出功能。读完本文你将掌握 Wasp 认证的完整落地流程并理解其背后的数据模型与源码级实现原理。认证Wasp 的一等公民能力大多数现代应用都需要创建用户、登录、管理会话这整套能力。在传统全栈开发中这往往意味着引入额外的认证库、手写会话管理、搭建密码哈希与校验流程而在 Wasp 中认证是框架的一等公民特性first-class auth support你只需要做少量声明式配置Wasp 就会在后台自动生成完整的认证 UI登录、注册表单一个logout()action一个 React HookuseAuth()Queries 和 Actions 中可用的context.user密码哈希存储与基于 Session 的会话管理。以 TodoApp 教程为例接入认证总共需要完成以下清单创建UserEntity在app.auth中启用用户名密码认证添加登录与注册页面设置主页面authRequired以要求登录建立User与Task之间的关联关系修改 Queries 与 Actions使用户只能看到和修改自己的任务添加登出按钮。第一步创建 User EntityWasp 管理认证时会自动在后台创建与认证相关的实体Auth、AuthIdentity、Session等这部分不需要你操心想深入了解可阅读 auth entities 文档。你只需要自己添加一个UserEntity用来记录谁拥有哪些任务// ... model User { id Int id default(autoincrement()) }在仓库的 TodoApp 示例 schema.prisma 中可以看到最终形态User拥有id主键并进一步声明了tasks Task[]反向关系字段Task通过user/userId外键关联回User。第二步在 main.wasp 中开启认证接下来告诉 Wasp 使用全栈认证功能。以 v0.15 教程采用的.waspDSL 语法为例app TodoApp { wasp: { version: ^0.15.0 }, title: TodoApp, auth: { // Tells Wasp which entity to use for storing users. userEntity: User, methods: { // Enable username and password auth. usernameAndPassword: {} }, // Well see how this is used in a bit. onAuthFailedRedirectTo: /login } } // ...这三个配置项各自承担关键职责userEntity: User指定用于存放业务用户的实体Wasp 会把它与内部认证实体Auth、AuthIdentity、Session关联起来methods: { usernameAndPassword: {} }启用用户名 密码认证方式onAuthFailedRedirectTo: /login当认证失败如未登录访问受保护页面时重定向到的路由。值得注意的是当前仓库的主线版本已迁移到新的 TypeScript specTS spec语法TodoApp 的 main.wasp.ts 中对应的声明写法如下二者表达的是同一套认证配置import { action, app, page, query, route } from wasp.sh/spec; export default app({ name: TodoApp, wasp: { version: 0.26.0 }, title: TodoApp, auth: { userEntity: User, methods: { usernameAndPassword: {}, }, onAuthFailedRedirectTo: /login, }, // ... });更新数据库wasp db migrate-dev完成配置后不要忘记同步数据库 schemawasp db migrate-dev这条命令会为 Wasp 内部认证体系创建数据表。从仓库中的认证迁移文件 20240716203310_auth/migration.sql 可以看到Wasp 实际生成了三张核心表Auth用户认证主记录通过userId外键一对一关联到你的User表删除用户时级联删除ON DELETE CASCADEAuthIdentity认证身份表主键为(providerName, providerUserId)providerData字段存放该提供方特有的数据如密码哈希等默认{}支持未来扩展多种登录方式Session会话表包含expiresAt过期时间和指向Auth的userId外键为记住登录状态提供底层支撑。这也是为什么教程末尾提示你可能看到一些额外的 Prisma 模型——它们正是 Wasp 为你自动创建的认证基础设施。第三步添加登录与注册页面表单由 Wasp 自动生成你只需要定义承载表单的页面。先在 Wasp 文件中声明路由和页面// ... route SignupRoute { path: /signup, to: SignupPage } page SignupPage { component: import { SignupPage } from src/SignupPage } route LoginRoute { path: /login, to: LoginPage } page LoginPage { component: import { LoginPage } from src/LoginPage }然后实现被 import 的 React 组件。登录页直接渲染 Wasp 提供的LoginFormimport { Link } from react-router-dom import { LoginForm } from wasp/client/auth export const LoginPage () { return ( div style{{ maxWidth: 400px, margin: 0 auto }} LoginForm / br / span I dont have an account yet (Link to/signupgo to signup/Link). /span /div ) }注册页结构几乎相同只是换成SignupFormimport { Link } from react-router-dom import { SignupForm } from wasp/client/auth export const SignupPage () { return ( div style{{ maxWidth: 400px, margin: 0 auto }} SignupForm / br / span I already have an account (Link to/logingo to login/Link). /span /div ) }这两个页面的完整实现可以在仓库的 LoginPage.jsx 与 SignupPage.jsx 中查看对应 TypeScript 版本见 TodoAppTs 示例。如果你使用 TypeScript还可以受益于 Wasp 类型安全的Link组件和routes对象详见类型安全链接文档。LoginForm与SignupForm内部由 Wasp 生成器模板提供例如在 LoginSignupForm.tsx 中表单通过useAuthContext()获取认证状态、错误消息与加载状态统一驱动登录/注册的交互逻辑。第四步让主页面要求认证我们不希望未登录用户访问主页因为他们在那里无法创建任务。在页面声明中加上authRequired: true即可将其设为私有页面// ... page MainPage { authRequired: true, component: import { MainPage } from src/MainPage }当authRequired为true时未认证用户访问该页面会被重定向到/login这正是app.auth.onAuthFailedRedirectTo配置生效的地方页面的 React 组件会自动收到一个user对象 propexport const MainPage ({ user }) { // Do something with the user // ... }TypeScript 下使用wasp/auth提供的AuthUser类型进行标注import { AuthUser } from wasp/auth export const MainPage ({ user }: { user: AuthUser }) { // Do something with the user // ... }在 TodoApp 的 main.wasp.ts 中这一配置以page(MainPage, { authRequired: true })的形式挂载到根路由/上。现在可以实际测试了访问应用主页/会被重定向到/login去注册页创建一个账号后会自动回到主页并看到 TODO 列表。如果此时用wasp db studio打开数据库会看到User实体与Task实体并存且认证体系相关的表也各就各位。第五步建立 User 与 Task 的一对多关系接下来定义用户与任务的一对多关系关于关系建模可参考 Prisma 官方文档的 Relations 章节// ... model User { id Int id default(autoincrement()) tasks Task[] } model Task { id Int id default(autoincrement()) description String isDone Boolean default(false) user User? relation(fields: [userId], references: [id]) userId Int? }同样修改 Entity 后必须迁移数据库wasp db migrate-dev仓库中的迁移文件 20240716203532_task_users/migration.sql 展示了这次迁移的实际 SQL由于 SQLite 不支持原地修改表结构Prisma 采用重建表策略——创建带userId的新Task表、把旧数据description、id、isDone复制过去、再删除旧表重命名外键约束为ON DELETE SET NULL。需要特别说明这里把user和userId设为可选?是为了让数据库中已有的、尚未分配用户的旧任务得以保留。但这并非推荐做法——它允许任务不属于任何人这种非预期状态。正常情况下应当通过数据迁移妥善处理存量数据哪怕只是全部删除。教程出于简化目的选择了可选字段方案。第六步更新 Queries 与 Actions 校验用户认证只是第一步关键是要让查询和操作只作用于当前登录用户自己的数据。修改getTasks拒绝未认证用户并只返回当前用户的任务import { HttpError } from wasp/server export const getTasks async (args, context) { if (!context.user) { throw new HttpError(401) } return context.entities.Task.findMany({ where: { user: { id: context.user.id } }, orderBy: { id: asc }, }) }相应地修改两个 Action。createTask在创建任务时通过user: { connect: { id: context.user.id } }把任务归属到当前用户updateTask则在where中同时限定任务 id 和用户 id确保只能更新自己的任务import { HttpError } from wasp/server export const createTask async (args, context) { if (!context.user) { throw new HttpError(401) } return context.entities.Task.create({ data: { description: args.description, user: { connect: { id: context.user.id } }, }, }) } export const updateTask async (args, context) { if (!context.user) { throw new HttpError(401) } return context.entities.Task.updateMany({ where: { id: args.id, user: { id: context.user.id } }, data: { isDone: args.isDone }, }) }一个值得注意的细节updateTask从 Prisma 的update改成了updateMany。原因是 Prisma 的update不允许在where中同时组合主键id和关联字段user而updateMany支持任意组合条件从而实现了只能更新属于自己的任务这一安全约束。这些代码与仓库中 queries.js 和 actions.js 的实际实现完全一致TypeScript 版本中还会用wasp/server/operations导出的GetTasks、CreateTask、UpdateTask类型为操作签名做类型约束。做完这些改动每个用户都将拥有一个仅自己可见、仅自己可编辑的任务列表。可以用wasp db studio创建几个用户并分别添加任务验证效果。第七步添加登出按钮最后加上登出功能。从wasp/client/auth导入logout并绑定到按钮点击事件// ... import { logout } from wasp/client/auth //... const MainPage () { // ... return ( div // ... button onClick{logout}Logout/button /div ) }在 TodoApp 的 MainPage.jsx 中登出按钮与任务列表、新建任务表单同处一页logout直接作为onClick处理器传入点击后 Wasp 会自动清除会话并触发onAuthFailedRedirectTo指定的重定向。至此完整的认证系统已经就绪TodoApp 正式成为多用户应用端到端验证认证流程的自动化测试仓库还提供了 Playwright 端到端测试用于验证这套认证流程的真实行为。在 simple.spec.ts 中可以看到完整的认证 业务闭环注册流程访问/signup填写随机生成的用户名密码并提交断言 URL 变为/说明注册成功后自动登录并跳转到受保护主页点击 Logout 后断言回到/login登录失败场景用错误密码登录断言页面出现 Invalid credentials 提示登录成功与任务操作用正确凭据登录回到/新建一个随机任务名并断言其渲染到页面上勾选任务复选框后刷新页面断言复选框仍处于勾选状态证明状态已持久化到数据库。配套的 helpers.ts 揭示了 Wasp 生成的表单细节注册表单标题为 Create a new account、用户名输入框nameusername、密码输入框类型为password登录表单标题为 Log in to your account、提交按钮文本为 Log in。这些断言同时验证了LoginForm/SignupForm生成的 DOM 结构与交互行为是理解认证 UI 的绝佳参考。延伸更多认证方式与后续学习Wasp 的认证体系并不局限于用户名密码。通过调整auth.methods配置你还可以接入 Google 登录、GitHub 登录 以及邮箱认证。底层机制保持一致AuthIdentity表的(providerName, providerUserId)复合主键正是为多认证方式共存而设计的——同一用户可以拥有多个身份来源。想继续深入可以参考Wasp 官方 Starter Templates获得带认证的完整项目起步模板本仓库的 examples 目录 下的其他示例如 waspello看板应用和 ask-the-documents含邮箱登录与社交登录展示认证在生产级示例中的真实用法Web Sockets 文档构建实时多用户应用。回顾整个流程声明一个UserEntity、配置app.auth、写两个页面组件、给页面加authRequired、建立 Entity 关系、在 Queries/Actions 中校验context.user、加上登出按钮——Wasp 负责了其余所有繁重工作包括密码哈希、会话管理、认证 UI 生成与数据库表结构。这正是batteries-included框架在认证这一复杂全栈能力上的价值所在。【免费下载链接】waspThe batteries-included full-stack framework for the AI era. Develop JS/TS web apps (React, Node.js, and Prisma) using declarative code that abstracts away complex full-stack features like auth, background jobs, RPC, email sending, end-to-end type safety, single-command deployment, and more.项目地址: https://gitcode.com/GitHub_Trending/wa/wasp创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考