在今天的网络应用中,认证是至关重要的。这是许多开发者在过去不得不实现的一个功能。值得庆幸的是,许多库通过提供许多内置功能使这项工作变得更容易。特别是,Firebase是一个处理用户管理和认证的优秀工具。
在本教程中,我们将介绍如何使用Firebase实现认证。
什么是Firebase?
Firebase在2014年被谷歌收购,是一个提供一整套产品的平台,包括但不限于。
- 实时数据库
- 身份认证
- 云信息传递
这些产品允许开发者轻松快速地创建和运行应用程序。
创建一个Firebase账户
在你写一行代码之前,你需要一个Firebase账户。到这里来创建一个。
在Firebase中,如果你想要API密钥,你就需要创建应用程序。这些应用必须属于项目。因此,如果你还没有建立一个项目,你需要创建一个。一旦你这样做了,就创建一个应用程序来获得你的密钥。
iOS、安卓和网络是一些可用的格式。
现在,点击项目概览旁边的设置图标(在你屏幕的左上方)。在项目设置和常规下,你应该看到你的应用程序和它的配置。
在你进入代码之前,你需要启用你想使用的登录方法。要做到这一点,请点击认证,然后点击签到方法。每种方法都有不同的配置,但为了本教程的目的,我将专注于传统的电子邮件/密码方法。
启用你的签到供应商。
将本地环境添加到Next.js中
现在你有了你的钥匙,是时候把它们添加到你的Next.js项目中了。
提示:如果你没有一个已经创建的环境,不要担心。这个命令会让你开始行动。
npx create-next-app
# or
yarn create next-app
Next.js项目会自动忽略.env.local ,这要归功于它的.gitignore 文件,所以你可以复制/粘贴你的密钥,而不用担心它们会被意外地提交到GitHub。
NEXT_PUBLIC_FIREBASE_PUBLIC_API_KEY=<YOUR_API_KEY>
NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN=<YOUR_DOMAIN>
NEXT_PUBLIC_FIREBASE_PROJECT_ID=<YOUR_PROJECT_ID>
别忘了:在Next.js中,命名环境变量的惯例是,它们必须以NEXT_PUBLIC开头。
安装Firebase
继续安装Firebase库。
npm install --save Firebase
# or
yarn add Firebase
在Next.js中创建一个Firebase实例
太好了!库已经安装好了,你的API密钥也已经到手。库已经安装好了,你的API密钥也设置好了。是时候使用这些密钥来创建一个Firebase实例了。当然,Firebase有很多有用的工具,但为了这篇文章,我们将只专注于认证。因此,你只需要Firebase/auth 和apiKey,authDomain, 和projectId 的证书。
import Firebase from 'Firebase/app';
import 'Firebase/auth';
const FirebaseCredentials = {
apiKey: process.env.NEXT_PUBLIC_FIREBASE_PUBLIC_API_KEY,
authDomain: process.env.NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN,
projectId: process.env.NEXT_PUBLIC_FIREBASE_PROJECT_ID
}
// if a Firebase instance doesn't exist, create one
if (!Firebase.apps.length) {
Firebase.initializeApp(FirebaseCredentials)
}
export default Firebase;
倾听Firebase的变化
你首先需要的是一个authUser ,你可以在你的应用程序中访问它。这个变量不仅有助于用户管理,也有助于相应地重定向路线。
例如,如果authUser 是空的,意味着用户还没有登录,当这个人试图访问一个受保护的路由(例如,一个仪表板)时,你应该把他们重定向到登录页面。
值得庆幸的是,Firebase.auth 可以跟踪状态,并且有一个内置的函数叫onAuthStateChanged ,允许你监听状态变化。
当状态改变时,根据你的需要对用户进行格式化,最后,将其设置为你的authUser 变量。使用loading 变量来表示Firebase是否在获取数据。
import { useState, useEffect } from 'react'
import Firebase from './Firebase';
const formatAuthUser = (user) => ({
uid: user.uid,
email: user.email
});
export default function useFirebaseAuth() {
const [authUser, setAuthUser] = useState(null);
const [loading, setLoading] = useState(true);
const authStateChanged = async (authState) => {
if (!authState) {
setAuthUser(null)
setLoading(false)
return;
}
setLoading(true)
var formattedUser = formatAuthUser(authState);
setAuthUser(formattedUser);
setLoading(false);
};
// listen for Firebase state change
useEffect(() => {
const unsubscribe = Firebase.auth().onAuthStateChanged(authStateChanged);
return () => unsubscribe();
}, []);
return {
authUser,
loading
};
}
创建一个用户上下文
为了在你的应用程序中访问authUser 和加载变量,你将使用Context API。
提示:不熟悉React Context?不要犹豫,看看官方的文档吧。
首先,用createContext 创建你的上下文对象,并设置默认值(authUser 作为null ,加载作为true )。然后,从useFirebaseAuth 获取实际的authUser 和loading 变量,并将其传递给提供者组件。
你还应该添加一个自定义钩子,在这种情况下,useAuth ,以访问当前的上下文值。
import { createContext, useContext, Context } from 'react'
import useFirebaseAuth from '../lib/useFirebaseAuth';
const authUserContext = createContext({
authUser: null,
loading: true
});
export function AuthUserProvider({ children }) {
const auth = useFirebaseAuth();
return <authUserContext.Provider value={auth}>{children}</authUserContext.Provider>;
}
// custom hook to use the authUserContext and access authUser and loading
export const useAuth = () => useContext(authUserContext);
然后,在我们的_app.js ,将这个提供者包裹在你的应用程序中。这可以确保子组件能够访问你的用户上下文。
import { AuthUserProvider } from '../context/AuthUserContext';
function MyApp({ Component, pageProps }) {
return <AuthUserProvider><Component {...pageProps} /></AuthUserProvider>
}
export default MyApp
创建受保护的路由
受保护的路由是你的应用程序的页面或部分,应该只被某些用户访问。在这种情况下,只有登录的用户才能访问这些内容。要设置这个,从你的自定义useAuth() 钩子中获取authUser 和loading 。
有了这些变量,检查Firebase是否还在获取数据(即加载的是true ),如果不是,authUser 是否是null 。如果是这样的话,那么用户就没有登录,你应该把他们重定向到登录页面。
在你的应用程序中进行测试,确保重定向是正确发生的。
import { useEffect } from 'react';
import { useRouter } from 'next/router';
import { useAuth } from '../context/AuthUserContext';
import {Container, Row, Col} from 'reactstrap';
const LoggedIn = () => {
const { authUser, loading } = useAuth();
const router = useRouter();
// Listen for changes on loading and authUser, redirect if needed
useEffect(() => {
if (!loading && !authUser)
router.push('/')
}, [authUser, loading])
return (
//Your logged in page
)
}
export default LoggedIn;
在Next.js中添加登录、注册和注销的功能
现在,让我们来看看多汁的部分。Firebase的一个伟大之处在于它有许多内置的功能,用于登录、创建用户和注销。
所以,让我们把它们添加到useFirebaseAuth函数中。使用Firebase.auth() 来访问不同的函数(signInWithEmailAndPassword,createUserWithEmailAndPassword, 和signOut )。
export default function useFirebaseAuth() {
// ...
const clear = () => {
setAuthUser(null);
setLoading(true);
};
const signInWithEmailAndPassword = (email, password) =>
Firebase.auth().signInWithEmailAndPassword(email, password);
const createUserWithEmailAndPassword = (email, password) =>
Firebase.auth().createUserWithEmailAndPassword(email, password);
const signOut = () =>
Firebase.auth().signOut().then(clear);
useEffect(() => {
const unsubscribe = Firebase.auth().onAuthStateChanged(authStateChanged);
return () => unsubscribe();
}, []);
return {
authUser,
loading,
signInWithEmailAndPassword,
createUserWithEmailAndPassword,
signOut
};
}
不要忘记更新你的上下文文件中的默认值。
const authUserContext = createContext({
authUser: null,
loading: true,
signInWithEmailAndPassword: async () => {},
createUserWithEmailAndPassword: async () => {},
signOut: async () => {}
});
export function AuthUserProvider({ children }) {
const auth = useFirebaseAuth();
return <authUserContext.Provider value={auth}>{children}</authUserContext.Provider>;
}
创建注册页面
在你的注册页面中,使用你的useAuth hook ,再次检索你的函数来创建一个用户。createUserWithEmailAndPassword ,需要两个参数:电子邮件和密码。
完成表单验证后,调用这个函数。如果它成功返回一个authUser ,那么你可以相应地重定向用户。
import { useState } from 'react';
import { useRouter } from 'next/router';
import { useAuth } from '../context/AuthUserContext';
import {Container, Row, Col, Button, Form, FormGroup, Label, Input, Alert} from 'reactstrap';
const SignUp = () => {
const [email, setEmail] = useState("");
const [passwordOne, setPasswordOne] = useState("");
const [passwordTwo, setPasswordTwo] = useState("");
const router = useRouter();
const [error, setError] = useState(null);
const { createUserWithEmailAndPassword } = useAuth();
const onSubmit = event => {
setError(null)
//check if passwords match. If they do, create user in Firebase
// and redirect to your logged in page.
if(passwordOne === passwordTwo)
createUserWithEmailAndPassword(email, passwordOne)
.then(authUser => {
console.log("Success. The user is created in Firebase")
router.push("/logged_in");
})
.catch(error => {
// An error occurred. Set error message to be displayed to user
setError(error.message)
});
else
setError("Password do not match")
event.preventDefault();
};
return (
<Container className="text-center custom-container">
<Row>
<Col>
<Form
className="custom-form"
onSubmit={onSubmit}>
{ error && <Alert color="danger">{error}</Alert>}
<FormGroup row>
<Label for="signUpEmail" sm={4}>Email</Label>
<Col sm={8}>
<Input
type="email"
value={email}
onChange={(event) => setEmail(event.target.value)}
name="email"
id="signUpEmail"
placeholder="Email" />
</Col>
</FormGroup>
<FormGroup row>
<Label for="signUpPassword" sm={4}>Password</Label>
<Col sm={8}>
<Input
type="password"
name="passwordOne"
value={passwordOne}
onChange={(event) => setPasswordOne(event.target.value)}
id="signUpPassword"
placeholder="Password" />
</Col>
</FormGroup>
<FormGroup row>
<Label for="signUpPassword2" sm={4}>Confirm Password</Label>
<Col sm={8}>
<Input
type="password"
name="password"
value={passwordTwo}
onChange={(event) => setPasswordTwo(event.target.value)}
id="signUpPassword2"
placeholder="Password" />
</Col>
</FormGroup>
<FormGroup row>
<Col>
<Button>Sign Up</Button>
</Col>
</FormGroup>
</Form>
</Col>
</Row>
</Container>
)
}
export default SignUp;
添加一个退出按钮
签出也是非常直接的。signOut() 从useAuth() ,并将其添加到一个按钮或一个链接中。
import { useEffect } from 'react';
import { useRouter } from 'next/router';
import { useAuth } from '../context/AuthUserContext';
import {Container, Row, Col, Button} from 'reactstrap';
const LoggedIn = () => {
const { authUser, loading, signOut } = useAuth();
const router = useRouter();
// Listen for changes on loading and authUser, redirect if needed
useEffect(() => {
if (!loading && !authUser)
router.push('/')
}, [authUser, loading])
return (
<Container>
// ...
<Button onClick={signOut}>Sign out</Button>
// ...
</Container>
)
}
export default LoggedIn;
创建一个登录页面
最后是登录功能!它与前两个功能完全相同。从useAuth() 中获取signInWithEmailAndPassword() ,并传入用户的电子邮件和密码。如果它们是正确的,就重定向给用户,如果不是,就显示正确的错误信息。
import { useState } from 'react';
import Link from 'next/link';
import { useRouter } from 'next/router';
import { useAuth } from '../context/AuthUserContext';
import {Container, Row, Col, Button, Form, FormGroup, Label, Input, Alert} from 'reactstrap';
export default function Home() {
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
const [error, setError] = useState(null);
const router = useRouter();
const { signInWithEmailAndPassword } = useAuth();
const onSubmit = event => {
setError(null)
signInWithEmailAndPassword(email, password)
.then(authUser => {
router.push('/logged_in');
})
.catch(error => {
setError(error.message)
});
event.preventDefault();
};
return (
<Container className="text-center" style={{ padding: '40px 0px'}}>
<Row>
<Col>
<h2>Login</h2>
</Col>
</Row>
<Row style={{maxWidth: '400px', margin: 'auto'}}>
<Col>
<Form onSubmit={onSubmit}>
{ error && <Alert color="danger">{error}</Alert>}
<FormGroup row>
<Label for="loginEmail" sm={4}>Email</Label>
<Col sm={8}>
<Input
type="email"
value={email}
onChange={(event) => setEmail(event.target.value)}
name="email"
id="loginEmail"
placeholder="Email" />
</Col>
</FormGroup>
<FormGroup row>
<Label for="loginPassword" sm={4}>Password</Label>
<Col sm={8}>
<Input
type="password"
name="password"
value={password}
onChange={(event) => setPassword(event.target.value)}
id="loginPassword"
placeholder="Password" />
</Col>
</FormGroup>
<FormGroup row>
<Col>
<Button>Login</Button>
</Col>
</FormGroup>
<FormGroup row>
<Col>
No account? <Link href="/sign_up">Create one</Link>
</Col>
</FormGroup>
</Form>
</Col>
</Row>
</Container>
)
}
总结
在本教程中,我们介绍了如何创建一个Firebase账户、项目和应用程序。然后,我们学习了如何使用React Context来创建一个用户环境。在这个上下文中,我们添加了用户和加载变量,以及记录、注册和退出等功能。最后,我们使用这些函数在我们的Next.js应用中实现了认证,这要感谢Firebase!
使用Firebase在Next.js中实现身份验证》一文首次出现在LogRocket博客上。