使用ReactJS和Firebase进行身份验证

时间:2018-06-27 13:44:28

标签: javascript reactjs firebase authentication firebase-authentication

我是Web开发的新手,并开始学习ReactJS。

到目前为止,构建简单的Web应用程序没有问题。

现在我想通过Firebase使用身份验证。

我知道如何使用Firebase对用户进行身份验证,但是我无法获得如何设计前端以限制对某些页面的访问的方法。

以前,我使用PHP,因为我使用了session变量和include来包含要显示给用户的HTML部分。

但是我不知道如何使用Firebase在ReactJS中做到这一点。

初始和失败方法:

我考虑过更新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身份验证功能。

只是代码段:

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等)来实现吗?)

还是应该为此使用Cloud功能?

3 个答案:

答案 0 :(得分: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中,而是发送到   网址也是如此-但这在当今非常罕见。

     


有关更多信息,请查看手册的Session Handling部分,其中提供了一些有用的信息。   信息。

     

例如,有一个关于Passing the Session ID的页面,   解释了如何使用   Cookie或URL中-以及哪些配置选项会对此产生影响。

因此,session只是一个cookie。然后,我想我们可以使用Cookie来了解用户登录状态。

在Firebase中,cookie存在问题。您只能将Cookie名称设置为__session。 Firebase会忽略其他名称,因此您将无法在服务器上读取Cookie。

您需要使用Firebase Functions进行一些后端工作并根据用户登录状态提供html。

因此,请在您的项目目录中设置Firebase功能。在根中

$ firebase init functions

现在,您必须对Firebase说,到达您域的任何请求都必须调用一个函数。

为此,您必须在rewrites文件中写入firebase.json。我们的函数名称为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。否则,我们发送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导入的。因此,请安装它。

我们在这里做的是:

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

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

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

  4. 然后我们重新加载页面。当我们重新加载页面时,用户再次点击服务器。然后,我们可以检查__session cookie并将Home html发送给用户。

答案 1 :(得分:0)

  

以前,我使用PHP,因为我使用了会话变量,并且包含要显示给用户的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中的auth状态。最好在您的React生命周期方法之一中为auth状态提供一个侦听器:https://firebase.google.com/docs/reference/js/firebase.auth.Auth#onAuthStateChanged

在您的应用程序中的任何时候,身份验证状态都将变为无效,阻止其查看页面。

答案 2 :(得分:0)

好吧,我向您展示如何在React App上使用firebase Auth。我只是想让您知道这不是正确的方法,您会找到很多实现方法。

因此,让我们开始吧,首先,我正在使用Google Auth,我有一个名为firebase.service.js的文件,用于处理Auth。该文件如下所示:

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;

在这里您可以看到该函数检查是否存在任何登录的用户,否则它将将该用户发送回根路径。请注意,我是从auth而不是从./services导入firebase,这是因为此身份验证已初始化为firebase.service.js,然后在所有应用程序中使用它。 / p>

现在您可以将此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;

您可以看到PrivateRouseRoute中的react-router组件并排使用。

我以为是全部,如果您想问其他任何问题,只需评论一下,如果您想从我的代码中查看项目,就可以找到它here