首页 > 解决方案 > 为什么注册用户未在 Firebase 中进行身份验证?为什么用户不能将产品添加到数据库 Firebase?

问题描述

我应该在我的项目中添加什么,以便经过身份验证的所有者可以添加产品或修改它们?

我应该怎么做才能使注册用户可以从管理员面板将产品添加到目录?

我有一个使用npx create-react-app my-app Web App 创建的应用程序,它是餐厅、商店或街头小贩产品的管理面板。

在其中,企业主将产品添加到 Firebase 数据库,稍后可以通过此管理员面板从移动应用程序中查看。

为了增加我的知识,我按照项目和教程将所需的用户注册添加到应用程序(管理员面板)

最后,我设法用 firebase 创建了一个小型用户注册应用程序,你可以在这个GitHub 存储库中看到它: https ://github.com/miguelitolaparra/react-firebase-email-login

使用与 react-firebase-email-login 应用程序相同的组件,我在管理员面板中创建了一个注册系统,一切正常,您可以在以下链接中看到我将应用程序上传到服务器:

http://restaurantcliente.webapplicationdeveloper.es/

这应该是企业主管理其产品、添加或从目录中删除的地方。

要防止任何人注册和修改所有者的目录,请移除 SignUp 部分并从 Firebase 数据库中注册用户。

将 Firebase 规则更改为安全,如下所示:

service cloud.firestore {
  match / databases / {database} / documents {
    match / {document = **} {
      allow read: if true
      allow write: if request.auth.uid == request.data.author_uid
    }
  }
}

当我尝试从管理员面板添加新产品时出现问题,因为产品没有添加到数据库中。当我更改规则时,任何人都可以读写,它可以工作,管理员可以从管理员面板将产品添加到数据库

service cloud.firestore {
  match / databases / {database} / documents {
    match / {document = **} {
      allow read, write: if true;
    }
  }
}

我已经看过几个使用私有路由和其他组件的示例和教程,但我总是遇到将这些系统添加到我的项目中的问题,因为对 和 进行了新的更改react-router-domreact-router这些更改已删除了诸如<Switch>createHistory等元素。

我不知道如何使我的项目安全,并且当用户注册时,我可以从管理员面板将项目添加到目录中,而无需从数据库中手动添加它们。

在尝试了我在 GitHub 上找到的示例并且没有实现目标之后,我不知道还能做什么。我无法添加所有示例,因为我已经尝试了至少 30 个不同的项目。我应该在我的项目中添加什么,以便所有者可以添加产品或修改它们?

我应该怎么做才能使注册用户可以从管理员面板将产品添加到目录?

编辑以向问题添加更多代码

我希望这可以帮助我让它工作。您可以尝试以下链接中的示例:

http://restaurantcliente.webapplicationdeveloper.es

管理员

restaurantcliente@webapplicationdeveloper.es

密码: Restaurant1920

最大的问题是Web应用程序,我需要将它完全包装在身份验证模式下,以便在我编辑规则“安全”时它的所有元素和功能都可用但是这个我不知道该怎么做

该数据库由两个集合组成:Orders 和 Products Orders 集合是在客户从移动应用程序下订单时从移动应用程序创建的。Products 集合由餐厅所有者从管理员面板创建。

这是文件:NuevoPlato.js

import React, { useContext, useState } from 'react'
import { useFormik } from 'formik'
import * as Yup from 'yup'
import { FirebaseContext } from '../../firebase'
import { useNavigate } from 'react-router-dom'
import FileUploader from 'react-firebase-file-uploader'

const NuevoPlato = () => {

  // state para las imagenes
  const [subiendo, guardarSubiendo] = useState(false)
  const [progreso, guardarProgreso] = useState(0)
  const [urlimagen, guardarUrlimagen] = useState('')

  // Context con las operaciones de firebase
  const { firebase } = useContext(FirebaseContext)

  //console.log(firebase)

  // Hook para redireccionar
  const navigate = useNavigate()

  // validación y leer los datos del formulario
  const formik = useFormik({
    initialValues: {
      nombre: '',
      precio: '',
      categoria: '',
      imagen: '',
      descripcion: '',
    },
    validationSchema: Yup.object({

      nombre: Yup.string()
        .min(3, 'Los Platillos deben tener al menos 3 caracteres')
        .required('El Nombre del platillo es obligatorio'),
      precio: Yup.number()
        .min(1, 'Debes agregar un número')
        .required('El Precio es obligatorio'),
      categoria: Yup.string()
        .required('La categoría es obligatoria'),
      descripcion: Yup.string()
        .min(10, 'La descripción debe ser más larga')
        .required('La descripción es obligatoria')

    }),
    onSubmit: plato => {
      try {
        plato.existencia = true
        plato.imagen = urlimagen

        firebase.db.collection('productos').add(plato)

        // Redireccionar
        navigate('/menu')
      } catch (error) {
        console.log(error)
      }
    }
  })
  // Todo sobre las imagenes
  const handleUploadStart = () => {
    guardarProgreso(0)
    guardarSubiendo(true)
  }
  const handleUploadError = error => {
    guardarSubiendo(false)
    console.log(error)
  }
  const handleUploadSuccess = async nombre => {
    guardarProgreso(100)
    guardarSubiendo(false)

    // Almacenar la URL de destino
    const url = await firebase
      .storage
      .ref("productos")
      .child(nombre)
      .getDownloadURL()

    console.log(url)
    guardarUrlimagen(url)
  }
  const handleProgress = progreso => {
    guardarProgreso(progreso)

    console.log(progreso)
  }


  return (
    <>
      <h1 className="text-3xl font-light mb-4">Add new plate</h1>

      <div className="flex justify-center mt-10">
        <div className="w-full max-w-3xl">
          <form
            onSubmit={formik.handleSubmit}
          >
            
// THIS IS THE FORM WITH THE DATA                <input
              type="submit"
              className="bg-gray-800 hover:bg-gray-900 w-full mt-5 p-2 text-white uppercase font-bold"
              value="Agregar Plato"
            />
          </form>
        </div>
      </div>
    </>
  )

}

export default NuevoPlato

在管理员面板中,还管理“交货时间”,即完成订单所需的时间,由面板中的餐厅所有者定义。

但是,如果我编辑“安全”规则,我也无法修改此数据以使其显示在移动应用程序中。

文件设置传递时间 Orden.js

import React, { useState, useContext} from 'react'
import { FirebaseContext } from '../../firebase'

const Orden = ({ orden }) => {

  const [  tiempoentrega, guardarTiempoEntrega ]= useState(0)

// Context de Firebase
const { firebase } = useContext(FirebaseContext)

// define el tiempo de entrega del pedido en tiempo real
const definirTiempo = id => {
  try {
    firebase.db.collection('ordenes')
        .doc(id)
        .update({
          tiempoentrega
        })
  } catch (error) {
    console.log(error)
  }
}

// Marcar que el pedido esta completado
const completarOrden = id => {
   try {
     firebase.db.collection('ordenes')
           .doc(id)
           .update({
             completado: true
           })
   } catch (error) {
     console.lor(error)
   }
}

  return (
    <div className="sm:w-1/2 lg:w-1/3 px-2 mb-4">
      <div className="p-3 shadow-md bg-white">
        <h1 className="text-yellow-600 text-lg font-bold"> {orden.id} </h1>
        {orden.orden.map(platos => (
          <p className="text-gray-600"> {platos.cantidad} {platos.nombre} </p>
        ))}

        <p className="text-gray-700 font-bold">Total a Pay: {orden.total}$</p>

        {orden.tiempoentrega === 0 && (
          <div className="mb-4">
            <label className="block text-gray-700 text-sm font-bold mb-2">
            Delivery Time
            </label>

            <input
              type="number"
              className="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline"
              min="1"
              max="30"
              placeholder="Pon El Tiempo"
              value={tiempoentrega}
              onChange={ e => guardarTiempoEntrega( parseInt(e.target.value))}
            />

            <button
            onClick={ () => definirTiempo(orden.id) }
              type="submit"
              className="bg-gray-500 hover:bg-gray-700 w-full mt-5 p-2 text-white uppercase font-bold"
            >
              Define Time
            </button>
          </div>
        )}

        {orden.tiempoentrega > 0 && (
          <p className="text-gray-700">Delivery Time:
          <span className="font-bold"> {orden.tiempoentrega} Minutes </span>
           </p>
        )}

        { !orden.completado && orden.tiempoentrega > 0 &&(
          <button
          type="button"
          className="bg-blue-700 hover:bg-blue-400 w-full mt-5 p-2 text-white uppercase font-bold"
          onClick={ () => completarOrden(orden.id)}
          >
            Declare Done
          </button>
        )}
      </div>
    </div>
  )
}

export default Orden

我展示了一些文件

文件 App.js

import React, { useState, useEffect } from "react"
import { Routes, Route } from "react-router"

import firebase, { FirebaseContext } from "./firebase"
//import { auth } from 'firebase'
import firebaseObj from "./firebase"
import Ordenes from "./components/paginas/Ordenes"
import Menu from "./components/paginas/Menu"
import NuevoPlato from "./components/paginas/NuevoPlato"
import Sidebar from "./components/ui/Sidebar"
import Signin from "./components/Signin"

const auth = firebaseObj.auth

function App() {
  const [user, setUser] = useState(null)
  useEffect(() => {
    const unsubscribe = auth.onAuthStateChanged((userAuth) => {
      const user = {
        uid: userAuth?.uid,
        email: userAuth?.email,
      }
      if (userAuth) {
        console.log(userAuth)
        setUser(user)
      } else {
        setUser(null)
      }
    })
    return unsubscribe;
  }, [])

  if (!user) {
    return (
      <div className="md:flex min-h-screen">
        <div className="md:w-2/5 xl:w-4/5 p-6">
          <Signin />
        </div>
      </div>
    )
  } else {
    return (
      <FirebaseContext.Provider
        value={{
          firebase,
        }}
      >
        <div className="md:flex min-h-screen">
          <Sidebar />
          <div className="md:w-2/5 xl:w-4/5 p-6">
            <Routes>

              <Route path="/" element={<Ordenes />} />
              <Route path="/menu" element={<Menu />} />
              <Route path="/nuevo-plato" element={<NuevoPlato />} />
            </Routes>
          </div>
        </div>

      </FirebaseContext.Provider>
    );
  }
}

export default App

文件登录.js

import React, { useRef } from 'react'
import firebaseObj from '../firebase/firebase'

const auth = firebaseObj.auth

const Signin = () => {
  const emailRef = useRef(null);
  const passwordRef = useRef(null);
  const signUp = e => {
    e.preventDefault();
    auth.createUserWithEmailAndPassword(
      emailRef.current.value,
      passwordRef.current.value
    ).then(user => {
      console.log(user)
    }).catch(err => {
      console.log(err)
    })
  }
  const signIn = e => {
    e.preventDefault();
    auth.signInWithEmailAndPassword(
      emailRef.current.value,
      passwordRef.current.value
    ).then(user => {
      console.log(user)
    }).catch(err => {
      console.log(err)
    })
  }
  return (
    <div className="flex justify-center mt-10">
      <div className="w-full max-w-3xl">
        <h1 className="my-8 text-yellow-600 text-lg font-bold text-center text-7xl"> My humble restaurant </h1>
        <div className="mb-4">
          <form action="">
            <h1 className="text-center m-3">Start the Work Session</h1>
            <div className="mb-4">
              <label className="block text-gray-700 text-sm font-bold mb-2" htmlFor="nombre">Email</label>
              <input className="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline"
                ref={emailRef} type="email" placeholder="email" />
            </div>
            <div className="mb-4">
              <label className="block text-gray-700 text-sm font-bold mb-2" htmlFor="nombre">Password</label>
              <input className="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline"
                ref={passwordRef} type="password" placeholder="password" />
            </div>
            <button
              type="button"
              className="bg-blue-700 hover:bg-blue-400 w-full mt-5 p-2 text-white uppercase font-bold"
              onClick={signIn}
            >
              Log In
            </button>
            <div className="mb-4 mt-8 ">
              <h2 className="block text-gray-700 text-sm font-bold mb-2 text-3xl">Enter the administration panel of your restaurant and start preparing dishes and orders</h2>
            </div>
          </form>
        </div>
      </div>
    </div>
  )
}

export default Signin

文件包.json

{
      "name": "restaurantcliente",
      "version": "0.1.0",
      "private": true,
      "homepage": "/build", 
      "dependencies": {
        "@testing-library/jest-dom": "^5.11.4",
        "@testing-library/react": "^11.1.0",
        "@testing-library/user-event": "^12.1.10",
        "firebase": "^7.19.0",
        "formik": "^2.2.9",
        "react": "^17.0.2",
        "react-dom": "^17.0.2",
        "react-firebase-file-uploader": "^2.4.4",
        "react-router-dom": "^6.0.0-beta.0",
        "react-scripts": "^4.0.3",
        "web-vitals": "^1.0.1",
        "yup": "^0.32.9"
      },
      "scripts": {
        "start": "npm run watch:css && react-scripts start",
        "build": "npm run build:css && react-scripts build",
        "test": "react-scripts test",
        "eject": "react-scripts eject",
        "build:css": "postcss src/css/tailwind.css -o src/css/main.css",
        "watch:css": "postcss src/css/tailwind.css -o src/css/main.css"
      },
      "eslintConfig": {
        "extends": [
          "react-app",
          "react-app/jest"
        ]
      },
      "browserslist": {
        "production": [
          ">0.2%",
          "not dead",
          "not op_mini all"
        ],
        "development": [
          "last 1 chrome version",
          "last 1 firefox version",
          "last 1 safari version"
        ]
      },
      "devDependencies": {
        "autoprefixer": "^10.3.1",
        "history": "^5.0.0",
        "postcss-cli": "^8.3.1",
        "react-router": "^6.0.0-beta.0",
        "tailwindcss": "^2.2.7"
      }
    }

标签: javascriptreactjsfirebasefirebase-authenticationreact-router

解决方案


最简单的方法是去firebase控制台,复制你的管理员的uid(假设是类似的uid XwKD2Mnde8QWqHqN1Q8UfDZe0bF3)。比你的规则应该是:

service cloud.firestore {
  match /databases/{database}/documents {
        match /products/{document = **} {
        allow read: if true
        allow write: if request.auth.uid == 'XwKD2Mnde8QWqHqN1Q8UfDZe0bF3'
    }
    match /orders/{document = **} {
      allow read, write: if true
    }
  }
} 

(替换XwKD2Mnde8QWqHqN1Q8UfDZe0bF3为您的管理员 uid)。

您可以在此处找到用户 ID:用户 UID 示例

||这是最简单的方法,但仅适用于单个预定义值(您还可以选择使用so somethink like添加超过 1 个管理员帐户allow write: if request.auth.uid == 'XwKD2Mnde8QWqHqN1Q8UfDZe0bF3' || request.auth.uid == 'NzGq8...'),但如果您有更多管理员或他们可能会更改,则维护起来很糟糕。

最简洁的方法(如果您的用户可以更改,也最容易维护)是使用 Custom Claims。但是,如果我正确理解了您的需求(以及您对 firebase 的经验),我个人认为第一个解决方案可能更适合您的情况。

或者最后一个选项(如果您 100% 确定不会出现没有管理员权限的用户),您可以替换if request.auth.uid == 'XwKD2Mnde8QWqHqN1Q8UfDZe0bF3'if request.auth.uid != null(但我不建议使用此选项,因为如果您在任何时候添加任何用户帐户并忘记更新规则,例如一个简单的用户可以编辑您的产品价格)

Firebase 规则游乐场: firebase 规则游乐场


推荐阅读