Catch up on everthing we announced at this year's Firebase Summit. Learn more

Изучите основной синтаксис языка правил баз данных в реальном времени

Правила безопасности базы данных Firebase в реальном времени позволяют вам контролировать доступ к данным, хранящимся в вашей базе данных. Гибкий синтаксис правил позволяет создавать правила, которые соответствуют чему угодно, от всех операций записи в базу данных до операций на отдельных узлах.

В режиме реального времени базы данных Правила безопасности являются декларативными конфигурации для вашей базы данных. Это означает, что правила определяются отдельно от логики продукта. Это имеет ряд преимуществ: клиенты не несут ответственности за обеспечение безопасности, ошибочные реализации не поставят под угрозу ваши данные, и, что, возможно, самое главное, нет необходимости в промежуточном рефери, таком как сервер, для защиты данных от внешнего мира.

В этом разделе описывается базовый синтаксис и структура правил безопасности баз данных в реальном времени, используемых для создания полных наборов правил.

Структурирование ваших правил безопасности

Правила безопасности баз данных в реальном времени состоят из выражений, подобных 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 . Вот краткое описание их целей:

Типы правил
.читать Описывает, разрешено ли пользователям читать данные и когда.
.записывать Описывает, разрешено ли и когда записывать данные.
.validate Определяет, как будет выглядеть правильно отформатированное значение, есть ли у него дочерние атрибуты и тип данных.

Переменные захвата подстановочных знаков

Все операторы правил указывают на узлы. Заявление может указывать на конкретный узел или использовать $ подстановочного переменный захват до точки наборов узлов на уровне иерархии. Используйте эти переменные захвата для хранения значений ключей узлов для использования в последующих операторах правил. Этот метод позволяет создавать более сложные правила условия, что - то мы рассмотрим более подробно в следующем разделе.

{
  "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
});
Цель-C
Примечание: Этот Firebase продукт не доступен на цели клипа App.
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
}];
Быстрый
Примечание: Этот Firebase продукт не доступен на цели клипа App.
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
});
Цель-C
Примечание: Этот Firebase продукт не доступен на цели клипа App.
FIRDatabaseReference *ref = [[FIRDatabase database] reference];
[[ref child:@"records/rec1"] observeSingleEventOfType:FEventTypeValue withBlock:^(FIRDataSnapshot *snapshot) {
    // SUCCESS!
}];
Быстрый
Примечание: Этот Firebase продукт не доступен на цели клипа App.
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 в реальном времени:

  • Изучает следующую основную концепцию языка правил, динамических условия , которые позволяют вашим правила проверки авторизации пользователя, сравнить существующие и поступающие данные, проверять входящие данные, проверьте структуру запросов , поступающих от клиента, и многой другой.

  • Обзор типичных случаев использования безопасности и определения Firebase правил безопасности , которые обращаются к ним .