【免费下载链接】howtographqlThe Fullstack Tutorial for GraphQL项目地址https://gitcode.com/gh_mirrors/ho/howtographql点击查看免费下载本篇指南基于 How To GraphQL 教程中 React Apollo 前端的 Authentication 章节带你完整走通一套前后端配合的认证方案用signup/login两个 GraphQL Mutation 获取 JWT、把 token 存入浏览器、再通过 Apollo 的setContextLink 为每个请求自动附加Authorization头最终让服务端识别“以谁的身份”发起请求并据此关联数据。完整的认证链路可以概括为四步用户在/login页面提交表单前端通过useMutation发起signup或loginMutationMutation 返回token前端将其写入localStorage并跳转回首页之后ApolloClient发出的每个请求都会经过authLink这个中间件把 token 拼进Authorization: Bearer token请求头服务端从Authorization头中解析出userId放入 resolver 的context从而在创建Link时自动关联postedBy。以下代码示例均来自教程配套的hackernews-react-apollo前端项目与server后端项目章节原文见 5-authentication.md。一、准备 Login 组件与路由1. Login 组件两种状态的登录/注册表单认证功能的起点是src/components/Login.js组件。它用一份formState同时承载登录和注册两种模式formState.login为true时面向已有账户的用户只渲染email和password两个输入框formState.login为false时面向尚未注册的用户额外渲染一个name输入框用于创建账户。提交按钮的onClick稍后会绑定到两个 Mutation此处先保留占位逻辑// src/components/Login.js import React, { useState } from react; import { useNavigate } from react-router-dom; const Login () { const navigate useNavigate(); const [formState, setFormState] useState({ login: true, email: , password: , name: }); return ( div h4 classNamemv3 {formState.login ? Login : Sign Up} /h4 div classNameflex flex-column {!formState.login ( input value{formState.name} onChange{(e) setFormState({ ...formState, name: e.target.value }) } typetext placeholderYour name / )} input value{formState.email} onChange{(e) setFormState({ ...formState, email: e.target.value }) } typetext placeholderYour email address / input value{formState.password} onChange{(e) setFormState({ ...formState, password: e.target.value }) } typepassword placeholderChoose a safe password / /div div classNameflex mt3 button classNamepointer mr2 button onClick{() console.log(onClick)} {formState.login ? login : create account} /button button classNamepointer button onClick{(e) setFormState({ ...formState, login: !formState.login }) } {formState.login ? need to create an account? : already have an account?} /button /div /div ); }; export default Login;第二个按钮通过login: !formState.login在两种模式间切换从而在同一个页面里复用了登录与注册两个流程。2. 定义 localStorage 中 token 的键名在src下新建constants.js统一声明用于在浏览器localStorage中存放凭据的键名// src/constants.js export const AUTH_TOKEN auth-token;安全提示把 JWT 存放在localStorage并不是前端认证的安全做法一旦页面存在 XSStoken 即可被脚本读取并随请求发走。本教程之所以这样处理是为了聚焦 GraphQL 本身、保持示例简洁。生产环境应结合 HttpOnly Cookie、更短的 token 有效期与刷新机制等方案来权衡切勿直接照搬。3. 注册 /login 路由打开App.js把Login加入路由表// src/components/App.js // ... import Login from ./Login; const App () { return ( div classNamecenter w85 Header / div classNameph3 pv1 background-gray Routes Route path/ element{LinkList/} / Route path/create element{CreateLink/} / Route path/login element{Login/} / /Routes /div /div ); }; export default App;4. Header 组件按登录态渲染入口打开Header.js让它根据localStorage中是否存在 token 来切换界面未登录时右侧显示login入口、不渲染submit入口已登录时显示logout并放出submit入口提交新链接。这样在 UI 层就能保证“只有认证用户才看得到创建链接的入口”// src/components/Header.js import React from react; import { Link, useNavigate } from react-router-dom; import { AUTH_TOKEN } from ../constants; const Header () { const navigate useNavigate(); const authToken localStorage.getItem(AUTH_TOKEN); return ( div classNameflex pa1 justify-between nowrap orange div classNameflex flex-fixed black Link to/ classNameno-underline black div classNamefw7 mr1Hacker News/div /Link Link to/ classNameml1 no-underline black new /Link div classNameml1|/div Link to/search classNameml1 no-underline black search /Link {authToken ( div classNameflex div classNameml1|/div Link to/create classNameml1 no-underline black submit /Link /div )} /div div classNameflex flex-fixed {authToken ? ( div classNameml1 pointer black onClick{() { localStorage.removeItem(AUTH_TOKEN); navigate(/); }} logout /div ) : ( Link to/login classNameml1 no-underline black login /Link )} /div /div ); }; export default Header;两处细节值得注意authToken localStorage.getItem(AUTH_TOKEN)是整个组件渲染逻辑的开关没有 token 时submit不渲染点击logout时执行localStorage.removeItem(AUTH_TOKEN)并navigate(/)回到首页——因为 token 是前端唯一的状态源清掉它即完成登出下次请求自然不再携带Authorization头。二、实现 signup 与 login 两个 Mutationsignup和login就是两个普通的 GraphQL Mutation用法与前面章节里的createLink完全一致。在Login.js顶部定义两份 mutation 文档// src/components/Login.js const SIGNUP_MUTATION gql mutation SignupMutation( $email: String! $password: String! $name: String! ) { signup( email: $email password: $password name: $name ) { token } } ; const LOGIN_MUTATION gql mutation LoginMutation( $email: String! $password: String! ) { login(email: $email, password: $password) { token } } ;两个 Mutation 结构非常相似接收若干入参返回一个token。这个 token 之后会被附加到所有后续请求上用来向服务端表明“请求是以该用户的名义发出的”。1. 用 useMutation 绑定表单把提交按钮的onClick改为三目表达式按当前状态分别调用login或signupdiv classNameflex mt3 button classNamepointer mr2 button onClick{formState.login ? login : signup} {formState.login ? login : create account} /button button classNamepointer button onClick{(e) setFormState({ ...formState, login: !formState.login }) } {formState.login ? need to create an account? : already have an account?} /button /div然后在组件内用 Apollo 的useMutationhook 创建这两个 mutation 触发器const [login] useMutation(LOGIN_MUTATION, { variables: { email: formState.email, password: formState.password }, onCompleted: ({ login }) { localStorage.setItem(AUTH_TOKEN, login.token); navigate(/); } }); const [signup] useMutation(SIGNUP_MUTATION, { variables: { name: formState.name, email: formState.email, password: formState.password }, onCompleted: ({ signup }) { localStorage.setItem(AUTH_TOKEN, signup.token); navigate(/); } });三个要点variables直接取自表单状态mutation 执行时把当前输入作为变量发送onCompleted回调在 mutation 成功完成后触发先把响应中的login.token/signup.token写入localStorage键名即AUTH_TOKEN再跳转回首页别忘了补齐 importimport { useMutation, gql } from apollo/client; import { useNavigate } from react-router-dom; import { AUTH_TOKEN } from ../constants;2. 本地验证运行yarn start打开http://localhost:3000/login点击need to create an account?切换到注册模式填写name、email、password后点击create account。一切正常的话应用会跳回首页并且 Header 中submit按钮重新出现——说明 token 已写入localStorage。要确认用户确实落库可以在 database 项目的 dev Playground 中发送users查询验证新用户存在。三、用 Apollo Link 为所有请求附加认证头登录成功只是拿到了 token真正的关键是让之后发出的每一个 GraphQL 请求都自动携带它。由于所有 API 请求都是由应用根部的ApolloClient实例创建并发送的需要在index.js中为它配置一个认证 Link。Apollo 的 Link 机制允许你构建“中间件”在请求到达httpLink之前对其进行修改。认证正是这一机制的典型用法——用setContext创建一个authLink放在httpLink与ApolloClient实例化之间// src/index.js const authLink setContext((_, { headers }) { const token localStorage.getItem(AUTH_TOKEN); return { headers: { ...headers, authorization: token ? Bearer ${token} : } }; });配套 importimport { setContext } from apollo/client/link/context; import { AUTH_TOKEN } from ./constants;工作原理分两步每次ApolloClient发请求时都会执行这个回调回调先从localStorage取出 token如果存在然后把authorization: Bearer token合并进请求头返回给 context供后续的httpLink读取并真正发出 HTTP 请求。最后把 link 链交给客户端const client new ApolloClient({ link: authLink.concat(httpLink), cache: new InMemoryCache() });authLink.concat(httpLink)表示请求先经过authLink注入请求头再交给httpLink发起网络调用。至此只要localStorage里有 token所有 API 请求都会被自动认证登出后token 被移除authorization头变为空串请求即回到匿名状态。这正是本章节配套测验考察的核心点“ApolloClient 发出的 HTTP 请求是如何认证的”正确答案不是给ApolloClient传参或给它传一个认证方法而是通过专门的 Apollo Link 中间件把认证 token 附加到请求上。四、服务端如何消费这个 token前端的最终目的是让服务端能识别请求身份。教程的后端项目server在server/src/resolvers/Mutation.js中的postresolver 展示了这一过程// server/src/resolvers/Mutation.js async function post(parent, args, context, info) { const { userId } context; let postedBy undefined if (userId) { postedBy { connect: { id: userId } } } const newLink await context.prisma.link.create({ data: { url: args.url, description: args.description, postedBy } }); context.pubsub.publish(NEW_LINK, newLink); // not important for now return newLink; }逐行拆解const { userId } contextuserId是服务端在构造 request context 时从Authorization请求头中解析 JWT 得到的。请求未携带该头时userId为undefined若userId存在则构造postedBy { connect: { id: userId } }用 Prisma 的connect写法把即将创建的Link直接外键关联到提交它的User若用户未登录postedBy保持undefinedPrisma 会忽略该字段Link便没有归属用户。教程特意允许“无主链接”存在仅出于演示考虑生产应用通常应改为强制认证context.pubsub.publish(NEW_LINK, newLink)会在后续订阅章节中发挥作用用于向订阅者推送新链接事件。对照服务端实现token 从哪里来、userId 如何入 context本仓库中配套的后端教程 graphql-js 认证章节 给出了同一套方案的完整服务端实现可对照理解前后端是如何咬合的token 的签发signupresolver 先用bcrypt.hash(args.password, 10)对密码做哈希入库再执行jwt.sign({ userId: user.id }, APP_SECRET)把userId装进 JWT 载荷并用APP_SECRET签名见 signup/login resolverloginresolver 则按email查库、用bcrypt.compare比对密码校验通过后签发同样的 tokenuserId 的解析工具函数getUserId从req.headers.authorization取出Bearer前缀后的 token调用jwt.verify(token, APP_SECRET)验证签名后解出userId任何一步失败都会抛Not authenticated异常见 getUserId 实现context 的组装Apollo Server 的context被写成一个函数在每个请求到来时执行userId: req req.headers.authorization ? getUserId(req) : null从而把“认证后的身份”注入context供post等 resolver 直接读取见 context 配置。把前后端两段代码放在一起看整条链路就非常清晰了前端authLink写入的Authorization: Bearer token头正是服务端getUserId读取并验证的同一份数据userId进入context后又被postresolver 用于connect关联postedBy。前端负责“把身份带过去”服务端负责“验证身份并消费它”中间没有任何额外通道。小结本章节完成的认证方案可以归纳为四个关键件组件位置职责Login组件 路由src/components/Login.js、App.js提供登录/注册双态表单触发对应 MutationAUTH_TOKEN常量src/constants.js统一localStorage中的 token 键名贯穿组件与 LinkauthLinksetContextsrc/index.js每次发请求前把localStorage中的 token 写入Authorization: Bearer头postresolverserver/src/resolvers/Mutation.js从context.userId由 JWT 解析而来自动关联postedBy这套模式——“Mutation 返回 token、客户端中间件统一附加请求头、服务端从 context 读取身份”——是 GraphQL 项目中最常见的无状态认证方式。理解了它之后后续章节的投票、订阅等功能都可以建立在这个身份体系之上继续扩展教程整体章节规划可参考 React Apollo 课程结构。赞分享【免费下载链接】howtographqlThe Fullstack Tutorial for GraphQL项目地址https://gitcode.com/gh_mirrors/ho/howtographql点击查看免费下载相关推荐How To GraphQL React Apollo 前端教程用 Apollo Client 构建 Hackernews 克隆应用How To GraphQL React Apollo 前端教程用 Apollo Client 构建 Hackernews 克隆应用 本篇是 How ToHow to GraphQL React 教程用 Apollo Client 的 useMutation 发送 Mutation 创建 LinkHow to GraphQL React 教程用 Apollo Client 的 useMutation 发送 Mutation 创建 Link 本文基于 HCoreUI React用户登录与注册完整认证流程实现指南CoreUI React用户登录与注册完整认证流程实现指南 想要快速构建现代化的React管理后台系统吗CoreUI Free React Admin Te前端上一篇Axure 汉化 3 分钟搞定axure-cn 免费中文语言包安装教程下一篇VC 运行库修复一条命令装齐 2005–2022 全版本创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考