Firebase Auth, CRUD, правила безопасности

В этом руководстве по Firestore CRUD Vue.js я расскажу вам, как создать реальное веб-приложение To-Do с аутентификацией Firebase.

Это вторая часть учебника Firestore CRUD Vue.js.

🔥 Часть № 1: аутентификация Firebase для Vue.js

🔥 Часть № 2. Создание безопасного приложения для работы с задачами с помощью Vue.js + 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 перейдите в раздел srccomponents → создайте файл 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)">&#10005;</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 г.