使用 ReactJS 和 Firebase 进行身份验证
Posted
技术标签:
【中文标题】使用 ReactJS 和 Firebase 进行身份验证【英文标题】:Authentication using ReactJS and Firebase 【发布时间】:2018-12-06 10:42:50 【问题描述】:我是 Web 开发新手,开始学习 ReactJS。
到目前为止,构建简单的网络应用程序没有问题。
现在我想使用 Firebase 进行身份验证。
我知道如何使用 Firebase 对用户进行身份验证,但我不知道如何设计前端来限制对某些页面的访问。
以前,我使用 php,因为我使用了 session
变量和 include
来包含要显示给用户的 html 部分。
但我不知道如何在 ReactJS 中使用 Firebase。
初始和失败的方法:
我想更新this.state.loggedIn
并使用它来显示组件。但这不是正确的方法,因为我将两个组件都发送给显示,并且我们可以使用 Chrome 中的 React 开发者扩展轻松更改组件的状态。
这是我的主要 index.js 代码:
import React from 'react';
import ReactDOM from 'react-dom';
import Login from './components/Login/Login';
import Home from './components/Home/Home';
import fire from './components/Fire';
class App extends React.Component
constructor(props)
super(props);
this.state =
loggedIn: false,
;
this.authListener = this.authListener.bind(this);
componentDidMount()
this.authListener();
authListener()
fire.auth().onAuthStateChanged(user =>
if (user)
// User is signed in.
console.log(user);
this.setState(
loggedIn: true
)
else
// No user is signed in.
this.setState(
loggedIn: false
);
);
render()
return (
<div>
this.state.loggedIn ? <Home/> : <Login/>
</div>
);
ReactDOM.render(
<App/>, document.getElementById('app'));
在 Login.js 中,我正在调用 Firebase 身份验证函数。
只是 sn-p:
fire
.auth()
.signInWithEmailAndPassword(this.state.email, this.state.password)
.catch(function (error)
// Handle Errors here.
var errorCode = error.code;
var errorMessage = error.message;
console.log(error);
// ...
);
身份验证工作正常,我可以在控制台日志中看到它。
那么,我应该如何在 ReactJS + Firebase 中进行身份验证?(是否可以不使用任何其他包,如 react-routes 等?)
或者我应该为此使用云功能吗?
【问题讨论】:
您可以使用 react-cookies 进行视图访问限制,并在切换视图并进行条件渲染时检查凭据。不漂亮,但会工作 因此,出于安全目的,您希望像在 PHP 中一样将唯一需要的 HTML/JS 发送到客户端。我说的对吗? @SkrewEverything 是的。如果用户已登录,我应该向用户发送与登录时不同的组件。如果用户没有登录,我根本不应该将登录组件发送给用户。 找到解决方案了吗? @unknownymouse 没有。我还在等人来回答。 【参考方案1】:更新:
您在 html 文件中链接的所有内容都应始终来自您的公共目录。因此,请将捆绑的 .js
文件保存在公共目录中。
我已经为初学者写了一些节省时间的技巧。你可以找到更多关于初学者提示here。
其实很简单。
如果您只想将所需的 html 文件发送到浏览器,则每次需要完全不同的 html 时都必须访问服务器。
你说你使用了 PHP。在 PHP 中,我们使用会话来了解一个人是否已登录。
所以,我们尝试在这里实现它。
首先让我们看看 PHP 中的会话是如何工作的。取自SO Answer
一般情况下:
创建会话时会将会话 ID 发送给用户。 它存储在 cookie 中(默认情况下称为PHPSESSID
) 该 cookie 由浏览器随每个请求发送到服务器 服务器 (PHP) 使用包含 session_id 的 cookie 来了解哪个文件对应于该用户。会话文件中的数据是
$_SESSION
的内容, 序列化 (即,表示为字符串 -- 具有诸如 serialize) ;并且在文件被加载时被取消序列化 PHP,填充$_SESSION
数组。有时,会话 ID 不是存储在 cookie 中,而是发送到 URL 也是如此——但现在已经很少见了。
有关更多信息,您可以查看手册的Session Handling 部分,它提供了一些有用的信息 信息。
例如,有一个关于Passing the Session ID 的页面,其中 解释会话 id 如何在页面之间传递,使用 cookie,或在 URL 中——以及哪些配置选项会影响这一点。
所以,session
只是一个 cookie。那么我想我们可以使用cookie来了解用户的登录状态。
在 Firebase 中,cookie 存在问题。您只能将 cookie 名称设置为 __session
。 Firebase 会忽略其他名称,因此您无法读取服务器上的 cookie。
您需要使用 Firebase Functions 来做一些后端工作并根据用户登录状态提供 html。
因此,在您的项目目录中设置 Firebase 函数。在根目录中,
$ firebase init functions
现在您必须对 Firebase 说,任何进入您的域的请求都必须调用一个函数。
为此,您必须在 firebase.json
文件中写入 rewrites
。我们的函数名称将是main
。
"database":
"rules": "database.rules.json"
,
"hosting":
"public": "/dev",
"ignore": [
"firebase.json",
"**/.*",
"**/node_modules/**"
],
"rewrites":[
"source": "**",
"function": "main"
]
现在在任何地方创建一个index.js
(首选项目目录的根目录,因为这是主文件)。
index.js
import express from 'express';
import * as functions from 'firebase-functions';
import fs from 'fs';
var cookieParser = require('cookie-parser')
const app = express();
app.use(cookieParser());
app.get('**', (req, res) =>
// Check the custom set cookie for user
// If the user is not logged-in, redirect to Login. Otherwise, redirect to Home page
if (req.cookies.__session == 'loggedin')
var index = fs.readFileSync(__dirname + '/src/Home/index.html', 'utf8');
else
var index = fs.readFileSync(__dirname + '/src/Login/index.html', 'utf8');
res.send(index);
);
export let main = functions.https.onRequest(app);
对以上部分的一些解释:
express
:用于处理请求并发送响应。
firebase-functions
:由 Google 提供,用于使用 Firebase 功能。
fs
: 我们根据用户登录状态从文件系统中读取相应的html并提供给它。
cookie-parser
:我们使用它来轻松访问我们的 cookie。
注意:您需要将其转换为 ES5。所以使用 babel 命令来转换它(我假设你在你的根目录中保存了index.js
。)
$ babel index.js -d functions
我们使用名为__session
的cookie 来了解用户的登录状态。
如果__session
设置为loggedin
,我们会向用户发送Home
html
。否则,我们发送Login
html
。
现在,真正的问题是:“我们需要在哪里设置 cookie __session
?”
我们在用户浏览器中设置cookie。
我们利用onAuthStateChanged()
。
在您的Login page component
中调用此方法,例如:
componentDidMount()
this.authListener();
authListener()
fire.auth().onAuthStateChanged(user =>
if (user)
// User is signed in.
if (Cookies.get('__session') === undefined)
Cookies.set('__session', 'loggedin', expires: 3652 );
window.location.reload();
else
// No user is signed in.
Cookies.remove('__session');
);
注意:Cookies
对象是从 js-cookie 导入的。所以,安装它。
我们在这里做的是:
首先在页面加载时调用onAuthStateChanged()
。
如果用户已经登录,我们进入if
块。
然后我们检查__session
cookie。这很重要,因为有时用户可以清除所有 cookie。当我们尝试读取服务器中的 cookie 时,我们得到undefined
并将登录页面发送给用户。从1
重复处理。
然后我们重新加载页面。当我们重新加载页面时,用户再次点击服务器。然后我们可以检查__session
cookie 并将Home
html
发送给用户。
【讨论】:
我有问题。如何在 html 中链接我的 javascript 文件?它说内部错误。 谢谢。您的解决方案按预期工作。如果有人提出最佳答案,我会等待几天,然后再接受答案,因为有人一直在回答我的问题。【参考方案2】:以前,我使用 PHP,并且我使用 session 变量和 include 来包含 HTML 的部分以显示给用户。
Firebase 有多种方式保持其身份验证状态,如下所述:https://firebase.google.com/docs/auth/web/auth-state-persistence
默认为local
。在网络上,这是localStorage
。这对您来说可能是一个巨大的变化,因为通常身份验证状态处于某种会话或 cookie 中,它在服务器端和客户端完成。
那么,我应该如何在 ReactJS + Firebase 中进行身份验证?(是否可以不使用任何其他包,如 react-routes 等?)
但使用 Firebase,一切都在客户端完成。用户成功通过身份验证后,您将可以访问他们的身份验证令牌,如下所述:https://firebase.google.com/docs/auth/users#auth_tokens
使用此令牌,您需要在服务器端进行验证,如下所述:https://firebase.google.com/docs/auth/admin/verify-id-tokens 以保护/保护任何 API 访问。
但是为了保护 React 应用程序中的某些页面,您需要在某种中间件中验证 localStorage
中的身份验证状态。最好在您的 React 生命周期方法之一中拥有一个身份验证状态的侦听器:https://firebase.google.com/docs/reference/js/firebase.auth.Auth#onAuthStateChanged
在您的应用程序中的任何时候,身份验证状态都会变为无效,阻止他们查看页面。
【讨论】:
我的意思是我们仍在将所有组件发送给客户端。它仍然安全吗?【参考方案3】:好吧,我向您展示了我是如何在 React 应用程序上使用 firebase Auth 的。我只是想让你知道这不是正确的方式,你会找到很多方法来做到这一点。
让我们开始吧,首先我使用 Google 身份验证,我有一个名为 firebase.service.js
的文件,我在其中处理身份验证。该文件如下所示:
import firebase from 'firebase';
import firebaseConfig from './private';
export const firebaseApp = firebase.initializeApp(firebaseConfig);
export const auth = firebase.auth();
export const db = firebase.database();
export function loginGoogle(/* options */)
const provider = new firebase.auth.GoogleAuthProvider();
return firebase.auth().signInWithPopup(provider);
firebaseConfig
对象如下所示:
const firebaseConfig =
apiKey: API_KEY,
authDomain: AUTH_DOMAIN,
databaseURL: DB_URL,
projectId: PROJECT_ID,
storageBucket: STORE_BUCKET,
messagingSenderId: MSG_SENDER,
;
您可以在 Firebase 控制台中找到它。
在安装它的应用程序的索引文件中,我将ReactDOM.render
方法与身份验证检查包装在一起,以确保用户是否已登录。索引如下:
import React from 'react';
import ReactDOM from 'react-dom';
import firebase from 'firebase';
import './index.css';
import App from './App';
import registerServiceWorker from './registerServiceWorker';
import auth from './services';
auth.setPersistence(firebase.auth.Auth.Persistence.LOCAL).then(() =>
const stop = auth.onAuthStateChanged(() =>
ReactDOM.render(<App />, document.getElementById('root'));
stop();
);
);
registerServiceWorker();
现在您可以确定,如果用户离开并返回应用程序,如果他的会话仍然有效,他将不需要再次登录。
下一步是创建PrivateRoute
组件。这是我所做的:
import * as React from 'react';
import Redirect, Route from 'react-router-dom';
import PropTypes from 'prop-types';
import auth from '../services';
function PrivateRoute( component: Component, ...rest )
const toRender = propsRender => (
auth.currentUser ?
<Component ...propsRender /> :
<Redirect
to=
pathname: '/',
state: from: propsRender.location ,
/>
);
return <Route ...rest render=toRender />;
PrivateRoute.propTypes =
component: PropTypes.func.isRequired,
path: PropTypes.string.isRequired,
;
export default PrivateRoute;
在这里您可以看到该函数检查是否存在任何已登录的用户,否则它将用户发送回根路径。请注意,我是从./services
导入auth
,而不是从firebase
,这是因为此身份验证在firebase.service.js
初始化,然后我在所有应用程序中都使用它。
现在你可以使用这个PrivateRoute
作为一个普通路由,不同的是会检查用户是否登录。以此为例:
import * as React from 'react';
import BrowserRouter, Route, Switch, Redirect from 'react-router-dom';
import Layout from 'antd';
import moment from 'moment';
import 'moment/locale/pt-br';
import 'antd/dist/antd.css';
import PrivateRoute, MainLogged, Login, Navbar, Page404 from './components';
const Header = Layout;
moment.locale('pt-br');
function Main()
return <Redirect to="/login" />;
function App()
return (
<BrowserRouter>
<Layout>
<Header style= paddingLeft: '0', backgroundColor: '#096dd9' >
<Navbar />
</Header>
<Switch>
<Route exact path="/" component=Main />
<Route exact path="/login" component=Login />
<PrivateRoute path="/app" component=MainLogged />
<Route component=Page404 />
</Switch>
</Layout>
</BrowserRouter>
);
export default App;
如您所见,PrivateRouse
与来自react-router
的Route
组件并排使用。
我以为就是这样,如果您想问其他任何问题,只需发表评论,如果您想查看我从中获取此代码的项目,您可以找到它here。
【讨论】:
首先我需要检查react-router-dom
,然后我会评论你的答案
我们仍然将所有组件发送给客户端。它仍然安全吗?
是的,当你构建你的应用程序时,它会被模仿和丑化,无法理解代码。请确保不要将.map
发送到托管服务器。如果您使用 react-create-app 和 react-script 你必须从构建的应用程序中删除 .map
。以上是关于使用 ReactJS 和 Firebase 进行身份验证的主要内容,如果未能解决你的问题,请参考以下文章
使用 Firebase 和 ReactJS 的基于角色的身份验证
如何使用 apollo Reactjs 客户端刷新 Firebase IdToken
如何使用 ReactJS 应用程序开发和测试 Firebase 可调用函数?
如何使用 nodejs 和 reactjs 设置 firebase 模拟器?