识别用户的能力对于维护任何应用程序的安全性至关重要。同样重要的是为管理用户身份而编写的代码,尤其是在避免未经授权访问应用程序持有的数据的方面。在没有可用框架或库的情况下编写身份验证代码可能需要大量时间才能做好——更不用说持续维护该自定义代码了。
这就是Firebase来救援的地方。其即用型和直观的方法可以立即在站点上设置有效的用户身份管理。本教程将指导我们如何做到这一点:实现用户注册、验证和身份验证。
Firebase v9 SDK引入了一个新的模块化 API 界面,导致其多项服务发生变化,其中之一是 Firebase 身份验证。本教程是最新的 v9 中的更改。
查看演示GITHUB 回购
要学习本教程,您应该熟悉 React、React hooks 和 Firebase 版本 8。您还应该在您的机器上安装一个 Google 帐户和 Node。
目录
- 设置 Firebase
- 克隆和设置起始仓库
- 将 Firebase 集成到我们的 React 应用程序中
- 创建用户注册功能
- 使用 React Context API 管理用户状态
- 向注册用户发送验证电子邮件
- 在用户个人资料页面上工作
- 为 Profile 组件创建私有路由
- 创建登录功能
- 结论
- 参考
设置 Firebase
在我们开始使用 Firebase 来满足我们的注册和身份验证要求之前,我们必须首先设置我们的 Firebase 项目以及我们正在使用的身份验证方法。
要添加项目,请确保您已登录 Google 帐户,然后导航到Firebase 控制台并单击Add project。从那里,为项目命名(我使用的是“Firebase-user-reg-auth”),我们应该都准备好继续了。
有时可能会提示您启用 Google Analytics。本教程不需要它,因此请随意跳过该步骤。
Firebase 有多种针对移动设备和 Web 的身份验证方法,但在我们开始使用其中任何一种之前,我们必须首先在 Firebase 身份验证页面上启用它。在侧边栏菜单中,单击Authentication图标,然后在下一页上单击Get started。
我们将使用电子邮件身份验证。单击它,系统将提示我们启用它,这正是我们想要做的。
克隆和设置起始仓库
我已经创建了一个可以用于本教程的简单模板,以便我们可以专注于学习如何实现这些功能。所以我们现在需要做的是克隆 GitHub 存储库。
启动你的终端。以下是我们可以从命令行运行的内容:
git clone -b starter https://github.com/Tammibriggs/Firebase_user_auth.git
cd Firebase_user_auth
npm install
我还在package.json
文件的依赖对象中包含了 Firebase 版本 9。因此,通过运行该npm install
命令,将安装 Firebase v9 以及所有其他依赖项。
完成后,让我们用npm start
!
将 Firebase 集成到我们的 React 应用程序中
要集成 Firebase,我们需要先获取 Web 配置对象,然后在我们的 React 应用程序中使用它来初始化 Firebase。转到 Firebase 项目页面,我们将看到一组选项,如下所示:
单击 web ( </>
) 图标为 web 配置我们的 Firebase 项目,我们将看到如下页面:
输入firebase-user-auth作为 Web 应用程序的名称。之后,单击注册应用程序按钮,这会将我们带到提供对象的下一步firebaseConfig
。
将配置复制到剪贴板,因为稍后我们将需要它来初始化 Firebase。然后单击继续到控制台按钮以完成该过程。
现在,让我们初始化 Firebase 和 Firebase 身份验证,以便我们可以开始在我们的应用中使用它们。在src
我们的 React 应用程序的目录中,创建一个firebase.js
文件并添加以下导入:
// src/firebase.js
import { initializeApp } from 'firebase/app'
import {getAuth} from 'firebase/auth'
现在,在导入之后粘贴我们之前复制的配置,并添加以下代码行来初始化 Firebase 和 Firebase 身份验证。
// src/firebase.js
const app = initializeApp(firebaseConfig)
const auth = getAuth(app)
export {auth}
我们的firebase.js
文件现在应该如下所示:
// src.firebase.js
import { initializeApp } from "firebase/app"
import { getAuth } from "firebase/auth"
const firebaseConfig = {
apiKey: "API_KEY",
authDomain: "AUTH_DOMAIN",
projectId: "PROJECT_ID",
storageBucket: "STORAGE_BUCKET",
messagingSenderId: "MESSAGING_SENDER_ID",
appId: "APP_ID"
}
// Initialize Firebase and Firebase Authentication
const app = initializeApp(firebaseConfig)
const auth = getAuth(app)
export {auth}
接下来,我们将介绍如何使用 Firebase 提供的即用型功能为我们克隆的模板添加注册、电子邮件验证和登录功能。
创建用户注册功能
在 Firebase 版本 9 中,我们可以使用该功能构建用户注册createUserWithEmailAndPassword
功能。这个函数接受三个参数:
- 身份验证实例/服务
- 电子邮件
在版本 9 中,服务始终作为第一个参数传递。在我们的例子中,它是身份验证服务。
要创建此功能,我们将使用克隆模板目录中的Register.js
文件。src
我在这个文件中所做的是创建三个表单字段——电子邮件、和确认——输入由状态控制。现在,让我们开始做生意。
让我们首先添加一个验证密和确认输入的函数,检查它们是否不为空且相同: 在Register
组件中的状态之后添加以下代码行:
// src/Register.js
// ...
const validatePassword = () => {
let isValid = true
if (password !== '' && confirmPassword !== ''){
if (password !== confirmPassword) {
isValid = false
setError('Passwords does not match')
}
}
return isValid
}
// ...
在上面的函数中,我们返回一个isValid
变量,该变量可以根据密的有效性返回true或false 。isValid
稍后,我们将使用此变量的值来创建一个条件,即仅当为 true时才会调用负责注册用户的 Firebase 函数。
要创建注册功能,让我们首先对Register.js
文件进行必要的导入:
// src/Register.js
import {auth} from './firebase'
import {createUserWithEmailAndPassword} from 'firebase/auth'
validatePassword
现在,在密函数之后添加以下代码行:
// src/Register.js
// ...
const register = e => {
e.preventDefault()
setError('')
if(validatePassword()) {
// Create a new user with email and password using firebase
createUserWithEmailAndPassword(auth, email, password)
.then((res) => {
console.log(res.user)
})
.catch(err => setError(err.message))
}
setEmail('')
setPassword('')
setConfirmPassword('')
}
// ...
在上面的函数中,我们设置了一个条件,只有在返回值为truecreateUserWithEmailAndPassword
时才调用该函数。validatePassword
为了开始工作,让我们在register
提交表单时调用该函数。我们可以通过向onSubmit
表单添加一个事件来做到这一点。修改 的开始标签,registration_form
如下所示:
// src/Register.js
<form onSubmit={register} name='registration_form'>
有了这个,我们现在可以在我们的网站上注册一个新用户。要对此进行测试,请转到http://localhost:3000/register
浏览器中,填写表格,然后单击“注册”按钮。
单击注册按钮后,如果我们打开浏览器的控制台,我们将看到新注册用户的详细信息。
使用 React Context API 管理用户状态
Context API是一种在 React 组件树的任何级别与组件共享数据的方法,而无需将其作为 props 向下传递。由于树中的不同组件可能需要用户,因此使用 Context API 非常适合管理用户状态。
在我们开始使用 Context API 之前,我们需要设置一些东西:
-
createContext()
使用方法创建上下文对象 - 将我们想要共享用户状态的组件作为Context.Provider的子级传递
- 将我们希望子/消费组件作为道具访问的值传递给
Context.Provider
让我们开始吧。在该src
目录中,创建一个AuthContext.js
文件并向其中添加以下代码行:
// src/AuthContext.js
import React, {useContext} from 'react'
const AuthContext = React.createContext()
export function AuthProvider({children, value}) {
return (
<AuthContext.Provider value={value}>
{children}
</AuthContext.Provider>
)
}
export function useAuthValue(){
return useContext(AuthContext)
}
在上面的代码中,我们创建了一个上下文AuthContext
,我们还创建了另外两个函数,它们可以让我们轻松使用上下文 APIAuthProvider
和useAuthValue
.
该AuthProvider
函数允许我们将用户状态的值分享给AuthContext.Provider
while的所有孩子,useAuthValue
让我们可以轻松访问传递给AuthContext.Provider
.
现在,要为 提供 children 和 value 属性AuthProvider
,请将App.js
文件修改为如下所示:
// src/App.js
// ...
import {useState} from 'react'
import {AuthProvider} from './AuthContext'
function App() {
const [currentUser, setCurrentUser] = useState(null)
return (
<Router>
<AuthProvider value={{currentUser}}>
<Switch>
</Switch>
</AuthProvider>
</Router>
);
}
export default App;
在这里,我们AuthProvider
围绕由App
. 这样,currentUser
提供给的值AuthProvider
将可供我们应用程序中除App
组件之外的所有组件使用。
就设置 Context API 而言就是这样!要使用它,我们必须导入该useAuthValue
函数并在 的任何子组件中调用它AuthProvider
,例如Login
. 代码看起来像这样:
import { useAuthValue } from "./AuthContext"
function childOfAuthProvider(){
const {currentUser} = useAuthValue()
console.log(currentUser)
return
}
现在,currentUser
将永远是null
因为我们没有将其值设置为任何东西。要设置它的值,我们需要首先从 Firebase 获取当前用户,这可以通过使用在我们的firebase.js
文件 ( auth.currentUser
) 中初始化的 auth 实例或onAuthStateChanged
函数来完成,这实际上恰好是获取当前用户的推荐方式用户。这样,当我们获取当前用户时,我们确保 Auth 对象不处于中间状态(例如初始化)。
在App.js
文件中,添加一个useEffect
导入以及useState
以下导入:
// src/App.js
import {useState, useEffect} from 'react'
import {auth} from './firebase'
import {onAuthStateChanged} from 'firebase/auth'
currentUser
现在在 App 组件中的状态之后添加以下代码行:
// src/App.js
// ...
useEffect(() => {
onAuthStateChanged(auth, (user) => {
setCurrentUser(user)
})
}, [])
// ...
在上面的代码中,我们获取当前用户并将其设置为组件渲染时的状态。现在,当我们注册用户时,currentUser
将使用包含用户信息的对象设置状态。
向注册用户发送验证电子邮件
一旦用户注册,我们希望他们在能够访问我们网站的主页之前验证他们的电子邮件地址。我们可以为此使用该sendEmailVerification
功能。它只需要一个参数,即当前注册用户的对象。调用时,Firebase 会向注册用户的电子邮件地址发送一封电子邮件,其中包含用户可以验证其电子邮件的链接。
让我们转到Register.js
文件并修改Link
和createUserWithEmailAndPassword
导入,如下所示:
// src/Register.js
import {useHistory, Link} from 'react-router-dom'
import {createUserWithEmailAndPassword, sendEmailVerification} from 'firebase/auth'
在上面的代码中,我们还导入了useHistory
钩子。这将帮助我们访问和操作浏览器的历史,简而言之,这意味着我们可以使用它在应用程序的页面之间切换。但在我们可以使用它之前,我们需要调用它,所以让我们在error
状态之后添加以下代码行:
// src/Register.js
// ...
const history = useHistory()
// ...
现在,修改函数的.then
方法,createUserWithEmailAndPassword
如下所示:
// src/Register.js
// ...
.then(() => {
sendEmailVerification(auth.currentUser)
.then(() => {
history.push('/verify-email')
}).catch((err) => alert(err.message))
})
// ...
这里发生的情况是,当用户注册一个有效的电子邮件地址时,他们将收到一封验证电子邮件,然后被带到验证电子邮件页面。
在这个页面上我们需要做几件事:
- 在“验证电子邮件已发送至:”部分之后显示用户的电子邮件
- 使“重新发送电子邮件”按钮起作用
- 创建用于在单击“重新发送电子邮件”按钮 60 秒后禁用该按钮的功能
- 验证电子邮件后,将用户带到他们的个人资料页面
我们将首先显示注册用户的电子邮件。这需要使用AuthContext
我们之前创建的。在VerifyEmail.js
文件中,添加以下导入:
// src/VerifyEmail.js
import {useAuthValue} from './AuthContext'
return
然后,在组件中的语句前添加以下代码VerifyEmail
:
// src/VerifyEmail.js
const {currentUser} = useAuthValue()
现在,要显示电子邮件,请在语句中的<br/>
标记后添加以下代码。return
// src/VerifyEmail.js
// ...
<span>{currentUser?.email}</span>
// ...
在上面的代码中,我们使用可选链接来获取用户的电子邮件,这样当电子邮件为空时,我们的代码将不会抛出错误。
现在,当我们刷新verify-email页面时,我们应该会看到注册用户的电子邮件。
让我们转到下一个使“重新发送电子邮件”按钮起作用的事情。首先,让我们进行必要的导入。将以下导入添加到VerifyEmail.js
文件中:
// src/VerifyEmail.js
import {useState} from 'react'
import {auth} from './firebase'
import {sendEmailVerification} from 'firebase/auth'
现在,让我们添加一个状态,该状态将负责根据是否已发送验证电子邮件来禁用和启用“重新发送电子邮件”按钮。此代码currentUser
在VerifyEmail
组件中执行:
// src/VerifyEmail.js
const [buttonDisabled, setButtonDisabled] = useState(false)
对于处理重新发送验证电子邮件和禁用/启用按钮的功能,我们需要在buttonDisabled
状态之后:
// src/VerifyEmail.js
// ...
const resendEmailVerification = () => {
setButtonDisabled(true)
sendEmailVerification(auth.currentUser)
.then(() => {
setButtonDisabled(false)
}).catch((err) => {
alert(err.message)
setButtonDisabled(false)
})
}
// ...
接下来,在return
语句中,修改Resend Email按钮,如下所示:
// ...
<button
onClick={resendEmailVerification}
disabled={buttonDisabled}
>Resend Email</button>
// ...
现在,如果我们转到验证电子邮件页面并单击按钮,则会向我们发送另一封电子邮件。但是我们如何创建此功能存在问题,因为如果我们尝试在不到一分钟的时间内再次单击该按钮,我们会从 Firebase 收到一个错误,说我们发送了太多请求。这是因为 Firebase 在能够向同一地址发送另一封电子邮件之前有一分钟的时间间隔。这是我们需要解决的问题。
我们需要做的是在发送验证电子邮件后使按钮保持禁用状态 60 秒(或更长时间)。我们可以通过在“重新发送电子邮件”按钮中显示倒数计时器来稍微增强用户体验,让用户知道该按钮只是暂时禁用。
在VerifyEmail.js
文件中,添加一个useEffect
导入:
import {useState, useEffect} from 'react'
接下来,在状态后添加以下内容buttonDisabled
:
// src/VerifyEmail.js
const [time, setTime] = useState(60)
const [timeActive, setTimeActive] = useState(false)
在上面的代码中,我们创建了一个time
用于 60 秒倒计时的timeActive
状态,以及一个用于控制何时开始倒计时的状态。
在我们刚刚创建的状态之后添加以下代码行:
// src/VerifyEmail.js
// ...
useEffect(() => {
let interval = null
if(timeActive && time !== 0 ){
interval = setInterval(() => {
setTime((time) => time - 1)
}, 1000)
}else if(time === 0){
setTimeActive(false)
setTime(60)
clearInterval(interval)
}
return () => clearInterval(interval);
}, [timeActive, time])
// ...
在上面的代码中,我们创建了一个只在or状态改变useEffect
时运行的钩子。在这个钩子中,我们使用该方法将状态的前一个值每秒减一,然后当状态的值等于 0 时停止递减状态。timeActive
time
time
setInterval
time
由于useEffect
钩子依赖于timeActive
andtime
状态,因此必须在倒计时开始之前更改这些状态之一。更改time
状态不是一种选择,因为只有在发送了验证电子邮件后才能开始倒计时。所以,相反,我们需要改变timeActive
状态。
在resendEmailVerification
函数中,将 的.then
方法修改sendEmailVerification
为如下所示:
// src/VerifyEmail.js
// ...
.then(() => {
setButtonDisabled(false)
setTimeActive(true)
})
// ...
现在,当发送电子邮件时,timeActive
状态将变为true,并且开始倒计时。在上面的代码中,我们需要更改禁用按钮的方式,因为当倒计时处于活动状态时,我们需要禁用按钮。
我们很快就会这样做,但现在,让我们让倒数计时器对用户可见。将“重新发送电子邮件”按钮修改为如下所示:
// src/VerifyEmail.js
<button
onClick={resendEmailVerification}
disabled={buttonDisabled}
>Resend Email {timeActive && time}</button>
要在倒计时处于活动状态时保持按钮处于禁用状态,我们disabled
将按钮的属性修改为如下所示:
disabled={timeActive}
这样,在发送验证电子邮件时,该按钮将被禁用一分钟。现在我们可以继续buttonDisabled
从我们的代码中删除状态。
虽然这个功能有效,但我们如何实现它仍然存在一个问题:当用户注册并在他们尚未收到电子邮件时被带到验证电子邮件页面时,他们可能会尝试单击重新发送电子邮件按钮,如果他们在不到一分钟的时间内完成了这项工作,Firebase 将再次出错,因为我们发出了太多请求。
为了解决这个问题,我们需要在向新注册的用户发送电子邮件后禁用重新发送电子邮件按钮 60 秒。这意味着我们需要一种方法来更改组件内的状态。我们也可以为此使用 Context API。它将允许我们全局操作和访问状态。timeActive
Register
timeActive
让我们对我们的代码进行一些修改以使事情正常工作。在VerifyEmail
组件中,剪切timeActive
状态,粘贴到状态App
后的组件中currentUser
。
// src/App.js
function App() {
// ...
const [timeActive, setTimeActive] = useState(false)
// ...
接下来,将timeActive
and放入value propsetTimeActive
的对象中。AuthProvider
它应该如下所示:
// src/App.js
// ...
<AuthProvider value={{currentUser, timeActive, setTimeActive}}>
// ...
timeActive
现在我们可以setTimeActive
在AuthProvider
. 要修复我们代码中的错误,请转到VerifyEmail.js
文件timeActive
并setTimeActive
从以下位置解构useAuthProvider
:
// src/VerifyEmail.js
const {timeActive, setTimeActive} = useAuthValue()
timeActive
现在,要在向注册用户发送验证电子邮件后更改状态,请在Register.js
文件中添加以下导入:
// src/Register.js
import {useAuthValue} from './AuthContext'
接下来,使用此代码段setTimeActive
从组件useAuthValue
中的其他状态中Register
解构:
// src/Register.js
const {setTimeActive} = useAuthValue()
最后,在register
函数中,使用以下方法设置timeActive
状态:.then
sendEmailVerification
// src/Register.js
// ...
.then(() => {
setTimeActive(true)
history.push('/verify-email')
})
// ...
这样,用户将能够发送验证电子邮件,而不会从 Firebase 收到任何错误。
关于用户验证的最后一件事是在用户验证电子邮件后将其带到他们的个人资料页面。为此,我们将在对象中使用一个reload
函数。currentUser
它允许我们重新加载来自 Firebase 的用户对象,这样我们就会知道什么时候发生了变化。
首先,让我们进行所需的导入。在VerifyEmail.js
文件中,让我们添加以下内容:
// src/VerifyEmail.js
import {useHistory} from 'react-router-dom'
我们正在导入useHistory
,以便我们可以用来将用户导航到个人资料页面。接下来,在状态之后添加以下代码行:
// src/VerifyEmail.js
const history = useHistory()
history
最后,在变量之后添加以下代码行:
// src/VerifyEmail.js
// ...
useEffect(() => {
const interval = setInterval(() => {
currentUser?.reload()
.then(() => {
if(currentUser?.emailVerified){
clearInterval(interval)
history.push('/')
}
})
.catch((err) => {
alert(err.message)
})
}, 1000)
}, [history, currentUser])
// ...
在上面的代码中,我们reload
每隔一秒运行一次函数,直到用户的电子邮件得到验证,如果验证了,我们将用户导航到他们的个人资料页面。
为了测试这一点,让我们按照从 Firebase 发送的电子邮件中的说明来验证我们的电子邮件。如果一切顺利,我们将自动进入我们的个人资料页面。
现在,个人资料页面没有显示任何用户数据,并且他的退出链接不起作用。那是你的下一个任务。
在用户个人资料页面上工作
让我们从显示电子邮件和电子邮件验证值开始。为此,我们将利用 中的currentUser
状态AuthContext
。我们需要做的是导入useAuthValue
,currentUser
从中解构,然后显示来自用户对象的电子邮件和电子邮件验证值。
Profile.js
文件应该如下所示:
// src/Profile.js
import './profile.css'
import {useAuthValue} from './AuthContext'
function Profile() {
const {currentUser} = useAuthValue()
return (
<div className='center'>
<div className='profile'>
<h1>Profile</h1>
<p><strong>Email: </strong>{currentUser?.email}</p>
<p>
<strong>Email verified: </strong>
{`${currentUser?.emailVerified}`}
</p>
<span>Sign Out</span>
</div>
</div>
)
}
export default Profile
有了这个,电子邮件和电子邮件验证值现在应该显示在我们的个人资料页面上。
为了使退出功能正常工作,我们将使用该signOut
功能。它只需要一个参数,即auth
实例。所以,在Profile.js
. 让我们添加这些进口。
// src/Profile.js
import { signOut } from 'firebase/auth'
import { auth } from './firebase'
现在,在return
语句中,修改<span>
包含“Sign Out”的内容,以便signOut
在单击时调用该函数:
// src/Profile.js
// ...
<span onClick={() => signOut(auth)}>Sign Out</span>
// ...
为 Profile 组件创建私有路由
现在,即使使用未经验证的电子邮件地址,用户也可以访问个人资料页面。我们不希望那样。未经验证的用户在尝试访问配置文件时应该被重定向到登录页面。这就是私人路线的用武之地。
在src
目录中,让我们创建一个新PrivateRoute.js
文件并将以下代码添加到其中:
// src/PrivateRoute.js
import {Route, Redirect} from 'react-router-dom'
import {useAuthValue} from './AuthContext'
export default function PrivateRoute({component:Component, rest}) {
const {currentUser} = useAuthValue()
return (
<Route
{rest}
render={props => {
return currentUser?.emailVerified ? <Component {props} /> : <Redirect to='/login' />
}}>
</Route>
)
}
这PrivateRoute
几乎与使用Route
. 不同之处在于,如果他们的电子邮件未经验证,我们将使用render
道具将用户重定向到个人资料页面。
我们希望个人资料页面是私有的,所以导入PrivateRoute
:
// src/App.js
import PrivateRoute from './PrivateRoute'
然后我们可以在组件中替换Route
with 。路线现在应该如下所示:PrivateRoute
Profile
Profile
// src/App.js
<PrivateRoute exact path="/" component={Profile} />
好的!我们已使个人资料页面仅对拥有经过验证的电子邮件的用户开放。
创建登录功能
由于只有邮箱经过验证的用户才能在使用该signInWithEmailAndPassword
功能登录时访问他们的个人资料页面,所以我们还需要检查他们的电子邮件是否已验证,如果未验证,则应将用户重定向到验证电子邮件页面,其中 60 -秒倒计时也应该开始。
这些是我们需要添加到Login.js
文件中的导入:
import {signInWithEmailAndPassword, sendEmailVerification} from 'firebase/auth'
import {auth} from './firebase'
import {useHistory} from 'react-router-dom'
import {useAuthValue} from './AuthContext'
Login
接下来,在组件的状态中添加以下代码行。
// src/Login.js
const {setTimeActive} = useAuthValue()
const history = useHistory()
然后在变量后添加以下函数history
:
// src/Login.js
// ...
const login = e => {
e.preventDefault()
signInWithEmailAndPassword(auth, email, password)
.then(() => {
if(!auth.currentUser.emailVerified) {
sendEmailVerification(auth.currentUser)
.then(() => {
setTimeActive(true)
history.push('/verify-email')
})
.catch(err => alert(err.message))
}else{
history.push('/')
}
})
.catch(err => setError(err.message))
}
// ...
这会登录一个用户,然后检查他们是否经过验证。如果他们通过验证,我们会将他们导航到他们的个人资料页面。但如果它们未经验证,我们会发送一封验证电子邮件,然后将它们重定向到验证电子邮件页面。
完成这项工作所需要做的就是在login
提交表单时调用该函数。因此,让我们将 的开始标签修改为login_form
:
// src/Login.js
<form onSubmit={login} name='login_form'>
而且,嘿,我们完成了!
结论
在本教程中,我们学习了如何使用 Firebase 身份验证版本 9 在 React 中构建功能齐全的用户注册和身份验证服务。是不是超级容易?不,我们需要处理一些事情。但这比从头开始构建我们自己的服务要容易得多吗?你敢打赌!这就是我希望你从阅读本文中得到的。