【问题标题】:Authentication using ReactJS and Firebase使用 ReactJS 和 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 没有。我还在等人来回答。

标签: javascript reactjs firebase authentication firebase-authentication


【解决方案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);

对以上部分的一些解释:

  1. express:用于处理请求并发送响应。

  2. firebase-functions:由 Google 提供,用于使用 Firebase 功能。

  3. fs : 我们根据用户登录状态从文件系统中读取相应的html并提供给它。

  4. cookie-parser:我们使用它来轻松访问我们的 cookie。

注意:您需要将其转换为 ES5。所以使用 babel 命令来转换它(我假设你在你的根目录中保存了index.js。)

$ babel index.js -d functions

我们使用名为__session 的cookie 来了解用户的登录状态。

如果__session 设置为loggedin,我们会向用户发送Home html。否则,我们发送Loginhtml

现在,真正的问题是:“我们需要在哪里设置 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 导入的。所以,安装它。

我们在这里做的是:

  1. 首先在页面加载时调用onAuthStateChanged()

  2. 如果用户已经登录,我们进入if块。

  3. 然后我们检查__session cookie。这很重要,因为有时用户可以清除所有 cookie。当我们尝试读取服务器中的 cookie 时,我们得到undefined 并将登录页面发送给用户。从1重复处理。

  4. 然后我们重新加载页面。当我们重新加载页面时,用户再次点击服务器。然后我们可以检查__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-routerRoute 组件并排使用。

我以为就是这样,如果您想问其他任何问题,只需发表评论,如果您想查看我从中获取此代码的项目,您可以找到它here

【讨论】:

  • 首先我需要检查react-router-dom,然后我会评论你的答案
  • 我们仍然将所有组件发送给客户端。它仍然安全吗?
  • 是的,当你构建你的应用程序时,它会被模仿和丑化,无法理解代码。请确保不要将.map 发送到托管服务器。如果您使用 react-create-app 和 react-script 你必须从构建的应用程序中删除 .map
猜你喜欢
  • 1970-01-01
  • 2020-10-09
  • 2017-03-11
  • 2019-03-28
  • 2018-10-19
  • 2022-01-12
  • 2020-09-02
  • 1970-01-01
  • 2021-05-26
相关资源
最近更新 更多