Firebase Auth, CRUD, правила безопасности
В этом руководстве по Firestore CRUD Vue.js я расскажу вам, как создать реальное веб-приложение To-Do с аутентификацией Firebase.
Это вторая часть учебника Firestore CRUD Vue.js.
🔥 Часть № 1: аутентификация Firebase для Vue.js
🔥 Часть № 2. Создание безопасного приложения для работы с задачами с помощью Vue.js + Firestore (вы здесь)
Что мы строим?
Приступаем 🚀
- Создать компонент и маршрут для представления To-Do
- Структура данных Firestore для приложения To-Do
- Добавить пользовательские данные в Cloud Firestore
- Получите пользовательские данные с помощью Cloud Firestore
- Обновить данные пользователя в базе данных Firestore
- Удалить данные из Cloud Firestore
- Защитите приложение To-Do с помощью правил Firestore
Если у вас уже запущен проект vue.js и вы добавили Firebase в свой проект из руководства Аутентификация Firebase для Vue.js, перейдите к разделу Создание компонента и маршрут для Просмотр списка дел.
Запуск и запуск стартового проекта Vue.js
Идем дальше и скачиваем стартовый проект vue.js.
CD в проект на вашем терминале и выполните следующую команду:
npm install
После завершения установки зависимостей запустите приложение, перейдя по предоставленному URL-адресу localhost.
Если все в порядке, у вас должно получиться вот такое приложение:
Настройте Firebase для проекта Vue.js
Как только проект vue будет запущен и запущен, следующим шагом будет добавление Firebase в ваш проект, перейдя в main.js и заменив код конфигурации из вашего проекта Firebase.
const firebaseConfig = { apiKey: "****************************", authDomain: "****************************", databaseURL: "****************************", projectId: "****************************", storageBucket: "****************************", messagingSenderId: "****************************", appId: "****************************" };
Если кнопки входа OAuth, такие как Facebook и Google, не работают, вам придется настроить их, как описано в руководстве Аутентификация Firebase для Vue.js.
Создайте компонент Todo и маршрут
В проекте vue.js перейдите в раздел src → components → создайте файл Todos.vue
.
Затем добавьте скаффолд-код.
<template> </template> <script> export default { } </script> <style> </style>
Когда это будет сделано, перейдите в папку router → index.js
файл и импортируйте компонент вверху.
import Todos from '@/components/Todos'
Затем добавьте новый объект для todo внутри массива маршрутизаторов.
{ path: '/todo', name: 'Todos', component: Todos, meta: { auth: true } }
Установите флаг auth:true
в этом объекте маршрута todo, который гарантирует, что только аутентифицированные пользователи будут иметь доступ к компоненту страницы todo.
Вернитесь к файлу ToDo.vue
и добавьте HTML-код для навигации и заголовка внутри между начальным и конечным тегами template.
<section> <navigation></navigation> <h5 class="center-align">To-Dos</h5> </section>
Если вам интересно, в этом проекте я использую Materialize CSS Framework.
Переходим в раздел скриптов.
Вверху добавлен компонент <navigation>
.
<script> import navigation from "@/components/NavBar.vue"; export default { components: { navigation } }; </script>
Затем импортируйте NavBar.vue
и добавьте его внутрь объекта компонентов.
Было бы неплохо иметь элемент навигации в верхней части страницы todos. Перейдите к NavBar.vue
и добавьте следующий код внутрь элемента ul
.
<li v-show="user"> <router-link to="/todo">To Do</router-link> </li>
На этом этапе вы можете получить доступ к странице задач, перейдя по адресу http://localhost:8080/todo.
Порт вашего локального хоста может быть другим. В моем случае это 8080.
Прежде чем погрузиться в приложение, давайте уделим немного времени и подумаем, как структурировать/моделировать данные для приложения To-Do.
Структурирование данных Firestore для приложения Todo
Очень важно иметь представление о том, как моделировать или структурировать данные Firestore, прежде чем приступать к написанию кода.
Вот что я придумал:
Приложение для моделирования данных Firestore
Во-первых, создайте коллекцию верхнего уровня, называемую пользователями, и добавьте uid текущего пользователя, вошедшего в систему, в качестве идентификатора документа. Вы можете легко получить uid, используя firebase.auth().uid
.
Основная причина использования uid вместо автоматически сгенерированного идентификатора заключается в защите данных каждого пользователя с помощью правил Firebase, о которых я расскажу в разделе Безопасное приложение To-Do с использованием правил Firestore.
Внутри документа каждого пользователя создайте подколлекцию с именем todos, в которой будет несколько документов todos с некоторыми полями, такими как название, isCompleted и т. д.
Довольно прямолинейно! 🙂
Для этого проекта существует несколько способов структурирования данных. Не стесняйтесь, дайте мне знать, если вы найдете лучшее решение, чем приведенное ниже для этого приложения.
Создание пользовательских данных в облачном хранилище Firestore
Первый шаг — добавить фрагмент кода HTML под заголовком в файле ToDo.vue.
Он имеет ul
с одним элементом списка, который является заголовком списка.
<ul class="collection with-header"> <li class="collection-header"> <div class="row"> <div class="input-field col s10"> <input id="new_todo" type="text" class="validate" v-model="todo.title" /> </div> <div class="input-field col s2"> <button class="btn" @click="addTodo">Add</button> </div> </div> </li> </ul>
Как видите, у меня есть поле ввода, которое привязывает todo.title
, а кнопка добавления привязана к событию щелчка с функцией обратного вызова addTodo()
.
Добавление следующего кода CSS приведет к тому, что ul будет иметь размер 500 пикселей и центрироваться на холсте.
<style> .collection.with-header { max-width: 500px; margin: 0 auto; } </style>
Затем объявите объект todo со свойством title.
data() { return { todo: { title: "", } }; }
Затем импортируйте firebase вверху раздела JavaScript.
import firebase from "firebase";
Наконец, определите функцию обратного вызова addTodo()
внутри methods:{}
и сделайте запрос на добавление новых данных todo в Cloud Firestore.
addTodo() { firebase .firestore() .collection("users") .doc(firebase.auth().currentUser.uid) .collection("todos") .add({ title: this.todo.title, createdAt: new Date(), isCompleted: false, }) }
Ознакомьтесь с разделом Изучите CRUD-запросы Firestore, если хотите освежить в памяти.
Давайте посмотрим на запрос Firebase для добавления новых данных.
Получите ссылку на коллекцию пользователей и вызовите для нее метод doc()
, передав uid
текущего пользователя в качестве аргумента.
После этого получите ссылку на подколлекцию с именем todos и добавьте документ с помощью метода add()
, передав объект JavaScript, который имеет три пары ключ-значение:
- title:строка
- createdAt:отметка времени
- isCompleted:логическое значение
На этом этапе приложение будет выглядеть так, как показано ниже.
Давайте добавим новый пункт todo.
Хороший!
Затем извлеките данные из Cloud Firestore.
Получите пользовательские данные с помощью Cloud Firestore
Первый шаг — определить массив todos
, который будет содержать объекты todo текущего пользователя, вошедшего в систему.
data() { return { todos: [], todo: { title: "" } }; }
Nдальше, объявите getTodos()
функцию внутри methods:{}
В приведенном ниже коде я использую асинхронный синтаксис ожидания для этой асинхронной операции.
Этот запрос очень похож на add()
. Получите ссылку на коллекцию пользователей и вызовите метод doc()
, передав uid
текущего пользователя в качестве аргумента.
async getTodos() { var todosRef = await firebase.firestore() .collection("users") .doc(firebase.auth().currentUser.uid) .collection("todos"); todosRef.onSnapshot(snap => { this.todos = []; snap.forEach(doc => { var todo = doc.data(); todo.id = doc.id; this.todos.push(todo); }); }); }
Затем получите ссылку на подколлекцию todos и сохраните объект запроса в переменной с именем todosRef
.
Запустите метод onSnapshot()
для объекта запроса todoRef. Я мог бы использовать get()
, но я хочу видеть изменение представления в реальном времени по мере изменения базы данных Cloud Firestore.
Внутри метода onSnapshot()
выполните итерацию по объекту привязки и установите данные документа с помощью doc.data()
в переменную с именем todo
. Кроме того, добавьте свойство id к этому объекту и установите для него автоматически сгенерированный идентификатор с помощью doc.id
.
Наконец, поместите объект todo в созданный ранее массив todos.
Обязательно сбросьте массив todos, который находится в первой строке внутри метода onSnapshot()
, чтобы избежать дублирования данных.
Теперь вызовите функцию getTodos()
внутри метода created() {}
.
created() { this.getTodos(); },
Третий шаг — добавить HTML-код.
Внутри неупорядоченного списка после первого элемента списка в стиле заголовка добавьте следующее:
<li class="collection-item" v-for="todo in todos" :key="todo.id"> {{todo.title}} </li>
Переберите массив todos, используя v-for
, и покажите заголовок, используя todo.title
.
Довольно прямолинейно!
Обновите данные пользователя в Cloud Firestore
Было бы неплохо иметь флажок справа от каждого элемента списка дел, чтобы пользователи могли проверять, когда это сделано.
Чтобы это произошло, добавьте ввод флажка в существующий HTML-код.
<li class="collection-item" v-for="todo in todos" :key="todo.id" :class="{ fade: todo.isCompleted }"> {{todo.title}} <span class="secondary-content"> <label> <input type="checkbox" class="filled-in" :checked="todo.isCompleted" @change="updateTodoItem(todo.id, $event)" /> <span></span> </label> </span> </li>
К каждому элементу списка задач будет привязано событие изменения. Функция обратного вызова для события изменения — updatedTodoItem()
, которая принимает два аргумента.
Первый — это автоматически сгенерированный идентификатор элемента списка задач, к которому я могу получить доступ, используя todo.id
, а второй — объект event
.
Давайте объявим функцию обратного вызова события изменения updateTodoItem()
внутри объекта me thods:{}
.
updateTodoItem(docId, e) { var isChecked = e.target.checked; firebase .firestore() .collection("users") .doc(firebase.auth().currentUser.uid) .collection("todos") .doc(docId) .update({ isCompleted: isChecked }); },
Когда пользователь нажимает на флажок, зафиксируйте проверенный статус с помощью e.target.checked
и установите его в переменную isChecked
.
Как видите, этот запрос очень похож на предыдущий.
Используйте метод update()
, чтобы изменить значение isCompleted
, присвоив ему значение isChecked
.
Если вы установите флажок на этом этапе, вы увидите, что значение isCompleted
изменится на true
в Cloud Firestore.
Изменение немедленно отразится на представлении.
Постоянство происходит, потому что я установил значение todo.isCompleted
для свойства :checked
HTML.
Хороший.
Давайте перейдем к тому, как пользователи могут удалять свои собственные элементы списка дел.
Удалить данные пользователя из Cloud Firestore
Добавьте элемент span внутри элемента списка с помощью класса с именем deleteIcon.
<li class="collection-item" v-for="todo in todos" :key="todo.id" :class="{ fade: todo.isCompleted }"> <span class="deleteIcon" @click="deleteToDo(todo.id)">✕</span> {{todo.title}} <span class="secondary-content"> <label> <input type="checkbox" class="filled-in" :checked="todo.isCompleted" @change="completedPressed(todo.id, $event)" /> <span></span> </label> </span> </li>
Прикрепите событие click к элементу span с помощью функции обратного вызова deleteTodo()
и передайте ей идентификатор в качестве аргумента.
Затем создайте функцию deleteTodo()
внутри methods:{}
.
deleteToDo(docId) { firebase .firestore() .collection("users") .doc(firebase.auth().currentUser.uid) .collection("todos") .doc(docId) .delete(); }
Удалить довольно просто, и все, что вам нужно сделать, это вызвать метод delete()
для соответствующего документа и готово!
Вот CSS для раздела удаления.
.deleteIcon { margin-right: 10px; cursor: pointer; } .deleteIcon:hover { opacity: 0.5; }
На этом этапе приложение todo CRUD полностью функционально.
Но…
Это НЕ безопасно!
Защитите данные пользователей с помощью правил Firestore
Защитите данные пользователей с помощью правил Firestore
НАСТОЯЩАЯ безопасность заключается в правилах Firebase.
Позвольте мне повторить еще раз.
НАСТОЯЩАЯ безопасность обеспечивается правилами Firebase.
Просмотрите текущие правила безопасности, перейдя на вкладку Консоль Firebase → Аутентификация → вкладка Правила.
Это правило безопасности позволит любому читать или записывать в любое место базы данных Cloud Firestore, что хорошо для разработки и тестирования, но определенно НЕ для производства.
service cloud.firestore { match /databases/{database}/documents { match /{document=**} { allow read, write; } } }
Давайте изменим правила Firebase, чтобы только зарегистрированные пользователи могли читать или писать в базу данных, как показано ниже. Вы можете получить доступ к uid с помощью request.auth.uid и проверить, существует ли он.
service cloud.firestore { match /databases/{database}/documents { match /{document=**} { allow read, write: if request.auth.uid != null; } } }
Это гарантирует, что только аутентифицированные пользователи могут иметь разрешение на чтение или запись (создание, обновление и удаление) в базу данных Cloud Firestore.
Но он может позволить любому пользователю изменять любые данные других пользователей.
Например, предположим, что пользователь А вошел в систему и добавил некоторые элементы списка дел. Пользователь B зарегистрирован на другой машине, а также добавил несколько задач. Если пользователь А взламывает uid пользователя Б из браузера, пользователь А может получить данные пользователя Б, пока пользователь А вошел в систему, что НЕЗАЩИЩЕНО.
Чтобы предотвратить это, правило Firebase должно разрешать пользователям изменять только свои собственные данные, а не чужие.
service cloud.firestore { match /databases/{database}/documents { match /users/{uid}/todos/{todoId} { allow read, write: if request.auth.uid == uid } } }
Теперь только авторизованные пользователи могут изменять свои данные по следующему пути к базе данных users/{userId}/todos/{todoId}, если userId в коллекции пользователей совпадает с uid текущего пользователя, вошедшего в систему.
Вот оно! 🙂
Вывод
В этом руководстве по Firestore CRUD Vue.js вы узнали, как выполнять запросы CRUD с помощью Firestore, создав полнофункциональный To- Сделать заявку.
Затем я показал, как защитить приложение To-Do с помощью правил Firestore.
Загрузите исходный код примера на Github.
ДАЛЕЕ → Создание пользовательской платежной формы с использованием Stripe + Cloud Functions
Первоначально опубликовано на https://softauthor.com 29 июля 2019 г.