使用 firebase 验证用户身份并做出反应。
在本文中,我们将使用 Firebase 进行基本的用户身份验证。如果您有其他类型的用户身份验证经验,您可能会感到沮丧。
Firebase 确实有一个学习曲线,但与其他替代方案相比,我发现它的学习曲线较小。
Firebase 将承担许多繁重的后端功能
如果你想看看这个应用程序的功能,这里是“完成”的产品,你可以在这里
本教程为何有用?
这就是如何利用 firebase,这样您就不必创建自己的后端、加密用户密码或经历部署后端应用程序的麻烦。
先决条件:
-
了解 JavaScript,包括如何将参数传递给函数和异步代码。
-
了解 react、context、hooks 与create-react-app。
-
您选择的文本编辑器。(我将使用vscode)
-
对命令行有基本的了解。
-
git 知识。
可选:bash 命令行/Mac OS。您可以不用它,但在本教程中我会使用它。
首先,访问https://firebase.com创建一个新的 firebase 项目。
单击一个新项目。
单击“我的第一个项目”,然后您可以随意命名您的项目。
单击继续。
您可以选择不使用 Google Analytics,它不会干扰本教程,我将其保留,因此您将看到启用它的部分代码。
单击继续。
系统将提示您选择一个帐户。
选择默认帐户,然后单击创建项目。
您现在应该可以看到这一点。
您应该在 Firebase 控制台中执行此项目。
点击左侧导航上的身份验证。
点击设置登录方式。
有很多方法可以设置用户登录我们的应用。在本教程中,我们将使用最简单的方法。
点击电子邮件和密码。
单击启用。
节省。
确保它确实已启用。
现在转到项目概述。
我们需要获取有关我们的应用如何发送和接收 Firebase 数据的信息,因此我们必须获取以 SDK 形式提供给我们的 API 密钥和其他敏感信息。
单击括号即可开始。
时,最好的习惯是确保黑客无法访问您的 API 密钥、加密技术或其他用户的敏感信息。
dotenv 允许您将敏感信息保存为环境范围变量,这样您就无法发布到远程存储库,但仍可以在您的应用程序中使用。
在命令行上运行 npm install 来安装所有软件包
专业提示:在运行 npm install 之前,请确保您位于项目的根目录中
npm install firebase dotenv react-router-dom
现在打开项目。
我正在使用 vscode,所以这是从命令行执行的操作。
code .
查看 package.json 文件,您应该会看到您安装的包。
在应用程序中移动 SDK firebase。
在将 SDK 复制粘贴到我们的文件之前,最好将 .env 文件添加到 .gitignore 中,这样就不会将环境变量发布到 GitHub。很容易忘记。
然后将 API 密钥添加到 .env
然后从我们即将创建的 firebaseIndex.js 中引用它们到 .env 文件中。
这样,在学习本教程时,您就不会面临泄露密钥的危险。
点击你的 .gitignore
在文件中的任何位置写入 .env
然后右键单击根目录中的空白处。(如果没有,您可以最小化轮廓以显示空间。)
将以下变量复制并粘贴到 .env 文件中
REACT_APP_API_KEY=
REACT_APP_AUTHDOMAIN=
REACT_APP_BASEURL=
REACT_APP_PROJECT_ID=
REACT_APP_STORAGEBUCKET=
REACT_APP_MESSAGING_SENDER_ID=
REACT_APP_APP_ID=
REACT_APP_MEASUREMENT_ID=
包括引号在内,从 SDK 中逐一复制并粘贴信息。API 密钥、授权域、baseurl 等……
你应该有这样的东西。
来自 firebase 的信息。
REACT_APP_API_KEY="your secret api key"
REACT_APP_AUTHDOMAIN="your secret authdomain"
REACT_APP_BASEURL="your secret baseurl"
REACT_APP_PROJECT_ID="your secret projectid"
REACT_APP_STORAGEBUCKET="your secret storeagebucket"
REACT_APP_MESSAGING_SENDER_ID="your secret messaging sender id"
REACT_APP_APP_ID="your secret app id"
REACT_APP_MEASUREMENT_ID="your secret measurment id"
现在是简单的部分。
首先创建文件夹来保存 firebases SDK 和用于身份验证的辅助方法。
尝试从文本编辑器中执行此操作。
右键单击 src 文件夹,然后单击新建文件夹。
将文件夹命名为 firebase。
现在右键单击 firebase 文件夹并添加 firebaseIndex.js
firebaseIndex.js。
在 firebaseIndex.js 文件的顶部导入 firebase 以及您想要的功能。
import firebase from 'firebase'
import 'firebase/auth'
import 'firebase/app'
现在您的环境变量已在应用程序范围内设置,您可以复制并粘贴此 SDK,使用我提供的代码在 firebaseIndex 文件中引用您的敏感数据。
var firebaseConfig = {
apiKey: process.env.REACT_APP_API_KEY,
authDomain: process.env.REACT_APP_AUTHDOMAIN,
databaseURL: process.env.REACT_APP_BASEURL,
projectId: process.env.REACT_APP_PROJECT_ID,
storageBucket: process.env.REACT_APP_STORAGEBUCKET,
messagingSenderId: process.env.REACT_APP_MESSAGING_SENDER_ID,
appId: process.env.REACT_APP_APP_ID,
measurementId: process.env.REACT_APP_MEASUREMENT_ID
};
// Initialize Firebase
firebase.initializeApp(firebaseConfig);
firebase.analytics();
在 analytics() 方法下添加 firebase.auth() 辅助方法。
firebase.auth()
我们需要另一个文件中的 firebaseConfig 对象,因此需要将其导出
export default {
firebaseConfig,
}
整个文件应该看起来像这样。
import firebase from 'firebase'
import 'firebase/auth'
import 'firebase/app'
var firebaseConfig = {
apiKey: process.env.REACT_APP_API_KEY,
authDomain: process.env.REACT_APP_AUTHDOMAIN,
databaseURL: process.env.REACT_APP_BASEURL,
projectId: process.env.REACT_APP_PROJECT_ID,
storageBucket: process.env.REACT_APP_STORAGEBUCKET,
messagingSenderId: process.env.REACT_APP_MESSAGING_SENDER_ID,
appId: process.env.REACT_APP_APP_ID,
measurementId: process.env.REACT_APP_MEASUREMENT_ID
};
// Initialize Firebase
firebase.initializeApp(firebaseConfig);
firebase.analytics();
firebase.auth()
export default {
firebaseConfig,
}
如果您按照这些步骤操作,您可以随时推送到 github,并且它不会保存您的密钥。
添加身份验证方法。
在您的 firebase 文件夹中创建一个名为 auth methods 的文件,这是保存包含 signin、signup、signout 功能的对象的地方。
在顶部导入两样东西,firebaseConfig 对象和来自 firebase 的 firebase,像这样。
import firebaseconfig from './firebaseIndex'
import firebase from 'firebase'
现在进行导出并创建一个身份验证方法对象。
export const authMethods = {
// firebase helper methods go here...
}
我们将把它发送到上下文中,它将成为链接到登录表单的一系列方法的顶部。
这些将是键/值对,我们为其提供用于登录的匿名函数。
export const authMethods = {
// firebase helper methods go here...
signup: (email, password) => {
},
signin: (email, password) => {
},
signout: (email, password) => {
},
}
我第一次看到它的时候,感觉很不寻常。等我们结合上下文再来理解它,就会明白很多了。
这是来自用户身份验证的 firebase 文档。
signup: (email, password) => {
firebase.auth().createUserWithEmailAndPassword(email,password)
.then(res => {
console.log(res)
})
.catch(err => {
console.error(err)
})
},
在开始添加其他方法之前,我想测试一下这段代码是否有效。
为此,请构建上下文和注册表单,然后查看 Firebase 是否会响应。
为我们的应用程序创建上下文。
右键单击 src 文件夹并创建一个名为 provider 的新文件夹。
右键单击提供程序并创建一个名为 AuthProvider.js 的文件
制作一个功能组件,添加道具。
import React from 'react';
const AuthProvider = (props) => {
return (
<div>
</div>
);
};
export default AuthProvider;
在函数外部,创建一个 firebaseAuth 变量并使其等于反应上下文。
export const firebaseAuth = React.createContext()
我们必须导出它以便我们可以访问 useContext 钩子。
删除 div 标签并使提供程序位于 AuthProvider 的返回内部,我不会解释这里发生的所有事情,但如果您想了解有关上下文的更多信息,这是一篇我解释上下文和 useContext 钩子的文章。
const AuthProvider = (props) => {
return (
<firebaseAuth.Provider
value={{
test: "context is working"
}}>
{props.children}
</firebaseAuth.Provider>
);
};
AuthProvider.js
现在我们需要将 App.js 包装在 index.js 文件中的 AuthProvider 组件中。
我们还需要导入动态路由组件的能力,因为我们已经在这个文件中,从 react-router-dom 添加 BrowserRouter。
首先在顶部导入 AuthProvider 和 BrowserRouter。
import AuthProvider from './provider/AuthProvider'
import {BrowserRouter} from 'react-router-dom'
然后用 BrowserRouter 和 AuthProvider 制作一个 App 三明治。
ReactDOM.render(
<BrowserRouter>
<AuthProvider>
<App />
</AuthProvider>
</BrowserRouter>
, document.getElementById('root'));
两件事,
转到 App.js,在顶部更改 React 的导入方式以包含 useContext 和 React。
导入 {firebaseAuth} 以便我们可以像这样从中解构测试键/值对。
import React, {useContext} from 'react';
import {firebaseAuth} from './provider/AuthProvider'
在函数解构测试中从 firebaseAuth 变量进行测试。
console.log 测试。
const {test} = useContext(firebaseAuth)
console.log(test)
返回终端并启动服务器。
npm start
使用开发工具检查你应该会看到这一点。
连接到 authMethods
现在我们有了 App 范围的上下文,返回 AuthProvider.js 并导入 authMethods。
import {authMethods} from '../firebase/authmethods'
该文件是 Firebase 和我们即将制作的 Signup 组件之间的中间人,
这意味着所有状态逻辑都将存放在这里。
在 AuthProvider 内部创建一个名为 handleSignup 的函数。
const handleSignup = () => {
// middle man between firebase and signup
}
将其作为 firebaseAuth.Provider 中的值传递
<firebaseAuth.Provider
value={{
//replaced test with handleSignup
handleSignup
}}>
{props.children}
</firebaseAuth.Provider>
现在在 App.js 中使用 handleSignup 更改测试
const {handleSignup} = useContext(firebaseAuth)
console.log(handleSignup)
App.js
你应该看到
在 AuthProvider 中,将 authMethod.signup() 添加到 handleSignup。
const handleSignup = () => {
// middle man between firebase and signup
console.log('handleSignup')
// calling signup from firebase server
return authMethods.signup()
}
创建一个组件文件夹和 Signup.js 组件,在我们希望它最终出现的位置重新创建相同的功能,以便我们可以在 App.js 中定义我们的路由
制作 Signup.js
制作一个基本组件
// add useContext
import React, {useContext} from 'react';
const Signup = () => {
return (
<div>
Signup
</div>
);
};
export default Signup;
像在 App.js 中一样,从上下文中解构 handleSignup 函数
const {handleSignup} = useContext(firebaseAuth)
console.log(handleSignup)
__
在 App.js 中,通过删除样板并添加 Switch 和 Route 来添加 react-router-dom 的开头,设置由 Route 呈现的注册。
import {Route, Switch} from 'react-router-dom'
import Signup from './component/Signup'
App.js
return (
<>
{/* switch allows switching which components render. */}
<Switch>
{/* route allows you to render by url path */}
<Route exact path='/' component={Signup} />
</Switch>
</>
);
如果一切顺利,您应该会看到一个带有注册信息的白色屏幕。
制作一个注册表。
return (
<form>
{/* replace the div tags with a form tag */}
Signup
{/* make inputs */}
<inputs />
<button>signup</button>
</form>
);
此时,可能很想在这里建立状态。但我们希望上下文成为唯一的事实来源,以便如果用户在登录和注册之间切换,他们输入的任何内容都将保留下来。
返回 AuthProvider 并开始设置状态。
我们需要来自 Firebase 的令牌和用户数据的状态。
在 React 旁边导入 useState。
import React, {useState} from 'react';
AuthProvider.js
我们想要的状态将会实现。
-
token 为 null (一旦我们从 firebase 获取 token 则为字符串),有关json web token 的更多信息。
-
输入为包含电子邮件和密码两个字符串的对象。
-
错误作为数组,以便可以向用户显示错误消息。
将这些状态添加到 AuthProvider.js
const [inputs, setInputs] = useState({email: '', password: ''})
const [errors, setErrors] = useState([])
const [token, setToken] = useState(null)
将输入添加到提供者的值对象。
<firebaseAuth.Provider
value={{
//replaced test with handleSignup
handleSignup,
inputs,
setInputs,
}}>
在 Signup.js 中,使用 useContext 钩子从 authContext 获取它们,如下所示。
const {handleSignup, inputs, setInputs} = useContext(firebaseAuth)
将 handleChange 和 handleSubmit 函数作为基本形式。
const handleSubmit = (e) => {
e.preventDefault()
console.log('handleSubmit')
}
const handleChange = e => {
const {name, value} = e.target
console.log(inputs)
setInputs(prev => ({...prev, [name]: value}))
}
更改表单和输入字段以使用表单功能。
<form onSubmit={handleSubmit}>
{/* replace the div tags with a form tag */}
Signup
{/* make inputs */}
<input onChange={handleChange} name="email" placeholder='email' value={inputs.email} />
<input onChange={handleChange} name="password" placeholder='password' value={inputs.password} />
<button>signup</button>
</form>
如果您正确地完成了所有操作并运行了如下测试...
这是您可能收到的错误消息。
我们收到此错误的原因是我们没有向 authMethods.signup 传递它期望的电子邮件和密码参数。
将inputs.email和inputs.password传递到authMethods.signin
authMethods.signup(inputs.email, inputs.password)
当你做这样的测试时。
你应该会得到这样的回应。
但如果你尝试执行两次,就会出现错误。
这是因为您不能重复此操作。所有电子邮件都必须是唯一的。
为了使错误消息显示给用户,我们必须执行以下操作。
- 在 AuthProvider.js 中,将 setErrors 作为参数与电子邮件和密码一起传递,
这是我能想出的唯一方法。每当你必须向函数传递多个参数时,你都应该有一个很好的理由。
-
在 authMethods.js 的 signup() 中,在顶部添加第三个参数,并在 .catch 中,我们将错误消息保存到错误数组中。
-
通过将错误传递给 Signup.js 并通过数组映射,将错误显示到屏幕上。
1.
//sending setErrors
authMethods.signup(inputs.email, inputs.password, setErrors)
console.log(errors)
现在添加 setErrors 消息以及电子邮件和密码。
AuthProvider.js
2.
//catching setErrors
signup: (email, password, setErrors) => {
authMethods.js
如果出现多个错误,请将 catch 更改为 setErrors 包括 prev
.catch(err => {
//saving error messages here
setErrors(prev => ([...prev, err.message]))
})
如果它起作用并且您在控制台记录了它,您应该会看到这个错误。
- 将错误添加到 Provider 的值对象中
<firebaseAuth.Provider
value={{
//replaced test with handleSignup
handleSignup,
inputs,
setInputs,
//added errors to send to Signup.js
errors,
}}>
{props.children}
</firebaseAuth.Provider>
AuthProvider.js
从 Signup.js 中的 useContext 中解构它
const {handleSignup, inputs, setInputs, errors} = useContext(firebaseAuth)
Signup.js
现在添加一个三元组,只有发生错误时才会显示。
<button>signup</button>
{errors.length > 0 ? errors.map(error => <p style={{color: 'red'}}>{error}</p> ) : null}
</form>
如果一切正常,您将在屏幕上看到错误。
如果您想过滤重复项,您可以找出或看看我在 repo 上的表现,但本教程很长,还有一些事情要做。
这样您就可以为每个帐户启用多个电子邮件。
进入该项目内的firebase,单击身份验证。
点击登录方法
滚动到底部,那里用黑色小字母写着“高级”。它用粗体字写着“每个电子邮件一个帐户”。
点击蓝色的更改按钮
单击允许使用同一电子邮件的多个帐户。
这将帮助我们更快地进行测试,但不要忘记稍后将其切换回来。
-
与我们设置错误的方式相同,我们将把令牌保存到 localStorage,并将令牌的状态保存在 AuthProvider 中。
-
使得我们只有拥有令牌才能看到某些组件。
-
如果本地存储中的令牌与状态中的令牌匹配,则重定向到该页面。
-
重复该过程以进行登录。
-
使用登录方法擦除令牌并将用户推出我们应用程序的经过身份验证的部分。
-
转到 AuthProvider.js 并在 setErrors 之后添加 setToken 作为另一个参数。
//sending setToken function to authMethods.js
authMethods.signup(inputs.email, inputs.password, setErrors, setToken)
console.log(errors, token)
AuthProvider.js
将其作为第四个参数添加到顶部。
// added the 4th argument
signup: (email, password, setErrors, setToken) => {
在 .then 里面,在 console.log(res) 下面...
我将为您节省大量时间,您无需花费大量时间挖掘 res 对象来查找令牌。
这也会使异步代码变得有点混乱。
signup: (email, password, setErrors, setToken) => {
firebase.auth().createUserWithEmailAndPassword(email,password)
//make res asynchronous so that we can make grab the token before saving it.
.then( async res => {
const token = await Object.entries(res.user)[5][1].b
//set token to localStorage
await localStorage.setItem('token', token)
//grab token from local storage and set to state.
setToken(window.localStorage.token)
console.log(res)
})
.catch(err => {
setErrors(prev => ([...prev, err.message]))
})
},
authMethods.js
现在,如果你再创建一个帐户并进入浏览器开发工具
_ 2. 签到 _
我们将复制并粘贴大量用于注册的内容,并轻松配置登录。
我们将从组件树的底部开始,逐个文件地稍微修改 Signin 组件,直到它在 authMethods 中正常工作。
首先创建一个名为 Signin.js 的新文件
将所有内容从 Signup.js 复制并粘贴到 Signin.js
突出显示所有显示“注册”的地方,并将其更改为“登录”
如果您使用的是 Mac,请点击 React 组件的名称,然后按 Command + d。否则,您可以使用 ctrl + f 并在顶部输入。
我只有 3 个字,记得使用相同的方法将 handleSignup 更改为 handleSignin。
也改变按钮。
现在转到 App.js 并导入文件。
import Signin from './component/Signin'
确保导入的组件文件夹是单数。
为登录添加新路线
<Route exact path='/' component={Signup} />
<Route exact path='/signin' component={Signin} />
如果您输入http://localhost:3000/signin,您的登录组件现在将会呈现,但是一旦您单击按钮,它就会崩溃,因为没有 handleSignin 函数。
为了解决这个问题,我们可以转到 AuthProvider.js 并复制并粘贴更改措辞,就像我们对注册所做的那样。然后将 handleSignin 函数添加到值对象。
const handleSignin = () => {
//changed to handleSingin
console.log('handleSignin!!!!')
// made signup signin
authMethods.signin(inputs.email, inputs.password, setErrors, setToken)
console.log(errors, token)
}
现在将该函数添加到 firebaseAuth.Provider
<firebaseAuth.Provider
value={{
//replaced test with handleSignup
handleSignup,
handleSignin,
inputs,
setInputs,
errors,
}}>
{props.children}
</firebaseAuth.Provider>
AuthProvider.js
现在转到 authMethods.js 并执行类似操作,而不是 createUserWithEmailAndPassword,更改为... signInWithEmailAndPassword()
signin: (email, password, setErrors, setToken) => {
//change from create users to...
firebase.auth().signInWithEmailAndPassword(email,password)
//everything is almost exactly the same as the function above
.then( async res => {
const token = await Object.entries(res.user)[5][1].b
//set token to localStorage
await localStorage.setItem('token', token)
setToken(window.localStorage.token)
console.log(res)
})
.catch(err => {
setErrors(prev => ([...prev, err.message]))
})
},
如果您没有从本地存储中删除令牌,那么令牌仍然会存在。
快到了!!
-
制作一个主页组件并只允许拥有令牌的用户到达那里。
-
制作一个注销按钮,删除令牌并使用 react-router-dom 将用户推出页面。
因为您应该已经在 authMethods.js 中了,所以这次我们将从顶部开始,然后转到底部。
与其他两种方法相比,这种方法非常简单,因为我们不使用 firebase 来保存用户的状态。
//no need for email and password
signout: (setErrors, setToken) => {
// signOut is a no argument function
firebase.auth().signOut().then( res => {
//remove the token
localStorage.removeItem('token')
//set the token back to original state
setToken(null)
})
.catch(err => {
//there shouldn't every be an error from firebase but just in case
setErrors(prev => ([...prev, err.message]))
//whether firebase does the trick or not i want my user to do there thing.
localStorage.removeItem('token')
setToken(null)
console.error(err.message)
})
},
}
转到 AuthProvider.js 并创建一个注销函数
const handleSignout = () => {
authMethods.signout()
}
将方法添加到提供程序
setInputs,
errors,
handleSignout,
现在我们需要一个组件来让它变得有用,但我们还没有这样做。
创建一个 Home.js,并在其中创建一个基本的 React 组件。
import React from 'react';
const Home = (props) => {
return (
<div>
Home
</div>
);
};
export default Home;
导入 useContext 和 firebaseAuth
import React, {useContext} from 'react';
import {firebaseAuth} from '../provider/AuthProvider'
在组件内部的 return 和 Home 之间,从 useContext 中解构 signout
const {signout,} = useContext(firebaseAuth)
在返回语句中。添加登录成功,然后添加一个在退出时调用的按钮。
return (
<div>
Home, login successful!!!!!!
<button onClick={signout}>sign out </button>
</div>
);
在我们测试它之前,我们需要回到我们的组件树并改变访问每个组件的严格程度。
在 App.js 中,我们将使用三元语句来实现这一点,即如果没有将令牌保存到状态,用户就无法进入主页组件。
在 App.js 中导入 Home 组件。
import Home from './component/Home'
使用 useContext 从 firebaseAuth 中解构令牌
const { token } = useContext(firebaseAuth)
console.log(token)
当您使用 Route 渲染 Home 组件时,添加一个三元语句来检查令牌的数据类型
这意味着以不同的方式设置“/”或根 URL。
将您的 Home 组件路由更改为使用 render prop 而不是 component prop。并更严格地指定 URL 路径。
<Route exact path='/' render={rProps => token === null ? <Signin /> : <Home />} />
<Route exact path='/signin' component={Signin} />
<Route exact path='/signup' component={Signup} />
在 AuthProvider.js 中,将令牌添加到值对象。
<firebaseAuth.Provider
value={{
//replaced test with handleSignup
handleSignup,
handleSignin,
token,
inputs,
setInputs,
errors,
handleSignout,
}}>
{props.children}
</firebaseAuth.Provider>
现在用户可以登录和退出了。最后一步,让用户注册时,react-router-dom 能够直接跳转到主页。
转到 Signup.js 并从 react-router-dom 导入 withRouter
import {withRouter} from 'react-router-dom'
将默认导出传递给 withRouter 高阶组件
export default withRouter(Signup);
向注册组件添加道具
const Signup = (props) => {
现在我们可以访问 prop.history.push("/goAnyWhereInApp")
现在使 handleSubmit 成为一个异步函数并等待 handleSignup 然后推送到根 URL。
const handleSubmit = async (e) => {
e.preventDefault()
console.log('handleSubmit')
//wait to signup
await handleSignup()
//push home
props.history.push('/')
}
您可能会有延迟,但一旦您获得凭证,它就会起作用。
如果你想发布这个视频,这里是如何使用 Surge 的。我是 Firebase 的忠实粉丝,我之所以做这些教程,是因为有一位开发者在 Heroku 上吃了不少苦头。
这是成品
这是github,如果可以的话请给它一颗星。
最后就是这样
您现在拥有一个具有强大后端功能的静态站点。
我将会做更多关于 firebase 的教程。
如果您发现本教程有用,请点赞并分享。
firebase文档很有帮助,但我这里有一些内容可以使其更容易地转换到 react 项目。
如果您有任何想说的,请在下面的评论中添加。
文章来源:https://dev.to/itnext/user-auth-with-firebase-and-react-1725