คู่มือนี้สร้างขึ้นจากคู่มือเรียนรู้ภาษาหลักของกฎการรักษาความปลอดภัยของ Firebase เพื่อแสดงวิธีเพิ่มเงื่อนไขลงในกฎการรักษาความปลอดภัยของฐานข้อมูลเรียลไทม์ของ Firebase
องค์ประกอบหลักหลักของกฎความปลอดภัยของ Realtime Database คือเงื่อนไข ต
คือนิพจน์บูลีนที่กำหนดว่าการดำเนินการหนึ่งๆ หรือไม่
ควรยอมรับหรือปฏิเสธ สำหรับกฎพื้นฐาน จะใช้ true
และ false
ลิเทอรัลเป็น
สภาพปกติใช้งานได้ดี แต่ภาษาของกฎการรักษาความปลอดภัยของฐานข้อมูลเรียลไทม์ช่วยให้คุณ
วิธีเขียนเงื่อนไขที่ซับซ้อนมากขึ้น ซึ่งมีดังนี้
- ตรวจสอบการตรวจสอบสิทธิ์ผู้ใช้
- ประเมินข้อมูลที่มีอยู่เทียบกับข้อมูลที่ส่งเข้ามาใหม่
- เข้าถึงและเปรียบเทียบส่วนต่างๆ ของฐานข้อมูล
- ตรวจสอบข้อมูลขาเข้า
- ใช้โครงสร้างของการค้นหาขาเข้าสำหรับตรรกะการรักษาความปลอดภัย
การใช้ตัวแปร $ เพื่อบันทึกกลุ่มเส้นทาง
คุณสามารถบันทึกส่วนต่างๆ ของเส้นทางสำหรับการอ่านหรือเขียนได้โดยการประกาศ
บันทึกตัวแปรด้วยคำนำหน้า $
การดำเนินการนี้เป็นไวลด์การ์ดและจัดเก็บมูลค่าของกุญแจดังกล่าวไว้ใช้ภายใน
เงื่อนไขของกฎ:
{ "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 } } } }
การตรวจสอบสิทธิ์
รูปแบบหนึ่งของกฎความปลอดภัยที่ใช้กันมากที่สุดคือการควบคุมการเข้าถึงตาม สถานะการตรวจสอบสิทธิ์ของผู้ใช้ เช่น แอปของคุณอาจต้องการอนุญาตเฉพาะ ผู้ใช้ที่ลงชื่อเข้าใช้เพื่อเขียนข้อมูล
หากแอปใช้การตรวจสอบสิทธิ์ Firebase ตัวแปร request.auth
จะมี
ข้อมูลการตรวจสอบสิทธิ์สำหรับไคลเอ็นต์ที่ขอข้อมูล
สำหรับข้อมูลเพิ่มเติมเกี่ยวกับ request.auth
โปรดดูข้อมูลอ้างอิง
เอกสารประกอบ
Firebase Authentication ผสานรวมกับ Firebase Realtime Database เพื่อให้คุณควบคุมข้อมูลได้
ตามผู้ใช้แต่ละรายโดยใช้เงื่อนไข เมื่อผู้ใช้ตรวจสอบสิทธิ์ auth
ในกฎการรักษาความปลอดภัยของฐานข้อมูลแบบเรียลไทม์จะได้รับการสร้างขึ้นด้วย
ข้อมูลนี้รวมถึงตัวระบุที่ไม่ซ้ำกัน (uid
)
ข้อมูลบัญชีที่ลิงก์ เช่น รหัส Facebook หรืออีเมล และ
ข้อมูลอื่นๆ หากใช้ผู้ให้บริการตรวจสอบสิทธิ์ที่กำหนดเอง คุณก็จะเพิ่มช่องของตนเองได้
ไปยังเพย์โหลดการตรวจสอบสิทธิ์ของผู้ใช้
ส่วนนี้จะอธิบายวิธีรวมภาษาของกฎการรักษาความปลอดภัยของฐานข้อมูลเรียลไทม์ของ Firebase กับ ข้อมูลการตรวจสอบสิทธิ์ของผู้ใช้ เมื่อรวม 2 แนวคิดเข้าด้วยกัน คุณสามารถควบคุมการเข้าถึงข้อมูลตามข้อมูลประจำตัวของผู้ใช้ได้
ตัวแปร auth
ตัวแปร auth
ที่กำหนดไว้ล่วงหน้าในกฎเป็นค่าว่างก่อน
มีการตรวจสอบสิทธิ์เกิดขึ้น
เมื่อตรวจสอบสิทธิ์ผู้ใช้ด้วยการตรวจสอบสิทธิ์ของ Firebase แล้ว จะประกอบด้วยแอตทริบิวต์ต่อไปนี้
ผู้ให้ทุน | วิธีการตรวจสอบสิทธิ์ที่ใช้ ("password", "anonymous", "facebook", "github", "google", หรือ "twitter") |
uid | รหัสผู้ใช้ที่ไม่ซ้ำซึ่งไม่ซ้ำกันในผู้ให้บริการทั้งหมด |
โทเค็น |
เนื้อหาของโทเค็นรหัสการตรวจสอบสิทธิ์ Firebase ดูข้อมูลอ้างอิง
เอกสารประกอบสำหรับ
auth.token เพื่อดูรายละเอียดเพิ่มเติม
|
ต่อไปนี้คือกฎตัวอย่างที่ใช้ตัวแปร auth
เพื่อให้แน่ใจว่า
ผู้ใช้แต่ละรายจะเขียนไปยังเส้นทางที่ผู้ใช้กำหนดได้เท่านั้น ดังนี้
{ "rules": { "users": { "$user_id": { // grants write access to the owner of this user account // whose uid must exactly match the key ($user_id) ".write": "$user_id === auth.uid" } } } }
การจัดโครงสร้างฐานข้อมูลให้รองรับเงื่อนไขการตรวจสอบสิทธิ์
การวางโครงสร้างฐานข้อมูลให้อยู่ในรูปแบบการเขียน
Rules ง่ายขึ้น รูปแบบทั่วไปอย่างหนึ่งในการจัดเก็บข้อมูลผู้ใช้ใน Realtime Database คือ
จัดเก็บผู้ใช้ทั้งหมดไว้ในโหนด users
โหนดเดียวที่มีระดับ
ค่า uid
สำหรับผู้ใช้ทุกคน ถ้าต้องการจำกัดการเข้าถึง
ข้อมูลนี้ซึ่งมีเพียงผู้ใช้ที่เข้าสู่ระบบเท่านั้นที่จะเห็นข้อมูลของตนเอง กฎของคุณ
จะมีหน้าตาแบบนี้
{ "rules": { "users": { "$uid": { ".read": "auth !== null && auth.uid === $uid" } } } }
การใช้การอ้างสิทธิ์ที่กำหนดเองของการตรวจสอบสิทธิ์
สำหรับแอปที่ต้องมีการควบคุมการเข้าถึงที่กำหนดเองสำหรับผู้ใช้แต่ละคน Firebase Authentication
อนุญาตให้นักพัฒนาซอฟต์แวร์กำหนดการอ้างสิทธิ์ให้กับผู้ใช้ Firebase
คุณจะเข้าถึงการอ้างสิทธิ์เหล่านี้ได้ในตัวแปร auth.token
ในกฎ
ต่อไปนี้คือตัวอย่างของกฎที่ใช้ hasEmergencyTowel
การอ้างสิทธิ์ที่กำหนดเอง:
{ "rules": { "frood": { // A towel is about the most massively useful thing an interstellar // hitchhiker can have ".read": "auth.token.hasEmergencyTowel === true" } } }
นักพัฒนาซอฟต์แวร์ที่สร้าง ของตนเอง
โทเค็นการตรวจสอบสิทธิ์ที่กำหนดเองสามารถเพิ่มการอ้างสิทธิ์ลงในโทเค็นเหล่านี้ได้ เหล่านี้
ใช้ได้กับตัวแปร auth.token
ในกฎ
ข้อมูลที่มีอยู่กับข้อมูลใหม่
ตัวแปร data
ที่กำหนดไว้ล่วงหน้าใช้เพื่ออ้างอิงข้อมูลก่อน
จะมีการดำเนินการเขียน ในทางกลับกัน ตัวแปร newData
มีข้อมูลใหม่ที่จะปรากฏหากการเขียนสำเร็จ
newData
แสดงผลลัพธ์ที่ผสานรวมของข้อมูลใหม่ที่เขียน
และข้อมูลที่มีอยู่
เพื่อให้เห็นภาพ กฎนี้ทำให้เราสามารถสร้างระเบียนใหม่ หรือลบที่มีอยู่ แต่ไม่ทำการเปลี่ยนแปลงกับข้อมูลที่มีอยู่
// 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()"
การอ้างอิงข้อมูลในเส้นทางอื่นๆ
สามารถใช้ข้อมูลใดๆ เป็นเกณฑ์สำหรับกฎได้ การใช้ฟิลด์ที่กำหนดไว้ล่วงหน้า
ตัวแปร root
, data
และ newData
จะเข้าถึงเส้นทางใดก็ได้ตามที่มีอยู่ก่อนหรือหลังเหตุการณ์การเขียน
ลองพิจารณาตัวอย่างนี้ ซึ่งอนุญาตให้เขียนตราบใดที่ค่าของแอตทริบิวต์
/allow_writes/
โหนดคือ true
แต่โหนดหลักไม่มี
ตั้งค่าแฟล็ก readOnly
แล้ว และมีเด็กที่ชื่อ foo
ใน
ข้อมูลที่เขียนใหม่:
".write": "root.child('allow_writes').val() === true && !data.parent().child('readOnly').exists() && newData.child('foo').exists()"
กำลังตรวจสอบข้อมูล
การบังคับใช้โครงสร้างข้อมูลและตรวจสอบรูปแบบและเนื้อหาข้อมูลควร
ทำได้โดยใช้กฎ .validate
ซึ่งจะทำงานหลังจาก
กฎ .write
ข้อให้สิทธิ์ในการเข้าถึงเสร็จสมบูรณ์ ด้านล่างนี้เป็นตัวอย่าง
คำจำกัดความของกฎ .validate
รายการซึ่งอนุญาตเฉพาะวันที่ในรูปแบบ
YYYY-MM-DD ระหว่างปี 1900-2099 ซึ่งตรวจสอบโดยใช้นิพจน์ทั่วไป
".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])$/)"
กฎ .validate
เป็นกฎความปลอดภัยประเภทเดียวที่ไม่ต่อเนื่อง หากมี
กฎการตรวจสอบความถูกต้องล้มเหลวในระเบียนย่อย การดำเนินการเขียนทั้งหมดจะถูกปฏิเสธ
นอกจากนี้ คำจำกัดความของการตรวจสอบความถูกต้องจะถูกละเว้นเมื่อมีการลบข้อมูล (กล่าวคือ เมื่อค่าใหม่)
ที่กำลังเขียนอยู่คือ null
)
เรื่องเหล่านี้อาจดูเป็นเพียงประเด็นเล็กๆ แต่อันที่จริงแล้วเป็นฟีเจอร์สำคัญสำหรับการเขียน กฎการรักษาความปลอดภัยฐานข้อมูลเรียลไทม์ของ Firebase ที่มีประสิทธิภาพ โดยพิจารณากฎต่อไปนี้
{ "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()" } } } }
พิจารณาผลลัพธ์ของการดำเนินการเขียนต่อไปนี้โดยคำนึงถึงตัวแปรนี้
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);
Objective-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];
Swift
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);
REST
# 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
ตอนนี้เรามาดูโครงสร้างเดียวกัน แต่ใช้กฎ .write
แทน .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()" } } } }
ในตัวแปรนี้ การดำเนินการต่อไปนี้จะสำเร็จ
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);
Objective-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];
Swift
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);
REST
# 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
ตัวอย่างนี้แสดงความแตกต่างระหว่างกฎ .write
กับกฎ .validate
ดังที่แสดงไว้ กฎทั้งหมดเหล่านี้ควรเขียนโดยใช้ .validate
พร้อมด้วย
ข้อยกเว้นที่เป็นไปได้ของกฎ newData.hasChildren()
ซึ่งขึ้นอยู่กับว่า
ควรอนุญาตให้มีการลบ
กฎที่อิงตามคำค้นหา
แม้ว่าคุณจะใช้กฎเป็นตัวกรองไม่ได้ แต่คุณ
จำกัดการเข้าถึงข้อมูลชุดย่อยได้โดยใช้พารามิเตอร์การค้นหาในกฎ
ใช้นิพจน์ query.
ในกฎเพื่อให้สิทธิ์อ่านหรือเขียนตาม
พารามิเตอร์การค้นหา
เช่น กฎที่อิงตามคำค้นหาต่อไปนี้ใช้กฎความปลอดภัยที่อิงตามผู้ใช้
และกฎที่อิงตามคำค้นหาเพื่อจำกัดการเข้าถึงข้อมูลในคอลเล็กชัน baskets
เฉพาะตะกร้าช็อปปิ้งที่ผู้ใช้ที่ใช้งานอยู่เป็นเจ้าของเท่านั้น
"baskets": {
".read": "auth.uid !== null &&
query.orderByChild === 'owner' &&
query.equalTo === auth.uid" // restrict basket access to owner of basket
}
ข้อความค้นหาต่อไปนี้ ซึ่งมีพารามิเตอร์การค้นหาในกฎจะ สำเร็จ:
db.ref("baskets").orderByChild("owner")
.equalTo(auth.currentUser.uid)
.on("value", cb) // Would succeed
แต่คำค้นหาที่ไม่มีพารามิเตอร์ในกฎจะดำเนินการไม่สำเร็จ
ข้อผิดพลาด PermissionDenied
:
db.ref("baskets").on("value", cb) // Would fail with PermissionDenied
คุณยังใช้กฎที่อิงตามคำค้นหาเพื่อจำกัดปริมาณข้อมูลที่ไคลเอ็นต์ดาวน์โหลดได้ด้วย ผ่านการดำเนินการอ่าน
ตัวอย่างเช่น กฎต่อไปนี้จำกัดสิทธิ์การเข้าถึงระดับอ่านให้เฉพาะ 1, 000 รายการแรก ของข้อความค้นหาตามลำดับความสำคัญ:
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)
นิพจน์ query.
ต่อไปนี้มีอยู่ในกฎความปลอดภัยของฐานข้อมูลแบบเรียลไทม์
นิพจน์กฎที่อิงตามข้อความค้นหา | ||
---|---|---|
นิพจน์ | ประเภท | คำอธิบาย |
query.orderByKey query.orderByPriority query.orderByValue |
boolean | เป็นจริงสำหรับคำค้นหาที่เรียงตามคีย์ ลำดับความสำคัญ หรือค่า เป็นเท็จ |
query.orderByChild | สตริง ค่าว่าง |
ใช้สตริงเพื่อแสดงเส้นทางที่เกี่ยวข้องไปยังโหนดย่อย ตัวอย่างเช่น
query.orderByChild === "address/zip" หากคำค้นหาไม่ใช่
เรียงลำดับโดยโหนดย่อยค่านี้เป็นค่าว่าง
|
query.startAt query.endAt query.equalTo |
สตริง หมายเลข บูลีน ค่าว่าง |
เรียกข้อมูลขอบเขตของคำค้นหาที่เรียกใช้ หรือแสดงผลเป็น Null หากมี ไม่ได้กำหนดขอบเขต |
query.limitToFirst query.limitToLast |
หมายเลข ค่าว่าง |
เรียกคืนขีดจำกัดของการค้นหาที่ดำเนินการอยู่ หรือแสดงผลค่า Null หากมี ไม่จำกัด |
ขั้นตอนถัดไป
หลังจากปรึกษาหารือเกี่ยวกับเงื่อนไขกันแล้ว คุณมีทางเลือกมากขึ้น เข้าใจ Rules และพร้อมที่จะ:
เรียนรู้วิธีจัดการกรณีการใช้งานหลักและเรียนรู้เวิร์กโฟลว์สำหรับการพัฒนา การทดสอบและทำให้ Rules ใช้งานได้:
- เรียนรู้เกี่ยวกับตัวแปรRules ที่กำหนดไว้ล่วงหน้าทั้งชุดที่คุณสามารถใช้ได้ เพื่อสร้างเงื่อนไข
- เขียนกฎที่กล่าวถึงสถานการณ์ที่พบบ่อย
- ต่อยอดความรู้โดยทบทวนสถานการณ์ที่คุณต้องสังเกตและหลีกเลี่ยงกฎที่ไม่ปลอดภัย
- ดูข้อมูลเกี่ยวกับชุดโปรแกรมจำลองภายในของ Firebase และวิธีใช้งานเพื่อทดสอบ Rules
- ดูวิธีการสำหรับการทำให้ Rules ใช้งานได้
ดูข้อมูล Rules ฟีเจอร์สำหรับ Realtime Database โดยเฉพาะ