javascript - 为什么注册用户未在 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-dom
,react-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"
}
}
解决方案
最简单的方法是去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)。
||
这是最简单的方法,但仅适用于单个预定义值(您还可以选择使用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
(但我不建议使用此选项,因为如果您在任何时候添加任何用户帐户并忘记更新规则,例如一个简单的用户可以编辑您的产品价格)
推荐阅读
- c# - 加解密方法
- python - Azure Python SDK - 使用 cloud-init 启动 VM
- drupal - 试图从drupal gt获取非对象的属性
- python - 找到与文件中的时间戳更接近的时间戳
- microservices - 使用 URL 调用 feign 客户端
- sql - SQL Left Join on Criteria between dates?
- ios - react-native-maps fitToSuppliedMarker/fitToCorodinates 不显示所有标记
- c# - Collectoiview.Filter 不过滤数据
- ios - 如何在 MKMapView Swift 上动画 MKOverlay 的移动
- authentication - Safari 12 和“防止跨站跟踪”模式