Правила безопасности Firebase Realtime Database позволяют вам контролировать доступ к данным, хранящимся в вашей базе данных. Гибкий синтаксис правил позволяет вам создавать правила, которые соответствуют чему угодно, от всех записей в вашу базу данных до операций на отдельных узлах.
Правила безопасности базы данных в реальном времени — это декларативная конфигурация для вашей базы данных. Это означает, что правила определяются отдельно от логики продукта. Это имеет ряд преимуществ: клиенты не отвечают за обеспечение безопасности, ошибочные реализации не поставят под угрозу ваши данные, и, возможно, самое главное, нет необходимости в промежуточном рефери, таком как сервер, для защиты данных от внешнего мира.
В этом разделе описывается базовый синтаксис и структура правил безопасности баз данных в реальном времени, используемых для создания полных наборов правил.
Структурирование правил безопасности
Правила безопасности базы данных в реальном времени состоят из выражений, подобных JavaScript, содержащихся в документе JSON. Структура ваших правил должна соответствовать структуре данных, которые вы храните в своей базе данных.
Базовые правила определяют набор узлов, которые необходимо защитить, используемые методы доступа (например, чтение, запись) и условия , при которых доступ либо разрешен, либо запрещен. В следующих примерах наши условия будут простыми true
и false
утверждениями, но в следующей теме мы рассмотрим более динамичные способы выражения условий.
Так, например, если мы пытаемся защитить child_node
под parent_node
, общий синтаксис, который нам нужно использовать, следующий:
{ "rules": { "parent_node": { "child_node": { ".read": <condition>, ".write": <condition>, ".validate": <condition>, } } } }
Давайте применим этот шаблон. Например, предположим, что вы отслеживаете список сообщений и у вас есть данные, которые выглядят следующим образом:
{ "messages": { "message0": { "content": "Hello", "timestamp": 1405704370369 }, "message1": { "content": "Goodbye", "timestamp": 1405704395231 }, ... } }
Ваши правила должны быть структурированы аналогичным образом. Вот набор правил для безопасности только для чтения, которые могут иметь смысл для этой структуры данных. Этот пример иллюстрирует, как мы указываем узлы базы данных, к которым применяются правила, и условия оценки правил на этих узлах.
{ "rules": { // For requests to access the 'messages' node... "messages": { // ...and the individual wildcarded 'message' nodes beneath // (we'll cover wildcarding variables more a bit later).... "$message": { // For each message, allow a read operation if <condition>. In this // case, we specify our condition as "true", so read access is always granted. ".read": "true", // For read-only behavior, we specify that for write operations, our // condition is false. ".write": "false" } } } }
Основные правила Операции
Существует три типа правил для обеспечения безопасности в зависимости от типа операции, выполняемой над данными: .write
, .read
, и .validate
. Вот краткое изложение их целей:
Типы правил | |
---|---|
.читать | Описывает, разрешено ли пользователям читать данные и когда это происходит. |
.писать | Описывает, разрешено ли записывать данные и когда это происходит. |
.проверить | Определяет, как будет выглядеть правильно отформатированное значение, имеет ли оно дочерние атрибуты и тип данных. |
Подстановочные переменные захвата
Все операторы правил указывают на узлы. Оператор может указывать на определенный узел или использовать переменные захвата $
wildcard для указания на наборы узлов на уровне иерархии. Используйте эти переменные захвата для хранения значений ключей узлов для использования внутри последующих операторов правил. Этот метод позволяет вам писать более сложные условия Rules , которые мы рассмотрим более подробно в следующей теме.
{ "rules": { "rooms": { // this rule applies to any child of /rooms/, the key for each room id // is stored inside $room_id variable for reference "$room_id": { "topic": { // the room's topic can be changed if the room id has "public" in it ".write": "$room_id.contains('public')" } } } } }
Динамические переменные $
также могут использоваться параллельно с постоянными именами путей. В этом примере мы используем переменную $other
для объявления правила .validate
, которое гарантирует, что у widget
нет дочерних элементов, кроме title
и color
. Любая запись, которая приведет к созданию дополнительных дочерних элементов, потерпит неудачу.
{ "rules": { "widget": { // a widget can have a title or color attribute "title": { ".validate": true }, "color": { ".validate": true }, // but no other child paths are allowed // in this case, $other means any key excluding "title" and "color" "$other": { ".validate": false } } } }
Каскад правил чтения и записи
Правила .read
и .write
работают сверху вниз, при этом более мелкие правила переопределяют более глубокие правила. Если правило предоставляет разрешения на чтение или запись по определенному пути, то оно также предоставляет доступ ко всем дочерним узлам под ним. Рассмотрим следующую структуру:
{ "rules": { "foo": { // allows read to /foo/* ".read": "data.child('baz').val() === true", "bar": { /* ignored, since read was allowed already */ ".read": false } } } }
Эта структура безопасности позволяет читать /bar/
всякий раз, когда /foo/
содержит дочерний baz
со значением true
. Правило ".read": false
в /foo/bar/
здесь не действует, поскольку доступ не может быть отозван дочерним путем.
Хотя это может показаться не совсем интуитивно понятным, это мощная часть языка правил, которая позволяет реализовать очень сложные привилегии доступа с минимальными усилиями. Это будет проиллюстрировано, когда мы перейдем к безопасности на основе пользователей далее в этом руководстве.
Обратите внимание, что правила .validate
не каскадируются. Все правила проверки должны быть выполнены на всех уровнях иерархии, чтобы запись была разрешена.
Правила — это не фильтры
Правила применяются атомарным образом. Это означает, что операция чтения или записи немедленно завершается неудачей, если в этом месте или в родительском месте нет правила, предоставляющего доступ. Даже если каждый затронутый дочерний путь доступен, чтение в родительском месте полностью завершится неудачей. Рассмотрим эту структуру:
{ "rules": { "records": { "rec1": { ".read": true }, "rec2": { ".read": false } } } }
Без понимания того, что правила оцениваются атомарно, может показаться, что выборка пути /records/
вернет rec1
, но не rec2
. Однако фактическим результатом является ошибка:
JavaScript
var db = firebase.database(); db.ref("records").once("value", function(snap) { // success method is not called }, function(err) { // error callback triggered with PERMISSION_DENIED });
Objective-C
FIRDatabaseReference *ref = [[FIRDatabase database] reference]; [[_ref child:@"records"] observeSingleEventOfType:FIRDataEventTypeValue withBlock:^(FIRDataSnapshot *snapshot) { // success block is not called } withCancelBlock:^(NSError * _Nonnull error) { // cancel block triggered with PERMISSION_DENIED }];
Быстрый
var ref = FIRDatabase.database().reference() ref.child("records").observeSingleEventOfType(.Value, withBlock: { snapshot in // success block is not called }, withCancelBlock: { error in // cancel block triggered with PERMISSION_DENIED })
Ява
FirebaseDatabase database = FirebaseDatabase.getInstance(); DatabaseReference ref = database.getReference("records"); ref.addListenerForSingleValueEvent(new ValueEventListener() { @Override public void onDataChange(DataSnapshot snapshot) { // success method is not called } @Override public void onCancelled(FirebaseError firebaseError) { // error callback triggered with PERMISSION_DENIED }); });
ОТДЫХ
curl https://docs-examples.firebaseio.com/rest/records/ # response returns a PERMISSION_DENIED error
Поскольку операция чтения в /records/
является атомарной, и нет правила чтения, которое предоставляет доступ ко всем данным в /records/
, это вызовет ошибку PERMISSION_DENIED
. Если мы оценим это правило в симуляторе безопасности в нашей консоли Firebase , мы увидим, что операция чтения была отклонена, поскольку ни одно правило чтения не разрешало доступ к пути /records/
. Однако обратите внимание, что правило для rec1
никогда не оценивалось, поскольку его не было в запрошенном нами пути. Чтобы получить rec1
, нам нужно будет получить к нему прямой доступ:
JavaScript
var db = firebase.database(); db.ref("records/rec1").once("value", function(snap) { // SUCCESS! }, function(err) { // error callback is not called });
Objective-C
FIRDatabaseReference *ref = [[FIRDatabase database] reference]; [[ref child:@"records/rec1"] observeSingleEventOfType:FEventTypeValue withBlock:^(FIRDataSnapshot *snapshot) { // SUCCESS! }];
Быстрый
var ref = FIRDatabase.database().reference() ref.child("records/rec1").observeSingleEventOfType(.Value, withBlock: { snapshot in // SUCCESS! })
Ява
FirebaseDatabase database = FirebaseDatabase.getInstance(); DatabaseReference ref = database.getReference("records/rec1"); ref.addListenerForSingleValueEvent(new ValueEventListener() { @Override public void onDataChange(DataSnapshot snapshot) { // SUCCESS! } @Override public void onCancelled(FirebaseError firebaseError) { // error callback is not called } });
ОТДЫХ
curl https://docs-examples.firebaseio.com/rest/records/rec1 # SUCCESS!
Перекрывающиеся заявления
К узлу может быть применено более одного правила. В случае, когда несколько выражений правил идентифицируют узел, метод доступа отклоняется, если любое из условий false
:
{ "rules": { "messages": { // A rule expression that applies to all nodes in the 'messages' node "$message": { ".read": "true", ".write": "true" }, // A second rule expression applying specifically to the 'message1` node "message1": { ".read": "false", ".write": "false" } } } }
В приведенном выше примере чтение узла message1
будет отклонено, поскольку второе правило всегда false
, даже если первое правило всегда true
.
Следующие шаги
Вы можете углубить свое понимание правил безопасности базы данных Firebase Realtime:
Изучите следующую важную концепцию языка Rules — динамические условия , которые позволяют вашим Rules проверять авторизацию пользователя, сравнивать существующие и входящие данные, проверять входящие данные, проверять структуру запросов, поступающих от клиента, и многое другое.
Ознакомьтесь с типичными вариантами использования безопасности и определениями правил безопасности Firebase, которые их охватывают .