Trang này được dịch bởi Cloud Translation API.
Switch to English

Bảo mật dữ liệu của bạn

Quy tắc cơ sở dữ liệu thời gian thực là cấu hình khai báo cho cơ sở dữ liệu của bạn. Điều này có nghĩa là các quy tắc được xác định riêng biệt với logic sản phẩm. Điều này có một số lợi thế: khách hàng không chịu trách nhiệm thực thi bảo mật, việc triển khai lỗi sẽ không ảnh hưởng đến dữ liệu của bạn và có lẽ quan trọng nhất là không cần một trọng tài trung gian, như máy chủ, để bảo vệ dữ liệu khỏi thế giới.

Cấu trúc các quy tắc của bạn

Các quy tắc cơ sở dữ liệu thời gian thực được tạo thành từ các biểu thức giống như JavaScript có trong tài liệu JSON. Cấu trúc quy tắc của bạn phải tuân theo cấu trúc dữ liệu bạn đã lưu trữ trong cơ sở dữ liệu của mình. Ví dụ: giả sử bạn đang theo dõi danh sách các tin nhắn và có dữ liệu giống như thế này:

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

Quy tắc của bạn nên được cấu trúc theo cách tương tự. Dưới đây là một tập hợp các quy tắc có thể có ý nghĩa đối với cấu trúc dữ liệu này.

{
  "rules": {
    "messages": {
      "$message": {
        // only messages from the last ten minutes can be read
        ".read": "data.child('timestamp').val() > (now - 600000)",

        // new messages must have a string content and a number timestamp
        ".validate": "newData.hasChildren(['content', 'timestamp']) && newData.child('content').isString() && newData.child('timestamp').isNumber()"
      }
    }
  }
}

Các loại quy tắc bảo mật

Có ba loại quy tắc cho việc thực thi bảo mật: .write , .read , và .validate . Dưới đây là một bản tóm tắt nhanh chóng về mục đích của họ:

Các loại quy tắc
.đọc Mô tả nếu và khi dữ liệu được người dùng cho phép đọc.
. viết Mô tả nếu và khi dữ liệu được phép viết.
.validate Xác định giá trị được định dạng chính xác sẽ trông như thế nào, cho dù nó có thuộc tính con và kiểu dữ liệu.

Các biến được xác định trước

Có một số biến hữu ích, được xác định trước có thể được truy cập bên trong một định nghĩa quy tắc bảo mật. Chúng tôi sẽ sử dụng hầu hết trong số họ trong các ví dụ dưới đây. Dưới đây là một bản tóm tắt ngắn gọn về từng và một liên kết đến tham chiếu API thích hợp.

Các biến được xác định trước
hiện nay Thời gian hiện tại tính bằng mili giây kể từ kỷ nguyên Linux. Điều này đặc biệt tốt khi xác thực dấu thời gian được tạo bằng firebase.database.ServerValue.TIMESTAMP của SDK.
nguồn gốc Một RuleDataSnapshot biểu thị đường dẫn gốc trong cơ sở dữ liệu Firebase như nó tồn tại trước khi hoạt động đã thử.
dữ liệu mới Một RuleDataSnapshot biểu thị dữ liệu như nó sẽ tồn tại sau khi hoạt động đã thử. Nó bao gồm dữ liệu mới được ghi và dữ liệu hiện có.
dữ liệu Một RuleDataSnapshot biểu thị dữ liệu như nó đã tồn tại trước khi hoạt động đã thử.
$ biến Đường dẫn ký tự đại diện được sử dụng để thể hiện id và khóa con động.
xác thực Đại diện cho tải trọng mã thông báo của người dùng được xác thực.

Các biến này có thể được sử dụng bất cứ nơi nào trong quy tắc của bạn. Ví dụ: các quy tắc bảo mật bên dưới đảm bảo rằng dữ liệu được ghi vào nút /foo/ phải là một chuỗi nhỏ hơn 100 ký tự:

{
  "rules": {
    "foo": {
      // /foo is readable by the world
      ".read": true,

      // /foo is writable by the world
      ".write": true,

      // data written to /foo must be a string less than 100 characters
      ".validate": "newData.isString() && newData.val().length < 100"
    }
  }
}

Dữ liệu hiện có so với Dữ liệu mới

Biến data được xác định trước được sử dụng để tham chiếu dữ liệu trước khi thao tác ghi diễn ra. Ngược lại, biến newData chứa dữ liệu mới sẽ tồn tại nếu thao tác ghi thành công. newData đại diện cho kết quả hợp nhất của dữ liệu mới được ghi và dữ liệu hiện có.

Để minh họa, quy tắc này sẽ cho phép chúng tôi tạo các bản ghi mới hoặc xóa các bản ghi hiện có, nhưng không thực hiện thay đổi đối với dữ liệu không có giá trị hiện có:

// we can write as long as old data or new data does not exist
// in other words, if this is a delete or a create, but not an update
".write": "!data.exists() || !newData.exists()"

Dữ liệu tham chiếu trong các đường dẫn khác

Bất kỳ dữ liệu có thể được sử dụng làm tiêu chí cho các quy tắc. Sử dụng các biến được xác định trước root , datanewData , chúng ta có thể truy cập bất kỳ đường dẫn nào như nó tồn tại trước hoặc sau một sự kiện ghi.

Hãy xem xét ví dụ này, cho phép các thao tác ghi miễn là giá trị của nút /allow_writes/true , nút cha không có bộ cờ readOnly và có một con có tên foo trong dữ liệu mới được viết:

".write": "root.child('allow_writes').val() === true &&
          !data.parent().child('readOnly').exists() &&
          newData.child('foo').exists()"

Đọc và viết quy tắc Cascade

quy tắc .read.write hoạt động từ trên xuống, với quy tắc nông hơn ghi đè quy tắc sâu hơn. Nếu một quy tắc cấp quyền đọc hoặc ghi tại một đường dẫn cụ thể, thì nó cũng cấp quyền truy cập cho tất cả các nút con bên dưới nó. Hãy xem xét cấu trúc sau:

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

Cấu trúc bảo mật này cho phép /bar/ được đọc từ bất cứ khi nào /foo/ chứa baz con có giá trị true . ".read": false quy tắc ".read": false trong /foo/bar/ không có hiệu lực ở đây, vì quyền truy cập không thể bị thu hồi bởi một đường dẫn con.

Mặc dù có vẻ không trực quan ngay lập tức, đây là một phần mạnh mẽ của ngôn ngữ quy tắc và cho phép các đặc quyền truy cập rất phức tạp được thực hiện với nỗ lực tối thiểu. Điều này sẽ được minh họa khi chúng ta nhận được bảo mật dựa trên người dùng sau này trong hướng dẫn này.

Lưu ý rằng quy tắc .validate không xếp tầng. Tất cả các quy tắc xác nhận phải được thỏa mãn ở tất cả các cấp của hệ thống phân cấp để cho phép viết được cho phép.

Quy tắc không phải là bộ lọc

Các quy tắc được áp dụng một cách nguyên tử. Điều đó có nghĩa là thao tác đọc hoặc ghi bị thất bại ngay lập tức nếu không có quy tắc tại địa điểm đó hoặc tại địa điểm chính cấp quyền truy cập. Ngay cả khi mọi đường dẫn con bị ảnh hưởng đều có thể truy cập được, việc đọc tại vị trí cha mẹ sẽ thất bại hoàn toàn. Hãy xem xét cấu trúc này:

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

Nếu không hiểu rằng các quy tắc được đánh giá nguyên tử, có vẻ như việc tìm nạp /records/ path sẽ trả về rec1 nhưng không phải rec2 . Tuy nhiên, kết quả thực tế là một lỗi:

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
});
Mục tiêu-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
}];
Nhanh
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
  });
});
NGHỈ NGƠI
curl https://docs-examples.firebaseio.com/rest/records/
# response returns a PERMISSION_DENIED error

Vì thao tác đọc tại /records/ là nguyên tử và không có quy tắc đọc nào cấp quyền truy cập vào tất cả dữ liệu trong /records/ , nên điều này sẽ gây ra lỗi PERMISSION_DENIED . Nếu chúng tôi đánh giá quy tắc này trong trình giả lập bảo mật trong bảng điều khiển Firebase , chúng tôi có thể thấy rằng thao tác đọc đã bị từ chối:

Attempt to read /records with auth=Success(null)
    /
    /records

No .read rule allowed the operation.
Read was denied.

Hoạt động đã bị từ chối vì không có quy tắc đọc nào cho phép truy cập vào /records/ path, nhưng lưu ý rằng quy tắc cho rec1 không bao giờ được đánh giá vì nó không nằm trong đường dẫn mà chúng tôi yêu cầu. Để tìm nạp rec1 , chúng tôi sẽ cần truy cập trực tiếp vào nó:

JavaScript
var db = firebase.database();
db.ref("records/rec1").once("value", function(snap) {
  // SUCCESS!
}, function(err) {
  // error callback is not called
});
Mục tiêu-C
FIRDatabaseReference *ref = [[FIRDatabase database] reference];
[[ref child:@"records/rec1"] observeSingleEventOfType:FEventTypeValue withBlock:^(FIRDataSnapshot *snapshot) {
    // SUCCESS!
}];
Nhanh
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
  }
});
NGHỈ NGƠI
curl https://docs-examples.firebaseio.com/rest/records/rec1
# SUCCESS!

Quy tắc dựa trên truy vấn

Mặc dù bạn không thể sử dụng quy tắc làm bộ lọc, bạn có thể giới hạn quyền truy cập vào các tập hợp dữ liệu bằng cách sử dụng tham số truy vấn trong quy tắc của mình. Sử dụng query. các biểu thức trong quy tắc của bạn để cấp quyền truy cập đọc hoặc ghi dựa trên các tham số truy vấn.

Ví dụ: quy tắc dựa trên truy vấn sau sử dụng quy tắc bảo mật dựa trên người dùng và quy tắc dựa trên truy vấn để hạn chế quyền truy cập vào dữ liệu trong bộ sưu tập baskets chỉ dành cho giỏ mua hàng mà người dùng đang hoạt động sở hữu:

 "baskets": {
  ".read": "auth.uid != null &&
            query.orderByChild == 'owner' &&
            query.equalTo == auth.uid" // restrict basket access to owner of basket
}
 

Truy vấn sau, bao gồm các tham số truy vấn trong quy tắc, sẽ thành công:

 db.ref("baskets").orderByChild("owner")
                 .equalTo(auth.currentUser.uid)
                 .on("value", cb)                 // Would succeed
 

Tuy nhiên, các truy vấn không bao gồm các tham số trong quy tắc sẽ không thành công với lỗi PermissionDenied :

 db.ref("baskets").on("value", cb)                 // Would fail with PermissionDenied
 

Bạn cũng có thể sử dụng các quy tắc dựa trên truy vấn để giới hạn số lượng khách hàng tải xuống thông qua các thao tác đọc.

Ví dụ: quy tắc sau giới hạn chỉ đọc quyền truy cập vào 1000 kết quả đầu tiên của truy vấn, theo thứ tự ưu tiên:

 messages: {
  ".read": "query.orderByKey &&
            query.limitToFirst <= 1000"
}

// Example queries:

db.ref("messages").on("value", cb)                // Would fail with PermissionDenied

db.ref("messages").limitToFirst(1000)
                  .on("value", cb)                // Would succeed (default order by key)

 

Các query. sau đây query. biểu thức có sẵn trong Quy tắc cơ sở dữ liệu thời gian thực.

Biểu thức quy tắc dựa trên truy vấn
Biểu hiện Kiểu Sự miêu tả
truy vấn.orderByKey
truy vấn.orderByP Warriority
truy vấn.orderByValue
boolean Đúng cho các truy vấn được sắp xếp theo khóa, mức độ ưu tiên hoặc giá trị. Sai khác.
truy vấn.orderByChild chuỗi
vô giá trị
Sử dụng một chuỗi để biểu diễn đường dẫn tương đối đến một nút con. Ví dụ: query.orderByChild == "address/zip" . Nếu truy vấn không được yêu cầu bởi một nút con, giá trị này là null.
truy vấn.startAt
truy vấn.endAt
truy vấn.equalTo
chuỗi
con số
boolean
vô giá trị
Truy xuất giới hạn của truy vấn thực thi hoặc trả về null nếu không có tập ràng buộc.
truy vấn.limitToFirst
truy vấn.limitToLast
con số
vô giá trị
Lấy giới hạn cho truy vấn thực thi hoặc trả về null nếu không có giới hạn nào được đặt.

Xác thực dữ liệu

Thực thi cấu trúc dữ liệu và xác định dạng và nội dung của dữ liệu cần được thực hiện bằng .validate quy tắc, mà đang chạy chỉ sau một .write quy tắc thành công để cấp quyền truy cập. Dưới đây là một định nghĩa quy tắc .validate mẫu chỉ cho phép ngày ở định dạng YYYY-MM-DD trong khoảng những năm 1900-2099, được kiểm tra bằng biểu thức chính quy.

".validate": "newData.isString() &&
              newData.val().matches(/^(19|20)[0-9][0-9][-\\/. ](0[1-9]|1[012])[-\\/. ](0[1-9]|[12][0-9]|3[01])$/)"

Các quy tắc .validate là loại quy tắc bảo mật duy nhất không xếp tầng. Nếu bất kỳ quy tắc xác thực nào thất bại trên bất kỳ bản ghi con nào, toàn bộ thao tác ghi sẽ bị từ chối. Ngoài ra, các định nghĩa xác thực được bỏ qua khi dữ liệu bị xóa (nghĩa là khi giá trị mới được ghi là null ).

Đây có thể là những điểm tầm thường, nhưng trên thực tế là các tính năng quan trọng để viết Quy tắc cơ sở dữ liệu thời gian thực Firebase mạnh mẽ. Hãy xem xét các quy tắc sau:

{
  "rules": {
    // write is allowed for all paths
    ".write": true,
    "widget": {
      // a valid widget must have attributes "color" and "size"
      // allows deleting widgets (since .validate is not applied to delete rules)
      ".validate": "newData.hasChildren(['color', 'size'])",
      "size": {
        // the value of "size" must be a number between 0 and 99
        ".validate": "newData.isNumber() &&
                      newData.val() >= 0 &&
                      newData.val() <= 99"
      },
      "color": {
        // the value of "color" must exist as a key in our mythical
        // /valid_colors/ index
        ".validate": "root.child('valid_colors/' + newData.val()).exists()"
      }
    }
  }
}

Với biến thể này, hãy xem kết quả cho các thao tác ghi sau:

JavaScript
var ref = db.ref("/widget");

// PERMISSION_DENIED: does not have children color and size
ref.set('foo');

// PERMISSION DENIED: does not have child color
ref.set({size: 22});

// PERMISSION_DENIED: size is not a number
ref.set({ size: 'foo', color: 'red' });

// SUCCESS (assuming 'blue' appears in our colors list)
ref.set({ size: 21, color: 'blue'});

// If the record already exists and has a color, this will
// succeed, otherwise it will fail since newData.hasChildren(['color', 'size'])
// will fail to validate
ref.child('size').set(99);
Mục tiêu-C
FIRDatabaseReference *ref = [[[FIRDatabase database] reference] child: @"widget"];

// PERMISSION_DENIED: does not have children color and size
[ref setValue: @"foo"];

// PERMISSION DENIED: does not have child color
[ref setValue: @{ @"size": @"foo" }];

// PERMISSION_DENIED: size is not a number
[ref setValue: @{ @"size": @"foo", @"color": @"red" }];

// SUCCESS (assuming 'blue' appears in our colors list)
[ref setValue: @{ @"size": @21, @"color": @"blue" }];

// If the record already exists and has a color, this will
// succeed, otherwise it will fail since newData.hasChildren(['color', 'size'])
// will fail to validate
[[ref child:@"size"] setValue: @99];
Nhanh
var ref = FIRDatabase.database().reference().child("widget")

// PERMISSION_DENIED: does not have children color and size
ref.setValue("foo")

// PERMISSION DENIED: does not have child color
ref.setValue(["size": "foo"])

// PERMISSION_DENIED: size is not a number
ref.setValue(["size": "foo", "color": "red"])

// SUCCESS (assuming 'blue' appears in our colors list)
ref.setValue(["size": 21, "color": "blue"])

// If the record already exists and has a color, this will
// succeed, otherwise it will fail since newData.hasChildren(['color', 'size'])
// will fail to validate
ref.child("size").setValue(99);
Java
FirebaseDatabase database = FirebaseDatabase.getInstance();
DatabaseReference ref = database.getReference("widget");

// PERMISSION_DENIED: does not have children color and size
ref.setValue("foo");

// PERMISSION DENIED: does not have child color
ref.child("size").setValue(22);

// PERMISSION_DENIED: size is not a number
Map<String,Object> map = new HashMap<String, Object>();
map.put("size","foo");
map.put("color","red");
ref.setValue(map);

// SUCCESS (assuming 'blue' appears in our colors list)
map = new HashMap<String, Object>();
map.put("size", 21);
map.put("color","blue");
ref.setValue(map);

// If the record already exists and has a color, this will
// succeed, otherwise it will fail since newData.hasChildren(['color', 'size'])
// will fail to validate
ref.child("size").setValue(99);
NGHỈ NGƠI
# PERMISSION_DENIED: does not have children color and size
curl -X PUT -d 'foo' \
https://docs-examples.firebaseio.com/rest/securing-data/example.json

# PERMISSION DENIED: does not have child color
curl -X PUT -d '{"size": 22}' \
https://docs-examples.firebaseio.com/rest/securing-data/example.json

# PERMISSION_DENIED: size is not a number
curl -X PUT -d '{"size": "foo", "color": "red"}' \
https://docs-examples.firebaseio.com/rest/securing-data/example.json

# SUCCESS (assuming 'blue' appears in our colors list)
curl -X PUT -d '{"size": 21, "color": "blue"}' \
https://docs-examples.firebaseio.com/rest/securing-data/example.json

# If the record already exists and has a color, this will
# succeed, otherwise it will fail since newData.hasChildren(['color', 'size'])
# will fail to validate
curl -X PUT -d '99' \
https://docs-examples.firebaseio.com/rest/securing-data/example/size.json

Bây giờ hãy nhìn của phân tích tình hình tương tự, nhưng sử dụng .write quy tắc thay vì .validate :

{
  "rules": {
    // this variant will NOT allow deleting records (since .write would be disallowed)
    "widget": {
      // a widget must have 'color' and 'size' in order to be written to this path
      ".write": "newData.hasChildren(['color', 'size'])",
      "size": {
        // the value of "size" must be a number between 0 and 99, ONLY IF WE WRITE DIRECTLY TO SIZE
        ".write": "newData.isNumber() && newData.val() >= 0 && newData.val() <= 99"
      },
      "color": {
        // the value of "color" must exist as a key in our mythical valid_colors/ index
        // BUT ONLY IF WE WRITE DIRECTLY TO COLOR
        ".write": "root.child('valid_colors/'+newData.val()).exists()"
      }
    }
  }
}

Trong biến thể này, bất kỳ thao tác nào sau đây sẽ thành công:

JavaScript
var ref = new Firebase(URL + "/widget");

// ALLOWED? Even though size is invalid, widget has children color and size,
// so write is allowed and the .write rule under color is ignored
ref.set({size: 99999, color: 'red'});

// ALLOWED? Works even if widget does not exist, allowing us to create a widget
// which is invalid and does not have a valid color.
// (allowed by the write rule under "color")
ref.child('size').set(99);
Mục tiêu-C
Firebase *ref = [[Firebase alloc] initWithUrl:URL];

// ALLOWED? Even though size is invalid, widget has children color and size,
// so write is allowed and the .write rule under color is ignored
[ref setValue: @{ @"size": @9999, @"color": @"red" }];

// ALLOWED? Works even if widget does not exist, allowing us to create a widget
// which is invalid and does not have a valid color.
// (allowed by the write rule under "color")
[[ref childByAppendingPath:@"size"] setValue: @99];
Nhanh
var ref = Firebase(url:URL)

// ALLOWED? Even though size is invalid, widget has children color and size,
// so write is allowed and the .write rule under color is ignored
ref.setValue(["size": 9999, "color": "red"])

// ALLOWED? Works even if widget does not exist, allowing us to create a widget
// which is invalid and does not have a valid color.
// (allowed by the write rule under "color")
ref.childByAppendingPath("size").setValue(99)
Java
Firebase ref = new Firebase(URL + "/widget");

// ALLOWED? Even though size is invalid, widget has children color and size,
// so write is allowed and the .write rule under color is ignored
Map<String,Object> map = new HashMap<String, Object>();
map.put("size", 99999);
map.put("color", "red");
ref.setValue(map);

// ALLOWED? Works even if widget does not exist, allowing us to create a widget
// which is invalid and does not have a valid color.
// (allowed by the write rule under "color")
ref.child("size").setValue(99);
NGHỈ NGƠI
# ALLOWED? Even though size is invalid, widget has children color and size,
# so write is allowed and the .write rule under color is ignored
curl -X PUT -d '{size: 99999, color: "red"}' \
https://docs-examples.firebaseio.com/rest/securing-data/example.json

# ALLOWED? Works even if widget does not exist, allowing us to create a widget
# which is invalid and does not have a valid color.
# (allowed by the write rule under "color")
curl -X PUT -d '99' \
https://docs-examples.firebaseio.com/rest/securing-data/example/size.json

Điều này minh họa sự khác nhau giữa .write.validate quy tắc. Như đã trình bày, tất cả các quy tắc này phải được viết bằng cách sử dụng .validate , ngoại trừ có thể có quy tắc newData.hasChildren() , tùy thuộc vào việc có nên cho phép xóa hay không.

Sử dụng biến $ để chụp phân đoạn đường dẫn

Bạn có thể chụp các phần của đường dẫn để đọc hoặc ghi bằng cách khai báo các biến bắt giữ với tiền tố $ . Điều này phục vụ như một thẻ hoang dã và lưu trữ giá trị của khóa đó để sử dụng bên trong các khai báo quy tắc:

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

Các biến $ động cũng có thể được sử dụng song song với các tên đường dẫn không đổi. Trong ví dụ này, chúng tôi đang sử dụng biến $other để khai báo quy tắc .validate để đảm bảo rằng widget không có con nào ngoài titlecolor . Bất kỳ viết nào sẽ dẫn đến việc thêm trẻ em được tạo ra sẽ thất bại.

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

Ví dụ trò chuyện ẩn danh

Chúng ta hãy đặt các quy tắc lại với nhau và tạo một ứng dụng trò chuyện ẩn danh, an toàn. Ở đây chúng tôi sẽ liệt kê các quy tắc và một phiên bản chức năng của trò chuyện được bao gồm bên dưới:

{
  "rules": {
    // default rules are false if not specified
    // setting these to true would make ALL CHILD PATHS readable/writable
    // ".read": false,
    // ".write": false,

    "room_names": {
      // the room names can be enumerated and read
      // they cannot be modified since no write rule
      // explicitly allows this
      ".read": true,

      "$room_id": {
        // this is just for documenting the structure of rooms, since
        // they are read-only and no write rule allows this to be set
        ".validate": "newData.isString()"
      }
    },

    "messages": {
      "$room_id": {
        // the list of messages in a room can be enumerated and each
        // message could also be read individually, the list of messages
        // for a room cannot be written to in bulk
        ".read": true,

        // room we want to write a message to must be valid
        ".validate": "root.child('room_names/'+$room_id).exists()",

        "$message_id": {
          // a new message can be created if it does not exist, but it
          // cannot be modified or deleted
          ".write": "!data.exists() && newData.exists()",
          // the room attribute must be a valid key in room_names/ (the room must exist)
          // the object to write must have a name, message, and timestamp
          ".validate": "newData.hasChildren(['name', 'message', 'timestamp'])",

          // the name must be a string, longer than 0 chars, and less than 20 and cannot contain "admin"
          "name": { ".validate": "newData.isString() && newData.val().length > 0 && newData.val().length < 20 && !newData.val().contains('admin')" },

          // the message must be longer than 0 chars and less than 50
          "message": { ".validate": "newData.isString() && newData.val().length > 0 && newData.val().length < 50" },

          // messages cannot be added in the past or the future
          // clients should use firebase.database.ServerValue.TIMESTAMP
          // to ensure accurate timestamps
          "timestamp": { ".validate": "newData.val() <= now" },

          // no other fields can be included in a message
          "$other": { ".validate": false }
        }
      }
    }
  }
}

Bước tiếp theo