Join us for Firebase Summit on November 10, 2021. Tune in to learn how Firebase can help you accelerate app development, release with confidence, and scale with ease. Register

Tìm hiểu cú pháp cốt lõi của ngôn ngữ Quy tắc cơ sở dữ liệu thời gian thực

Quy tắc bảo mật cơ sở dữ liệu thời gian thực của Firebase cho phép bạn kiểm soát quyền truy cập vào dữ liệu được lưu trữ trong cơ sở dữ liệu của mình. Cú pháp các quy tắc linh hoạt cho phép bạn tạo các quy tắc phù hợp với bất kỳ thứ gì, từ tất cả các lần ghi vào cơ sở dữ liệu của bạn đến các hoạt động trên các nút riêng lẻ.

Cơ sở dữ liệu thời gian thực Security Rules là cấu hình tường thuật 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 của sản phẩm. Điều này có một số lợi thế: máy khách 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 trọng tài trung gian, chẳng hạn như máy chủ, để bảo vệ dữ liệu khỏi thế giới.

Chủ đề này mô tả cú pháp và cấu trúc cơ bản Quy tắc bảo mật cơ sở dữ liệu thời gian thực được sử dụng để tạo các bộ quy tắc hoàn chỉnh.

Cấu trúc các quy tắc bảo mật của bạn

Quy tắc bảo mật cơ sở dữ liệu thời gian thực được tạo thành từ các biểu thức giống JavaScript có trong tài liệu JSON. Cấu trúc của các quy tắc của bạn phải tuân theo cấu trúc của dữ liệu bạn đã lưu trữ trong cơ sở dữ liệu của mình.

Nguyên tắc cơ bản xác định một tập hợp các nút phải được bảo đảm, các phương pháp truy cập (ví dụ, đọc, viết) liên quan, và điều kiện theo đó truy cập hoặc là cho phép hoặc từ chối. Trong ví dụ sau đây, điều kiện của chúng tôi sẽ đơn giản truefalse báo cáo, nhưng trong chủ đề tiếp theo chúng tôi sẽ giới thiệu thêm nhiều cách năng động để diễn tả điều kiện.

Vì vậy, ví dụ, nếu chúng ta đang cố gắng để bảo đảm một child_node dưới một parent_node , cú pháp chung để sau là:

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

Hãy áp dụng mô hình này. Ví dụ: giả sử bạn đang theo dõi danh sách thư và có dữ liệu giống như sau:

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

Các quy tắc của bạn phải đượ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 về bảo mật chỉ đọc có thể có ý nghĩa đối với cấu trúc dữ liệu này. Ví dụ này minh họa cách chúng tôi chỉ định các nút cơ sở dữ liệu áp dụng các quy tắc nào và các điều kiện để đánh giá các quy tắc tại các nút đó.

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

Các hoạt động quy tắc cơ bản

Có ba loại quy tắc cho việc bảo mật dựa trên loại hoạt động được thực hiện trên dữ liệu: .write , .read , và .validate . Dưới đây là tóm tắt nhanh về mục đích của chúng:

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

Các biến chụp ký tự đại diện

Tất cả các câu lệnh quy tắc đều trỏ đến các nút. Một tuyên bố có thể trỏ đến một nút cụ thể hoặc sử dụng $ biến chụp wildcard để trỏ đến bộ nút ở mức thứ bậc. Sử dụng các biến nắm bắt này để lưu trữ giá trị của các khóa nút để sử dụng bên trong các câu lệnh quy tắc tiếp theo. Kỹ thuật này cho phép bạn viết các điều kiện quy phức tạp hơn, một cái gì đó chúng tôi sẽ giới thiệu chi tiết hơn trong chủ đề tiếp theo.

{
  "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 động $ biến cũng có thể được sử dụng song song với tên đường dẫn không đổi. Trong ví dụ này, chúng tôi đang sử dụng $other biến để khai báo một .validate quy tắc đảm bảo rằng widget không có những đứa trẻ khác hơn titlecolor . Bất kỳ cách viết nào dẫn đến việc tạo thêm trẻ em sẽ không thành công.

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

Đọc và Viết Quy tắc Xếp tầng

.read.write quy tắc làm việc từ trên xuống, với các quy tắc nông trọng quy tắc sâu hơn. Nếu một khoản tài trợ quy tắc đọc hoặc quyền ghi tại một con đường đặc biệt, sau đó nó cũng cho phép truy cập vào tất cả các nút con 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 một đứa trẻ baz với giá trị true . Các ".read": false quy tắc dưới /foo/bar/ không có tác dụng ở đây, vì truy cập không thể bị thu hồi bằng một con đường nhỏ.

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

Lưu ý rằng .validate quy tắc không thác. Tất cả các quy tắc xác thực 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 ghi.

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 không thành công ngay lập tức nếu không có quy tắc tại vị trí đó hoặc tại vị trí 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 ở vị trí phụ huynh sẽ hoàn toàn thất bại. Hãy xem xét cấu trúc này:

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

Mà không hiểu rằng những quy tắc được đánh giá nguyên tử, nó có vẻ như lấy sự /records/ con đường sẽ trở rec1 nhưng không 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
});
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
}];
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
  });
});
LÊN ĐỈNH
curl https://docs-examples.firebaseio.com/rest/records/
# response returns a PERMISSION_DENIED error

Kể từ khi hoạt động đọc tại /records/ là nguyên tử, và không có quy tắc đọc rằng trợ cấp quyền truy cập vào tất cả các dữ liệu dưới /records/ , điều này sẽ ném một PERMISSION_DENIED lỗi. Nếu chúng tôi đánh giá quy tắc này trong trình giả lập an ninh trong chúng ta điều khiển căn cứ hỏa lực , chúng ta có thể thấy rằng các hoạt động đọc đã bị từ chối vì không có quy tắc đọc cho phép truy cập vào /records/ con đường. Tuy nhiên, lưu ý rằng các quy tắc cho rec1 không bao giờ được đánh giá vì nó không trong đường dẫn chúng tôi yêu cầu. Để lấy rec1 , chúng tôi sẽ cần phải truy cập trực tiếp:

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!
}];
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
  }
});
LÊN ĐỈNH
curl https://docs-examples.firebaseio.com/rest/records/rec1
# SUCCESS!

Câu lệnh chồng chéo

Có thể áp dụng nhiều quy tắc cho một nút. Trong trường hợp nhiều quy tắc biểu thức xác định một nút, các phương pháp truy cập bị từ chối nếu một trong các điều kiện là 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"
      }
    }
  }
}

Trong ví dụ trên, đọc đến message1 nút sẽ bị từ chối vì các quy tắc thứ hai luôn là false , mặc dù các quy tắc đầu tiên luôn luôn là true .

Bước tiếp theo

Bạn có thể hiểu sâu hơn về Quy tắc bảo mật cơ sở dữ liệu thời gian thực của Firebase:

  • Tìm hiểu các khái niệm quan trọng tiếp theo của ngôn ngữ quy, năng động điều kiện , cho phép Rules cho phép người dùng kiểm tra của bạn, so sánh dữ liệu hiện có và đến, xác nhận dữ liệu gửi đến, kiểm tra cấu trúc của các truy vấn đến từ các khách hàng, và nhiều hơn nữa.

  • Xem xét trường hợp sử dụng an ninh tiêu biểu và các định nghĩa căn cứ hỏa lực Security Rules địa chỉ họ .