使用 auth 知道用户已登录

Posted

技术标签:

【中文标题】使用 auth 知道用户已登录【英文标题】:Using auth to know that a user is logged in 【发布时间】:2021-07-08 04:56:29 【问题描述】:

我的问题是这样的:

我有一个用 react 构建的整个站点,我通过 Nodejs 连接到该站点,该站点的所有信息基本上都是从服务器端获取的。

对于聊天,不可能使用服务器端,因为您需要一个称为侦听器的东西,对于侦听器,您还需要在 React 中配置 firebase 并且侦听器将工作,对于聊天,您需要允许连接的用户发送消息,不会对所有人开放。要知道谁已连接,您需要使用名为 auth 的选项。

我需要身份验证,对于 firebase 中的规则,我将只能允许连接的用户访问聊天,而不是让规则对所有人开放。

当我将 firebase 中的规则设置为 true 时,聊天可以正常工作,但我希望只有注册用户才能使用聊天,我已将其设为规则。

我收到以下错误:

Uncaught Error in snapshot listener: FirebaseError: Missing or insufficient permissions.

问题是我已经通过服务器端连接,但还需要通过 react 以某种方式连接,我还需要通过 react 连接以查看谁已连接,并且只允许那些已连接的人聊天要使用。需要通过反应连接,因为只有通过反应我才能获得firebase必须提供的所有功能。只有通过react直接连接firebase才能获得auth,但它要求在react中我也会连接聊天。 (需要监听器,因为这样用户可以立即收到某人发给他的消息,并且需要 auth 只允许注册用户访问聊天)

为此,我需要通过 react 进行连接,并使用名为 auth 的东西来查看谁已连接。但是整个站点本身是围绕通过nodejs登录而构建的,并且在nodejs中无法使用auth。 nodejs无法跟踪auth,nodejs最后只返回JSON信息。

我不知道如何在react中启用auth的连接,因为我说的,将所有连接都转移到react中,会拖动来填充站点上的更改,但是通过nodejs登录不允许使用的auth,因为是服务器端,所以我的服务器端只返回JSON数据,不返回其他功能,比如auth。

我正在考虑通过 react 和 nodejs 进行登录时间,但我担心它不会起作用,因为它可能会创建 2 个不同的令牌并且只允许其中一个起作用。

我尝试过其他解决方案,例如 privateRoute,但仍然无济于事,因为必须以某种方式获得身份验证,这样才能知道谁已连接。如果有人可以帮助我或为我提供解决方案,我会很高兴,我认为应该有一些可以做的事情并解决问题。

我如何告诉 Firebase 注册用户想要访问聊天?

到目前为止我所做的代码:

通过nodejs登录


app.post('/login', login);


exports.login = (req, res) => 
  const user = 
    email: req.body.email,
    password: req.body.password,
  ;

  const  valid, errors  = validateLoginData(user);

  if (!valid) return res.status(400).json(errors);

  firebase
    .auth()
    .signInWithEmailAndPassword(user.email, user.password)
    .then((data) => 
      console.log(JSON.stringify(data));
      return data.user.getIdToken();
    )
    .then((token) => 
      db.collection('users')
        .where("email", "==", user.email)
        .get()
        .then((data) => 
          data.forEach((doc) => 
            let userHandle = doc.data().handle;
            db.collection("users")
              .doc(userHandle)
              .get()
              .then((doc) => 
                let user = doc.data();
                db.collection('users').doc(userHandle)
                  .set(user)
                  .then(() => 
                    return res.json( token: token);
                  )
                  .catch((err) => 
                    console.error(err);
                    return res.status(500).json( error: err.code );
                  );

              ).catch((error) => 
                console.error(error);
              );


          );
        )
        .catch((err) => 
          console.error(err);
          res.status(500).json( error: err.code );
        );
    )
    .catch((err) => 
      console.error(err);
      return res
        .status(403)
        .json( general: "Wrong credentials, please try again" );
    );
;

使用nodejs登录功能

export const loginUser = (userData, history) => (dispatch) => 
//userData contains mail and password  
axios
    .post('/login', userData)
    .then((res) => 
      setAuthorizationHeader(res.data.token);
      //maybe add the auth here somehow
      dispatch(getUserData());
      dispatch
        (
          type: LOGIN_USER,
          payload: res.data.handle
        );

      history.push('/');
    )
    .catch((err) => 
      dispatch(
        type: SET_ERRORS,
        payload: err.response.data
      );
    );
;

PrivateRoute in react

import React from 'react';
import  Route, Redirect  from 'react-router-dom';


const PrivateRoute = (component: Component, ...rest) => 
  return(
    <Route ...rest component=(props) => 
        const user = localStorage.getItem('user') ? JSON.parse(localStorage.getItem('user')) : null;

        if(user)
            return <Component ...props />
        else
            return <Redirect to=`/login` />
        

     />
   )

 

export default PrivateRoute

react to connect中的认证,但不是聊天,聊天我不知道怎么办

import React,  Component  from 'react';
import  BrowserRouter as Router, Route, Switch  from 'react-router-dom';
import './App.css';
import MuiThemeProvider from '@material-ui/core/styles/MuiThemeProvider';
import createMuiTheme from '@material-ui/core/styles/createMuiTheme';
import jwtDecode from 'jwt-decode';
// Redux
import  Provider  from 'react-redux';
import store from './redux/store';
import  SET_AUTHENTICATED  from './redux/types';
import  logoutUser, getUserData  from './redux/actions/userActions';
// Components
import Navbar from './components/layout/Navbar';
import themeObject from './util/theme';
import AuthRoute from './util/AuthRoute';
import PrivateRoute from './util/PrivateRoute';

// Pages
import home from './pages/home';
import login from './pages/login';
import signup from './pages/signup';
import chat from './pages/chat';

import axios from 'axios';

const theme = createMuiTheme(themeObject);
axios.defaults.baseURL =
  'https://europe-west1-projectdemourl-b123c.cloudfunctions.net/api';

const token = localStorage.FBIdToken;
if (token) 
  const decodedToken = jwtDecode(token);
  if (decodedToken.exp * 1000 < Date.now()) 
    store.dispatch(logoutUser());
    window.location.href = '/login';
   else 
    store.dispatch( type: SET_AUTHENTICATED );
    axios.defaults.headers.common['Authorization'] = token;
    store.dispatch(getUserData());
  


class App extends Component 
  render() 
    return (
      <MuiThemeProvider theme=theme>
        <Provider store=store>
          <Router>
            <Navbar />
            <div className="container">
              <Switch>
                <Route exact path="/" component=home />
                <AuthRoute exact path="/login" component=login />
                <AuthRoute exact path="/signup" component=signup />
                <PrivateRoute path="/chat" exact component=chat />
              </Switch>
            </div>
          </Router>
        </Provider>
      </MuiThemeProvider>
    );
  


export default App;

登录到 firebase,客户端

import firebase from "firebase/app";
import "firebase/firestore";

const firebaseConfig = 
    apiKey: process.env.REACT_APP_FIREBASE_KEY,
    authDomain: process.env.REACT_APP_FIREBASE_DOMAIN,
    projectId: process.env.REACT_APP_FIREBASE_PROJECT_ID,
    storageBucket: process.env.REACT_APP_FIREBASE_STORAGE_BUCKET,
    messagingSenderId: process.env.REACT_APP_FIREBASE_SENDER_ID,
    appId: process.env.REACT_APP_FIREBASE_APP_ID,
    measurementId: process.env.REACT_APP_FIREBASE_MEASUREMENT_ID
;

firebase.initializeApp(firebaseConfig);
export default firebase;

我用于聊天的功能, 在这些功能中,我使用 firebase


export const getRealtimeUsers = (handle) => 

    return async (dispatch) => 
        const db = firebase.firestore();
        const unsubscribe = db.collection("users")
            .onSnapshot((querySnapshot) => 
                db.collection('friends')
                    .get()
                    .then((data) => 
                        let friends = [];
                        data.forEach((doc) => 
                            if (doc.data().isFriends) 
                                if (doc.data().userHandleReceive == handle) 
                                    friends.push(doc.data().userHandleSend);
                                
                                else 
                                    friends.push(doc.data().userHandleReceive);
                                
                            
                        );
                        const users = [];
                        querySnapshot.forEach(function (doc) 
                            if (doc.data().handle != handle && (friends.indexOf(doc.data().handle) > -1) ) 
                                users.push(doc.data());
                            
                        );
                    )
                    .catch((err) => 
                        console.error(err);
                    );
            );
        return unsubscribe;
    


export const updateMessage = (msgObj) => 

    return async dispatch => 

        const db = firebase.firestore();
        db.collection('conversations')
            .add(
                ...msgObj,
                isView: false,
                createdAt: new Date()
            )
            .then((data) => 
                console.log(data);
            )
            .catch((error) => 
                console.error(error);
            );

    




export const getRealtimeConversations = (user) => 
    return async dispatch => 

        const db = firebase.firestore();
        db.collection('conversations')
            .where('user_uid_1', 'in', [user.uid_1, user.uid_2])
            .orderBy('createdAt', 'asc')
            .onSnapshot((querySnapshot) => 

                const conversations = [];

                querySnapshot.forEach(doc => 
                    if (
                        (doc.data().user_uid_1 == user.uid_1 && doc.data().user_uid_2 == user.uid_2)
                        ||
                        (doc.data().user_uid_1 == user.uid_2 && doc.data().user_uid_2 == user.uid_1)
                    ) 
                        conversations.push(doc.data())
                    

                );

                dispatch(
                    type: userConstants.GET_REALTIME_MESSAGES,
                    payload:  conversations 
                )
            )
    


这是代码的主要部分,另外说明我需要在react中添加loginUser函数选项到auth

我想澄清一下,我创建了一个通过 Nodejs 进行身份验证的用户,但 React 没有看到它经过身份验证。

我只是在这里遗漏了一些东西,问题非常非常小,但我无法弄清楚它在哪里,以及如何解决它

【问题讨论】:

您可能需要考虑console.log(req.body) 和您的正文解析器。你确定你的 req.body 不为空? 第二,如果你想对 firestore/RTDB 使用身份验证,你需要用户在客户端进行身份验证。 EITHER - 允许用户使用电子邮件/密码客户端登录。或者 - 如果你想 nodejs 进行身份验证,那么你需要将 JWT 令牌传递给客户端并允许客户端使用 JWT 登录,然后你可以在 firestore 或 RTDB 中看到身份验证信息 由于你使用的是firebase,所以在react端或nodejs端登录基本相同。 firebase.google.com/docs/auth/web/firebaseui 实际上我们大多数人使用客户端登录,而不是 nodejs 登录。 您只显示了节点端的代码,我不确定您如何在客户端进行身份验证。 @SomeoneSpecial 你说得对,感谢您的评论,我刚刚添加了身份验证过程,但它现在无法用于聊天 【参考方案1】:

你的推理有几个缺陷,你的代码有几个错误。

从根本上说: 为什么要在服务器端和客户端都登录?

如果您希望您的用户登录到您的数据库进行读取和/或写入,您可以在客户端执行此操作,也可以在服务器端执行此操作。两者都提供几乎相同的选项。

在服务器和客户端上都这样做没有多大意义。引出了一个问题:为什么你想要两者都做?...

解决办法:

既然您是在 html 页面中收集用户的登录信息,为什么不在客户端也立即登录用户呢?

以这个简单的 HTML 为例,它有两个输入并加载三个 firebase 脚本:

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <script src="https://www.gstatic.com/firebasejs/8.4.1/firebase-app.js"></script>
    <script src="https://www.gstatic.com/firebasejs/8.4.1/firebase-auth.js"></script>
    <script src="https://www.gstatic.com/firebasejs/8.4.1/firebase-firestore.js"></script>
</head>
<body>
    <input name="email" id="email" type="text">
    <input name="password" id="password" type="text">
    <button type="button" id="login">Login</button>
    <script src="index.js"></script>
</body>
</html>

在同一文件夹中将ìndex.js文件添加到它旁边:

var firebaseConfig = 
    apiKey: "AIzaSyAg2.....z7ObRoXg-goy9jc",
    authDomain: "s.....3.firebaseapp.com",
    projectId: "st.....40f63",
    storageBucket: "stacksh.....pspot.com",
    messagingSenderId: "537.....09",
    appId: "1:537.....04409:web:2.....7a6b67c14fa48"
;
firebase.initializeApp(firebaseConfig);
var db = firebase.firestore();
/* We listen for button click and then log in the user */
var button = document.getElementById('login');
button.addEventListener('click',function()
    var email = document.getElementById('email').value;
    var password = document.getElementById('password').value;
    firebase
        .auth()
        .signInWithEmailAndPassword(email, password)
        .then((data) => 
            console.log(data);
            alert("logged in");
            getRealtimeUsers();
            
        ).catch((err) => 
            res.send(JSON.stringify(err));
            return err;
        );
    
);
function getRealtimeUsers()
    const db = firebase.firestore();
    db.collection("users").get().then((querySnapshot) => 
        querySnapshot.forEach((doc) => 
            // doc.data() is never undefined for query doc snapshots
            console.log(doc.id, " => ", doc.data());
        );
    );

如果您还需要在服务器端登录,无论出于何种原因,您都可以使用服务器端登录。

警告 您的服务器端代码包含一个重大缺陷。它应该等待 promise 解决:
app.use(bodyParser.json());
app.use(bodyParser.urlencoded( extended: true ));
const config =  
    apiKey: "AIzaSyAg2GWsL04akpMNezuuz7ObRoXg-goy9jc",
    authDomain: "stackshop-40f63.firebaseapp.com",
    projectId: "stackshop-40f63",
    storageBucket: "stackshop-40f63.appspot.com",
    messagingSenderId: "537745304409",
    appId: "1:537745304409:web:2a8bb1f007a6b67c14fa48"
;

const firebase = require("firebase");
firebase.initializeApp(config);

// Log user in
async function login(req, res)   
    const user = 
        email: req.body.email,
        password: req.body.password,
    ;
    await firebase
        .auth()
        .signInWithEmailAndPassword(user.email, user.password)
        .then((data) => 
            
            firebase.auth().currentUser.getIdToken(false).then(function(idToken) 
                      // Send token to your backend via HTTPS
                      // ...
                    res.send(idToken);
                    //signInWithCustomToken()
                    
                ).catch(function(error) 
                  // Handle error
                );
            return data;
        ).catch((err) => 
            res.send(JSON.stringify(err));
            return err;
        );

app.post('/login', login);

【讨论】:

以上是关于使用 auth 知道用户已登录的主要内容,如果未能解决你的问题,请参考以下文章

firebase.auth().onAuthStateChanged 不工作

Auth::guest() 为已登录和已注销的用户返回 true

FireBaseUI Auth - 如何知道帐户是来自新注册用户还是现有用户?

将多个Auth提供程序链接到一个帐户

如何在 Symfony 中访问已登录的 Auth0 用户的电子邮件地址?

当 Auth 服务器重定向到 SSO 代理服务器时,它不知道登录是不是成功