Poznaj podstawową składnię języka reguł zabezpieczeń bazy danych czasu rzeczywistego

Reguły zabezpieczeń Bazy danych czasu rzeczywistego Firebase pozwalają kontrolować dostęp do danych przechowywanych w swojej bazie danych. Elastyczna składnia reguł umożliwia reguły, które pasują do wszystkiego, od wszystkich zapisów do bazy danych po operacje w poszczególnych węzłach.

Reguły zabezpieczeń bazy danych czasu rzeczywistego to konfiguracja deklaratywna bazy danych. Oznacza to, że że reguły są zdefiniowane niezależnie od logiki usługi. Ten Ma wiele zalet: klienci nie są odpowiedzialni za bezpieczeństwo, nie zaszkodzą Twoim danym i, co najważniejsze, nie potrzeba pośrednika, np. serwera, do ochrony danych z całego świata.

W tym temacie opisano podstawową składnię i strukturę reguł zabezpieczeń Bazy danych czasu rzeczywistego służy do tworzenia kompletnych zestawów reguł.

Tworzenie struktury reguł zabezpieczeń

Reguły zabezpieczeń bazy danych czasu rzeczywistego składają się z wyrażeń podobnych do JavaScriptu zawartych w Dokument JSON. Struktura reguł powinna być taka sama jak przechowywane w bazie danych.

Reguły podstawowe określają zbiór węzłów, które mają zostać zabezpieczone, metody dostępu (np. odczyt, zapisu), a także warunki, w których przypadku dostęp jest dozwolony lub zabroniony. W poniższych przykładach nasze warunki będą proste: true oraz false. jednak w następnym temacie omówimy bardziej dynamiczne sposoby wyraźne warunki.

Jeśli na przykład próbujemy zabezpieczyć child_node na podstawie parent_node, ogólna składnia to:

{
  "rules": {
    "parent_node": {
      "child_node": {
        ".read": <condition>,
        ".write": <condition>,
        ".validate": <condition>,
      }
    }
  }
}

Zastosujmy ten wzór. Załóżmy na przykład, że śledzisz listę wiadomości i mają dane podobne do tych:

{
  "messages": {
    "message0": {
      "content": "Hello",
      "timestamp": 1405704370369
    },
    "message1": {
      "content": "Goodbye",
      "timestamp": 1405704395231
    },
    ...
  }
}

Struktura reguł powinna wyglądać podobnie. Oto zestaw zabezpieczeń tylko do odczytu, które mogą mieć sens w przypadku tej struktury danych. Ten Pokazujemy na przykład, jak określamy węzły bazy danych, do których mają zastosowanie reguły, oraz warunki oceny reguł w tych węzłach.

{
  "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"
      }
    }
  }
}

Operacje na regułach podstawowych

Istnieją 3 typy reguł egzekwowania bezpieczeństwa w zależności od typu wykonywanie operacji na danych: .write, .read i .validate. Tutaj Oto krótkie podsumowanie ich przeznaczenia:

Typy reguł
.read, Określa, czy i kiedy użytkownicy mogą odczytywać dane.
.write, Określa, czy i kiedy można zapisywać dane.
.validate, Określa, jak będzie wyglądać prawidłowo sformatowana wartość atrybut podrzędny oraz typ danych.

Zmienne przechwytywania symboli wieloznacznych

Wszystkie instrukcje reguł wskazują na węzły. Wyrażenie może wskazywać lub użyj symbolu wieloznacznego $, aby przechwytywać zmienne w celu wskazania zbiorów węzłów danego poziomu. Użyj tych zmiennych przechwytywania, aby przechowywać wartość węzła do użytku w kolejnych instrukcjach reguł. Ta technika pozwala pisać bardziej złożonychRules warunków, które omówimy bardziej szczegółowo w następnym temacie.

{
  "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')"
        }
      }
    }
  }
}

Dynamicznych zmiennych $ można też używać równolegle ze stałą ścieżką. nazw. W tym przykładzie używamy zmiennej $other do deklarowania regułę .validate, która zapewnia, widget nie ma elementów potomnych innych niż title i color. Każdy zapis, który spowodowałby utworzenie dodatkowych elementów podrzędnych, kończy się niepowodzeniem.

{
  "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 }
    }
  }
}

Kaskada reguł odczytu i zapisu

Reguły .read i .write działają od góry, ale niżej i zastępują reguły bardziej zaawansowane. Jeśli reguła przyznaje uprawnienia do odczytu lub zapisu w określonym , a potem daje również dostęp do wszystkich podrzędnych węzłów. Weź pod uwagę taką strukturę:

{
  "rules": {
     "foo": {
        // allows read to /foo/*
        ".read": "data.child('baz').val() === true",
        "bar": {
          /* ignored, since read was allowed already */
          ".read": false
        }
     }
  }
}

Ta struktura zabezpieczeń umożliwia odczyt z /bar/ w dowolnym momencie /foo/ zawiera element podrzędny baz o wartości true. Reguła ".read": false w regule /foo/bar/ nie ma ponieważ dostęp nie może zostać unieważniony przez ścieżkę podrzędną.

Choć może się to nie wydawać od razu intuicyjne, to bardzo ważna część języka reguł i umożliwia wdrożenie bardzo złożonych uprawnień dostępu przy minimalnym nakładzie pracy. Ten zabezpieczeń opartych na użytkownikach w dalszej części tego przewodnika.

Pamiętaj, że reguły .validate nie są kaskadowe. Wszystkie reguły weryfikacji musi być spełniony na wszystkich poziomach hierarchii, aby możliwy był zapis.

Reguły nie są filtrami

Reguły są stosowane szczegółowo. Oznacza to, że odczyt lub zapis operacja kończy się niepowodzeniem natychmiast, jeśli w tej lokalizacji lub w lokalizacja nadrzędna, która przyznaje dostęp. Nawet jeśli każda ścieżka podrzędna, której dotyczy problem, jest dostępna, w lokalizacji nadrzędnej nie uda się całkowicie. Weźmy pod uwagę tę strukturę:

{
  "rules": {
    "records": {
      "rec1": {
        ".read": true
      },
      "rec2": {
        ".read": false
      }
    }
  }
}

Może się wydawać, że bez zrozumienia reguł np. pobieranie ścieżki /records/ zwróci rec1 ale nie rec2. Rzeczywisty wynik jest jednak błędem:

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
Uwaga: ta usługa Firebase nie jest dostępna w miejscu docelowym wycinka aplikacji.
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
}];
Swift
Uwaga: ta usługa Firebase nie jest dostępna w miejscu docelowym wycinka aplikacji.
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
})
Java
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
  });
});
REST
curl https://docs-examples.firebaseio.com/rest/records/
# response returns a PERMISSION_DENIED error

Operacja odczytu w /records/ jest niepodzielna i nie ma odczytują regułę, która przyznaje dostęp do wszystkich danych w jednostce organizacyjnej /records/, spowoduje to błąd PERMISSION_DENIED. Jeśli ocenimy w symulatorze zabezpieczeń w konsoli Firebase, widzimy, że odmowa odczytu, ponieważ żadna reguła odczytu nie zezwala na dostęp Ścieżka /records/. Pamiętaj jednak, że reguła dla rec1 nie została nigdy oceniona, ponieważ nie znajdowała się na żądanej ścieżce. Do pobrania rec1, musimy uzyskać do niego bezpośredni dostęp:

JavaScript
var db = firebase.database();
db.ref("records/rec1").once("value", function(snap) {
  // SUCCESS!
}, function(err) {
  // error callback is not called
});
Objective-C
Uwaga: ta usługa Firebase nie jest dostępna w miejscu docelowym wycinka aplikacji.
FIRDatabaseReference *ref = [[FIRDatabase database] reference];
[[ref child:@"records/rec1"] observeSingleEventOfType:FEventTypeValue withBlock:^(FIRDataSnapshot *snapshot) {
    // SUCCESS!
}];
Swift
Uwaga: ta usługa Firebase nie jest dostępna w miejscu docelowym wycinka aplikacji.
var ref = FIRDatabase.database().reference()
ref.child("records/rec1").observeSingleEventOfType(.Value, withBlock: { snapshot in
    // SUCCESS!
})
Java
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
  }
});
REST
curl https://docs-examples.firebaseio.com/rest/records/rec1
# SUCCESS!

Nakładające się oświadczenia

Do węzła może być stosowana więcej niż 1 reguła. W jeśli wiele wyrażeń reguł identyfikuje węzeł, metoda dostępu to odmowa, jeśli którykolwiek z warunków jest 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"
      }
    }
  }
}

W powyższym przykładzie odczyty do węzła message1 będą odrzucona, ponieważ druga reguła ma zawsze wartość false, mimo że pierwsza ma zawsze wartość true.

Dalsze kroki

Możesz lepiej zrozumieć reguły zabezpieczeń Bazy danych czasu rzeczywistego Firebase:

  • Poznaj kolejną główną pojęcie języka Rules – dynamiczny. warunki, dzięki którym Rules może sprawdzić użytkownika autoryzacja, porównywanie istniejących i przychodzących danych, weryfikowanie danych przychodzących, sprawdzanie strukturę zapytań pochodzących od klienta itd.

  • Przejrzyj typowe przypadki użycia zabezpieczeń i odpowiadające im definicje reguł zabezpieczeń Firebase.