react课程22-实现较为完整的用户管理功能
Last updated on October 18, 2024 am
本节负责实现身份验证和授权、用户注册、登陆登出、修改用户信息的功能
一、身份验证和授权
(1) Authentication
身份验证是确认用户的身份的过程,目的是验证用户是谁。
首先我们在Authentication中新建了一个user,然后到API DOCS中的user management中寻找Log in with Email/Password
这一段代码,copy下来放置进apiAuth中。
1 |
|
然后可以新建一个useLogin钩子,得到mutation函数来处理登陆。可以看出onSuccess函数可以得到data,于是可以记录到控制台查看信息。
调用 queryClient.setQueryData(['user'], user.user);
:将登录成功的用户数据缓存到 ['user']
查询中,这样应用的其他部分可以使用这个用户信息。(user是object,包含session和user,而我们想要的只是user:用户)
replace是为了替换浏览记录。
1 |
|
调用 supabase.auth.signInWithPassword({ email, password })
时,Supabase
会将你提供的 email
和 password
发送到它的身份验证服务。
Supabase
在后台会将提供的 email
和 password
与其数据库中存储的用户信息进行比较。验证流程包括:
- 查找用户:
Supabase
会在其用户数据库中根据提供的email
查找相应的用户记录。 - 验证密码:找到用户后,
Supabase
会对传入的password
进行加密(通常是哈希处理),然后与数据库中存储的哈希值进行比较。密码在存储时是经过哈希加密的,数据库不直接存储明文密码。
验证结果:
- 成功:如果邮箱存在且密码匹配,
Supabase
会返回一个包含用户数据的响应(即data
对象)。这表示用户身份验证成功,用户可以登录。 - 失败:如果邮箱不存在或者密码不正确,
Supabase
会返回一个error
对象,其中包含错误信息(例如 “Invalid login credentials”)。
我们会发现登陆成功后,在浏览器storage出现了token。
(2)Authorization
授权是在身份验证成功后,确定用户对系统资源或操作的访问权限的过程。
建立一个ProtectedRoute
组件,并在App.jsx
的Route中把Applayout
组件用它包裹起来。由于其他界面都在Applayout
中被渲染,所以用户必须通过身份验证,才能进入任何在 AppLayout
中渲染的页面。(代码简洁,性能优化,更好的维护性)
ProtectedRoute
是一个自定义的路由组件,主要职责是限制对某些路由的访问,用来保护一组路由,通常是检查用户是否登录或是否有访问权限。
1、从supabase中获取用户的信息
会话(Session)是指在一段时间内,用户与系统之间的持续交互状态或连接。会话管理是现代应用程序和服务中的重要机制,用于跟踪用户的身份和状态。具体来说,会话与身份验证和授权密切相关。会话通常在用户登录或访问某个系统时开始,并在用户注销或超时后结束(详见文末)
通过检查用户的当前会话,从本地内存中获取登录用户的信息。如果没有会话(即用户未登录),返回 null
;如果会话有效,则获取并返回用户信息。
1 |
|
2、useUser.js
获取当前用户并将其存储到缓存中,这样就不用每次都重新下载。(获取isAuthenticated)
1 |
|
下图为登陆后React Query工具里得到的信息。
3、在ProtectRouter中来验证是否需要重定向到login界面
navigate
并不限于回调函数或useEffect
中使用,但这些场景是最常见和安全的使用方式不推荐在组件首次渲染的时候使用navigate。详见Router专题
- 得到
isAuthenticated
- 如果没有登陆就重定向到login界面(注意是在加载完成后检验是否认证!!!)
- 如果还在加载就返回一个加载指示器
- 如果已经登陆就渲染App
1 |
|
(3)Log out
1、apiAuth
1 |
|
2、useLogout
queryClient.removeQueries()
:清除 react-query
缓存中的所有数据,确保用户退出后,应用不再保留之前的查询数据。
navigate('/login', { replace: true })
:将用户重定向到登录页面,并且使用 replace: true
替换当前的历史记录,这样用户在点击“返回”按钮时不会回到之前已登出的页面。
1 |
|
3、Logout组件
1 |
|
二、实现注册功能
在这个APP中,逻辑是只有登陆成功后才能注册其他用户。
(1)创建注册表单
1、需要用到的工具
1 |
|
2、验证信息
1 |
|
3、如何提交表单?错误如何展示?
1 |
|
(2)实现把用户注册到supabase
伤到了,谁懂,supabase界面全英文,看到有一个警告,但是看不懂也没当回事,但是验证创建user的时候就出问题了。原来现在需要打开自定义SMTP的开关才能用随便的邮箱去注册用户了。在设置-Authentication里面。这个表单输入的信息要是真实的。
否则就会报错:Error: Email address “2768260754@rr.com“ cannot be used as it is not authorized at Object.signup [as mutationFn]
但是这还没有结束,下一小节是如何实现邮箱验证。supabase你关闭SMTP发送的功能好巧不巧,我现在要用到。。。💔
1、apiAuth
传递给signUp的object中可以包含一个可选项,因为只有email和password是必需项,所以名字可以通过这样的方式传递进去,同时可以再传一个用户的头像。
1 |
|
2、useSignUp
1 |
|
3、onSubmit
1 |
|
这里reset会引发问题。jonas还没改。
4、其他
- 在Authentication-URL Configuration中的两个URL栏,分别填入了
(http://localhost:5173/dashboard) 和(http://localhost:5173)我不知道是干嘛用的
“Temp Mail”(临时邮件)是指一种可以临时使用的电子邮件服务,允许用户生成一个临时的电子邮件地址。用户可以使用这个地址接收邮件,而不需要使用自己的真实邮箱。
地址:https://temp-mail.org/en/ 界面:(上面生成邮箱,下面是收件箱)
由于我们打开了SMTP的验证要求,所以用户在注册后都会在邮箱中收到一封需要验证的邮件,否则还是不能用这个邮箱登陆。
(3)如何配置Gmail解决supabase停止发送邮件
这里是这个紧急变革的讨论区https://github.com/orgs/supabase/discussions/29370
咱们也是跟supabase的维护者对上话了😂能写进简历里吗
一开始我以为只有那些专门的邮箱发送服务商才可以,于是打算使用看起来很靠谱的Resend,但是由于它需要自己有个域名,甚至去华为买了一个(首年1元的)域名。。。
但是后来发现Gmail居然也可以?!
1、设置自定义SMTP
在supabase的设置中找到Auth,下滑找到这个地方,首先点击here,进入设置邮箱limit的地方,把第一条设置改成3以上(每小时能够发的邮件数)。下面的email和name就相应填,email一定要填gmail邮箱,name可以随意。
下面的HOST和POST number就是smtp.gmail.com和465.
Username需要再填一次邮箱地址,不是随便填的!!!!!!!!!!
密码也不是Gmail的登陆密码,而是需要专门的密码,在Gmail账户中打开两步验证后找到设置应用专用密码,然后生成一个,注意生成后只会出现一次,需要立马复制下来。
这里每次更改密码再回来会发现没有改变,这也是我去问supabase维护者的问题,但是其实没有影响的。
2、如果遇到问题怎么获得详细的错误原因?
在我们supabase的项目sidebar中有一个是
在里面可以找到关于Auth的所有日志,比如当SMTP没设置好就是这个错误:
点进去会有详细的信息。
3、成功!🥳
经过一番不懈的努力,我终于实现了邮箱验证的功能,为了确认真的可以收到邮件,我使用了Temp emial的随机地址wamacex775@exweme.com(但是只要你不更换,每次进去都会是这个地址,所以很方便),并且在收件箱中真的看见了邮件:
(4)确保安全性
修改Policy,否则未登录的用户也是可以访问到这些数据库的信息的。
三、构建Header
这是现在的Header:
我们在注册用户的函数中,上传了除开email和密码的可选项,其中包含了avatar。这些信息可以在React Query工具里看到。
我们的Header包含头像、还有两个图标。其中UserAvatar.jsx:
1 |
|
四、实现更改密码和头像
(1)准备工作
1、在storage中建立自己的头像库,命名为avatars
2、添加Policy
选择这一条,然后为auth用户添加所有的操作权限
3、得到avatars中图片的URL路径
随便在其中上传一张图片,点击get url得到其URL路径,在后续的apiAuth中修改avatar的函数中需要用到。
(2)建立更新用户信息的函数
1、函数参数
函数updateCurrentUser接收一个object,包含密码、全名和头像。
export async function updateCurrentUser({ password, fullName, avatar })
2、更新password或fullName
之所以可以把更新这些信息的函数笼统地弄成一个,是因为修改密码和修改全名并不在一个表格中,所以可以条件性地得到updateData。记住fullName和avatar在注册的时候就是通过options中的data关键字注册的,在传递要更新的内容的时候,也要把它们包裹在一个对象中传递。
在没有上传avatar的时候,就可以结束了,因为下面是有关更新avatar的内容。
1 |
|
3、上传avatar
自定义filename,使用上面的操作返回的用户信息可以得到用户id,使用random()函数连接,放置图像名字重复。此处不需要返回data,这里是直接把图像上传到avatars库中。下面好更新。
1 |
|
4、在用户信息中更新avatar
得到上面的URL中,把相应信息做一个替换,就可以更新了。
1 |
|
(3)useUpdate
onSuccess中,queryClient.setQueryData(['user'], user);
查询参数必须是数组,否则就会报下图的错误。
1 |
|
原因是:
- 数据结构:React Query 在内部维护查询缓存时,期望查询键(比如
['user']
)是一个数组,因为数组可以包含多个层次的键名和参数。直接使用字符串'user'
会导致它无法正确处理查询数据的结构。 - 查询数据格式:当你使用
queryClient.setQueryData
设置数据时,它希望能在内部维护一个对象结构。如果你只传递了一个字符串,它无法创建或修改必要的内部属性。
?.涉及到的知识点详细分析(COPY)
(1)session(会话)
会话是用户与系统之间的持续交互状态,它保存用户的身份和状态信息,以便在多次请求中维持用户的登录状态和操作权限。在你提供的代码中,session
是 Supabase 通过 JWT 来管理的会话,它记录了用户的登录状态,并用来确定用户是否有权限访问某些资源。
1. 会话的定义:
- 会话是用户在一段时间内与应用程序或服务器之间进行的交互过程。它可以持续几秒钟到数小时,甚至更久,取决于会话的管理方式和应用程序的配置。
- 会话通常在用户登录或访问某个系统时开始,并在用户注销或超时后结束。
2. 会话的作用:
- 维持用户的身份验证状态:会话允许系统在多次请求之间记住用户的身份。例如,用户登录后,会话用于保存登录状态,以免用户每次访问页面时都需要重新登录。
- 存储用户的临时信息:会话可以用来在不同页面间共享用户的临时数据,例如购物车信息、用户首选项等。
- 确保安全性:通过会话,系统可以跟踪和验证用户的操作,防止未经授权的访问。
3. 会话是如何工作的:
- 当用户登录系统时,服务器会为该用户创建一个唯一的会话 ID,并将会话信息(如用户身份、权限等)与该会话 ID 关联。
- 会话 ID 通常通过 Cookies、Token(例如 JWT,JSON Web Token),或其他机制(如 URL 参数)存储在客户端。
- 在每次用户发送请求时,客户端会将会话 ID 传递给服务器,服务器根据这个 ID 查找与之关联的会话数据,确认用户的身份和权限。
4. 会话的生命周期:
- 创建:会话通常在用户成功登录后由服务器创建,分配一个唯一的会话 ID。
- 维持:在会话存续期间,用户的每次请求都使用同一会话 ID,服务器根据这个 ID 确认用户身份。
- 销毁:会话可以通过用户主动注销、会话超时(例如长时间不活动)或服务器强制结束而销毁。当会话销毁后,用户需要重新登录以继续访问系统。
5. 会话在前端和后端的角色:
- 前端:前端通过 Cookies 或 Local Storage 来存储会话 ID 或 Token。在每次向服务器发出请求时,前端会自动携带这些凭据来验证用户身份。
- 后端:后端负责生成和管理会话,保存会话相关的用户状态、信息,并在每次请求时验证用户的会话是否有效。
6. 会话与 Token(如 JWT)的关系:
- 传统会话:会话 ID 由服务器生成,并且服务器需要存储会话信息。这种方式通常需要维护一个会话存储,适用于基于服务器的应用程序。
- Token(如 JWT):JWT 是一种常用的无状态会话机制,其中用户的身份信息通过加密后保存在 Token 中。服务器不需要存储会话信息,而是通过验证和解码 Token 来识别用户。
7. Supabase 中的会话:
在你使用的 Supabase
中,会话是通过 JWT(JSON Web Token) 进行管理的。当用户登录时,Supabase
会生成一个 JWT,并将其保存在客户端。每次请求都会带上这个 Token,以证明用户的身份。
supabase.auth.getSession()
:该方法用于获取当前会话信息(包括 JWT)。如果用户当前登录了,这个会话就会返回有效的 JWT。supabase.auth.getUser()
:在有有效会话时,使用该方法获取当前会话中的用户详细信息。