- 后端
- 前端
- Web框架
- 开发工具
【免费下载链接】redwood
RedwoodGraphQL
这篇指南将带你脱离 Netlify Identity Widget 的“一键式”束缚,改用 GoTrue-JS 客户端库直接对接 Netlify Identity 的 GoTrue API,在 Redwood 应用中完全掌控认证界面与交互流程。读完本文,你将掌握如何用yarn redwood setup auth goTrue生成认证配置、手写 Sign Up / Sign In 表单、实现 Sign Out 按钮,并通过useAuth的认证状态在导航中条件渲染正确的操作入口。
本文以 Redwood 官方文档《GoTrue Auth》为基础,并结合当前仓库源码(CLI 认证脚手架、Netlify 认证提供者实现)进行纵深讲解。
为什么放弃 Widget、拥抱 GoTrue-JS
如果你已完成 Redwood 教程的认证章节,会发现用 Netlify Identity Widget 几分钟内就能给应用加上认证。但 Widget 是一套封装好的弹窗交互,你很难定制界面与行为。
GoTrue-JS 是 Netlify Identity 的 GoTrue API 的官方 JavaScript 客户端库,它把注册、登录、登出、令牌刷新等能力以编程接口的形式暴露出来,让你:
- 完全自定义认证表单的 UI 与校验逻辑;
- 在开发阶段保持较低的接入复杂度;
- 复用 Redwood 的
useAuth钩子,以统一方式订阅认证状态。
在本文的实操中,我们将完成四件事:
- 用 Redwood CLI 生成 GoTrue 认证配置;
- 创建 Sign Up(注册)表单;
- 创建 Sign In(登录)表单;
- 创建 Sign Out(登出)按钮;
- 添加根据认证状态显示正确按钮的导航链接。
前置条件
动手之前,请确保已完成以下步骤:
- 创建一个 Redwood 应用;
- 注册 Netlify 账户;
- 启用 Netlify Identity;
- 启动开发服务器:
yarn redwood dev。
启用 Netlify Identity
登录 Netlify,进入站点Dashboard,打开Identity标签页,点击Enable Identity。启用后你会看到形如https://my-bodacious-app.netlify.app/.netlify/identity的 Identity API 端点。请把该地址复制保存,稍后实例化 GoTrue-JS 时需要用到。
生成认证配置
安装所需依赖包并生成 Redwood Auth 的样板代码,只需要一条 CLI 命令:
yarn redwood setup auth goTrue指定goTrue作为提供者后,Redwood 会自动把 GoTrue-JS 的配置写入web/src/App.js。打开该文件,你应该看到:
import { AuthProvider } from '@redwoodjs/auth' import GoTrue from 'gotrue-js' import { FatalErrorBoundary } from '@redwoodjs/web' import { RedwoodApolloProvider } from '@redwoodjs/web/apollo' import FatalErrorPage from 'src/pages/FatalErrorPage' import Routes from 'src/Routes' import './index.css' const goTrueClient = new GoTrue({ APIUrl: 'https://MYAPP.netlify.app/.netlify/identity', setCookie: true, }) const App = () => ( <FatalErrorBoundary page={FatalErrorPage}> <AuthProvider client={goTrueClient} type="goTrue"> <RedwoodApolloProvider> <Routes /> </RedwoodApolloProvider> </AuthProvider> </FatalErrorBoundary> ) export default App现在把刚才复制的 API 端点替换到APIUrl中:
// imports... const goTrueClient = new GoTrue({ APIUrl: 'https://gotrue-recipe.netlify.app/.netlify/identity', setCookie: true, })配置到此结束。核心要点是:<AuthProvider>接收client(GoTrue-JS 实例)和type('goTrue')两个关键属性,之后整个组件树都能通过useAuth访问该客户端及其全部方法。
关于“goTrue”的现状说明:当前仓库的 CLI 认证脚手架 中,
goTrue已作为“不再单独支持”的历史提供者被重定向(redirectCommand('goTrue')),官方现在建议改用netlify(基于 netlify-identity-widget)或 Custom Auth。但 GoTrue 的接入思路——用统一client+type注入<AuthProvider>、通过useAuth消费——在 Redwood 的所有认证提供者中一脉相承,理解本文流程仍可直接迁移。在 v6.x 时代,setup auth goTrue会直接生成上面的代码。
Sign Up:注册页
先确保在 Netlify Dashboard 的Identity > Settings and usage中,进入Emails > Confirmation template > Edit settings,勾选Allow users to sign up without verifying their email address并保存。这样首版注册流程不需要处理邮箱确认,其余进阶能力(邮箱确认、密码找回等)留待后续扩展。
生成注册页
yarn redwood generate page Signup该命令会为 Signup 添加一条 路由,并创建 SignupPage 组件。在web/src/pages/SignupPage/SignupPage.js中导入 Redwood Form 组件,加入一个最基础的注册表单:
import { Form, TextField, PasswordField, Submit } from '@redwoodjs/forms' const SignupPage = () => { return ( <> <h1>Sign Up</h1> <Form> <TextField name="email" placeholder="email" /> <PasswordField name="password" placeholder="password" /> <Submit>Sign Up</Submit> </Form> </> ) } export default SignupPageTextField与PasswordField的name属性会直接映射到onSubmit回调收到的数据对象字段上,这是 Redwood Form 的核心约定。
接入 useAuth 与注册逻辑
接下来让表单在提交时真正执行注册。先为Form增加onSubmit处理函数:
// imports... const SignupPage = () => { const onSubmit = (data) => { // do something here } return ( <> <h1>Sign Up</h1> <Form onSubmit={onSubmit}> <TextField name="email" placeholder="email" /> <PasswordField name="password" placeholder="password" /> <Submit>Sign Up</Submit> </Form> </> ) } //...现在需要一条与<AuthProvider />及 GoTrue-JS 客户端通信的通道——正是useAuth钩子。它让我们订阅认证状态及其属性,此处通过它拿到client(GoTrue-JS 实例):
import { Form, TextField, PasswordField, Submit } from '@redwoodjs/forms' import { useAuth } from '@redwoodjs/auth' const SignupPage = () => { const { client } = useAuth() const onSubmit = (data) => { // do something here } return ( <> <h1>Sign Up</h1> <Form onSubmit={onSubmit}> <TextField name="email" placeholder="email" /> <PasswordField name="password" placeholder="password" /> <Submit>Sign Up</Submit> </Form> </> ) } export default SignupPage在onSubmit中调用client.signup(email, password)创建新用户:
import { Form, TextField, PasswordField, Submit } from '@redwoodjs/forms' import { useAuth } from '@redwoodjs/auth' const SignupPage = () => { const { client } = useAuth() const onSubmit = (data) => { client .signup(data.email, data.password) .then((res) => console.log(res)) .catch((error) => console.log(error)) } return ( <> <h1>Sign Up</h1> <Form onSubmit={onSubmit}> <TextField name="email" placeholder="email" /> <PasswordField name="password" placeholder="password" /> <Submit>Sign Up</Submit> </Form> </> ) } export default SignupPage错误处理与成功跳转
注册本身已经能工作,但把响应打印到控制台显然不够。我们用React.useState管理错误状态,在每次提交前用setError(null)重置,并在表单中条件渲染错误消息:
import { Form, TextField, PasswordField, Submit } from '@redwoodjs/forms' import { useAuth } from '@redwoodjs/auth' const SignupPage = () => { const { client } = useAuth() const [error, setError] = React.useState(null) const onSubmit = (data) => { setError(null) client .signup(data.email, data.password) .then((res) => console.log(res)) .catch((error) => setError(error.message)) } return ( <> <h1>Sign Up</h1> <Form onSubmit={onSubmit}> {error && <p>{error}</p>} <TextField name="email" placeholder="email" /> <PasswordField name="password" placeholder="password" /> <Submit>Sign Up</Submit> </Form> </> ) } export default SignupPage注册成功后,最自然的做法是把用户引导到即将建好的登录页。先生成 Sign In 页面:
yarn redwood generate page Signin再从 Redwood Router 导入routes与navigate,在注册成功的then回调中跳转:
import { Form, TextField, PasswordField, Submit } from '@redwoodjs/forms' import { useAuth } from '@redwoodjs/auth' import { routes, navigate } from '@redwoodjs/router' const SignupPage = () => { const { client } = useAuth() const [error, setError] = React.useState(null) const onSubmit = (data) => { setError(null) client .signup(data.email, data.password) .then(() => navigate(routes.signin())) .catch((error) => setError(error.message)) } return ( <> <h1>Sign Up</h1> <Form onSubmit={onSubmit}> {error && <p>{error}</p>} <TextField name="email" placeholder="email" /> <PasswordField name="password" placeholder="password" /> <Submit>Sign Up</Submit> </Form> </> ) } export default SignupPage至此,注册页完成:提交表单 → 调用client.signup()→ 失败显示错误,成功跳转登录页。
Sign In:登录页
在上一节生成的 SigninPage 中加入包含email、password的基础表单、错误状态与空的onSubmit:
import { Form, TextField, PasswordField, Submit } from '@redwoodjs/forms' const SigninPage = () => { const [error, setError] = React.useState(null) const onSubmit = (data) => { // do sign in here } return ( <> <h1>Sign In</h1> <Form onSubmit={onSubmit}> {error && <p>{error}</p>} <TextField name="email" placeholder="email" /> <PasswordField name="password" placeholder="password" /> <Submit>Sign In</Submit> </Form> </> ) } export default SigninPage从useAuth中解构出logIn:
import { Form, TextField, PasswordField, Submit } from '@redwoodjs/forms' import { useAuth } from '@redwoodjs/auth' const SigninPage = () => { const { logIn } = useAuth() const [error, setError] = React.useState(null) const onSubmit = (data) => { setError(null) // do sign in here } return ( <> <h1>Sign In</h1> <Form onSubmit={onSubmit}> {error && <p>{error}</p>} <TextField name="email" placeholder="email" /> <PasswordField name="password" placeholder="password" /> <Submit>Sign In</Submit> </Form> </> ) } export default SigninPage注意与注册的差异:这次直接调用 Redwood Auth 的logIn函数(而非client),参数是一个对象,包含email、password和一个remember布尔值(控制是否记住登录状态):
import { Form, TextField, PasswordField, Submit } from '@redwoodjs/forms' import { useAuth } from '@redwoodjs/auth' const SigninPage = () => { const { logIn } = useAuth() const [error, setError] = React.useState(null) const onSubmit = (data) => { setError(null) logIn({ email: data.email, password: data.password, remember: true }) .then(() => { // do something }) .catch((error) => setError(error.message)) } return ( <> <h1>Sign In</h1> <Form onSubmit={onSubmit}> {error && <p>{error}</p>} <TextField name="email" placeholder="email" /> <PasswordField name="password" placeholder="password" /> <Submit>Sign In</Submit> </Form> </> ) } export default SigninPage登录成功后把用户带回首页。先生成首页(如果还没有):
yarn redwood generate page Home /在 SigninPage 中导入navigate与routes,在then回调中跳转:
import { Form, TextField, PasswordField, Submit } from '@redwoodjs/forms' import { useAuth } from '@redwoodjs/auth' import { navigate, routes } from '@redwoodjs/router' const SigninPage = () => { const { logIn } = useAuth() const [error, setError] = React.useState(null) const onSubmit = (data) => { setError(null) logIn({ email: data.email, password: data.password, remember: true }) .then(() => navigate(routes.home())) .catch((error) => setError(error.message)) } return ( <> <h1>Sign In</h1> <Form onSubmit={onSubmit}> {error && <p>{error}</p>} <TextField name="email" placeholder="email" /> <PasswordField name="password" placeholder="password" /> <Submit>Sign In</Submit> </Form> </> ) } export default SigninPage登录页完成:提交表单 →logIn({ email, password, remember })→ 失败显示错误,成功跳转首页。
Sign Out:登出按钮
登出是最容易实现的认证功能:只需触发useAuth的logOut方法。先生成一个组件存放登出按钮:
yarn redwood generate component SignoutBtn在web/src/components/SignoutBtn/SignoutBtn.js中渲染按钮并添加点击处理器:
const SignoutBtn = () => { const onClick = () => { // do sign out here. } return <button onClick={() => onClick()}>Sign Out</button> } export default SignoutBtn导入useAuth,解构logOut并在onClick中调用:
import { useAuth } from '@redwoodjs/auth' const SignoutBtn = () => { const { logOut } = useAuth() const onClick = () => { logOut() } return <button onClick={() => onClick()}>Sign Out</button> } export default SignoutBtn如果用户是在应用的私密区域点击登出按钮,还应该把用户导航离开当前页面:
import { useAuth } from '@redwoodjs/auth' import { navigate, routes } from '@redwoodjs/router' const SignoutBtn = () => { const { logOut } = useAuth() const onClick = () => { logOut().then(() => navigate(routes.home())) } return <button onClick={() => onClick()}>Sign Out</button> } export default SignoutBtn此时logOut()完成后会返回一个 Promise,随后跳转到首页。
Auth Links:根据认证状态渲染导航
现在实现条件导航:未登录时显示Sign Up与Sign In,已登录时显示Log Out。先生成导航组件:
yarn redwood generate component Navigation在web/src/components/Navigation/Navigation.js中,从@redwoodjs/router导入 Link 组件与 routes 对象,并导入useAuth以便订阅认证状态:
import { Link, routes } from '@redwoodjs/router' import { useAuth } from '@redwoodjs/auth' const Navigation = () => { return <nav></nav> } export default Navigation从useAuth解构isAuthenticated,并应用到渲染条件中:
import { Link, routes } from '@redwoodjs/router' import { useAuth } from '@redwoodjs/auth' const Navigation = () => { const { isAuthenticated } = useAuth() return ( <nav> {isAuthenticated ? ( // signed in - show the Sign Out button ) : ( // signed out - show the Sign Up and Sign In links )} </nav> ) } export default NavigationRedwood Auth 基于 React Context API 管理并广播认证状态,因此isAuthenticated始终是最新的——即使认证状态在组件树中其他位置发生变化(只要该组件是<AuthProvider />的子节点),React 也会自动重新渲染出正确的组件。
接下来导入登出按钮,并把 Sign In / Sign Up 链接放进条件分支:
import { Link, routes } from '@redwoodjs/router' import { useAuth } from '@redwoodjs/auth' import SignoutBtn from 'src/components/SignoutBtn/SignoutBtn' const Navigation = () => { const { isAuthenticated } = useAuth() return ( <nav> {isAuthenticated ? ( <SignoutBtn /> ) : ( <> <Link to={routes.signup()}>Sign Up</Link> <Link to={routes.signin()}>Sign In</Link> </> )} </nav> ) } export default Navigation把导航挂进全局布局
导航组件还需渲染到应用中。先生成一个名为 Global 的布局:
yarn redwood generate layout Global在web/src/layouts/GlobalLayout/GlobalLayout.js中导入并渲染导航组件:
import Navigation from 'src/components/Navigation/Navigation' const GlobalLayout = ({ children }) => { return ( <> <header> <Navigation /> </header> <main>{children}</main> </> ) } export default GlobalLayout最后,用 GlobalLayout 包裹每个生成的页面:
Home
import GlobalLayout from 'src/layouts/GlobalLayout/GlobalLayout' const HomePage = () => { return ( <GlobalLayout> <h1>Home</h1> <p>My Gotrue Redwood Auth</p> </GlobalLayout> ) } export default HomePageSign Up
import { Form, TextField, PasswordField, Submit } from '@redwoodjs/forms' import { useAuth } from '@redwoodjs/auth' import { routes, navigate } from '@redwoodjs/router' import GlobalLayout from 'src/layouts/GlobalLayout/GlobalLayout' const SignupPage = () => { const { client } = useAuth() const [error, setError] = React.useState(null) const onSubmit = (data) => { setError(null) client .signup(data.email, data.password) .then(() => navigate(routes.signin())) .catch((error) => setError(error.message)) } return ( <GlobalLayout> <h1>Sign Up</h1> <Form onSubmit={onSubmit}> {error && <p>{error}</p>} <TextField name="email" placeholder="email" /> <PasswordField name="password" placeholder="password" /> <Submit>Sign Up</Submit> </Form> </GlobalLayout> ) } export default SignupPageSign In
import { Form, TextField, PasswordField, Submit } from '@redwoodjs/forms' import { useAuth } from '@redwoodjs/auth' import { navigate, routes } from '@redwoodjs/router' import GlobalLayout from 'src/layouts/GlobalLayout/GlobalLayout' const SigninPage = () => { const { logIn } = useAuth() const [error, setError] = React.useState(null) const onSubmit = (data) => { setError(null) logIn({ email: data.email, password: data.password, remember: true }) .then(() => navigate(routes.home())) .catch((error) => setError(error.message)) } return ( <GlobalLayout> <h1>Sign In</h1> <Form onSubmit={onSubmit}> {error && <p>{error}</p>} <TextField name="email" placeholder="email" /> <PasswordField name="password" placeholder="password" /> <Submit>Sign In</Submit> </Form> </GlobalLayout> ) } export default SigninPage现在导航会根据认证状态渲染正确的链接和按钮:用户登录后看到Sign Out按钮,登出后看到Sign Up与Sign In链接。
源码视角:认证提供者如何工作
为加深理解,可以对照当前仓库中 Redwood 官方认证提供者的实现。以 Netlify 提供者的前端实现 为例,它的核心是createAuthImplementation返回的一组接口,包括:
login:打开登录弹窗并监听login/close/error事件,返回 Promise 形式的用户对象;logout:调用netlifyIdentity.logout(),在logout事件触发时 resolve;signup:打开注册弹窗,关闭时 resolve;getToken:调用netlifyIdentity.refresh()(仅在令牌过期时真正刷新),返回access_token;getUserMetadata/restoreAuthState:返回当前用户。
createAuth(implementation)会把这份实现接入 Redwood 的useAuth,这正是我们页面中logIn、logOut、client、isAuthenticated等 API 的底层来源。GoTrue-JS 时代的接入方式(<AuthProvider client={goTrueClient} type="goTrue">)与该模式完全一致:Redwood 只负责“翻译”认证客户端的能力,认证的存储、刷新与校验仍由提供方完成。
总结
至此,我们已经:
- 用
yarn redwood setup auth goTrue配置了 GoTrue 与 Redwood Auth 的对接; - 创建了 Sign Up 页面(含错误提示与成功跳转);
- 创建了 Sign In 页面(
logIn({ email, password, remember })登录并跳转首页); - 创建了 Sign Out 按钮(
logOut()后导航回首页); - 在 GlobalLayout 中挂载了条件导航,根据
isAuthenticated渲染正确操作入口。
这套“底层认证客户端 + RedwooduseAuth统一接口 + 自定义表单”的模式,可以平滑迁移到 Redwood 当前支持的任意认证提供者(如 Netlify、Supabase、Auth0 等),是深入理解 Redwood 认证体系的良好起点。后续还可以继续扩展邮箱确认、密码找回等进阶功能。
- 后端
- 前端
- Web框架
- 开发工具
【免费下载链接】redwood
RedwoodGraphQL
相关推荐
终极指南:如何使用Sign-In with Ethereum实现去中心化身份认证
终极指南:如何使用Sign In with Ethereum实现去中心化身份认证 Sign In with Ethereum(SIWE)正在彻底改变我们进行身份
Noodle平台认证系统实现:从Sign-In到权限管理全流程
Noodle平台认证系统实现:从Sign In到权限管理全流程 你是否还在为开源项目的用户认证系统搭建而烦恼?Noodle平台作为开源教育平台(Open Sou
教育前端后端Barter-rs多策略并发执行:提升交易效率的高级技巧
Barter rs多策略并发执行:提升交易效率的高级技巧 Barter rs是一个开源的Rust框架,专为构建事件驱动的实时交易和回测系统而设计。多策略并发执行
金融科技
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考