กฎความปลอดภัยของฐานข้อมูลเรียลไทม์ของ Firebase ให้คุณควบคุมการเข้าถึงข้อมูลที่จัดเก็บไว้ ในฐานข้อมูลของคุณ ไวยากรณ์กฎที่ยืดหยุ่นช่วยให้คุณสามารถสร้าง กฎที่ตรงกับอะไรก็ได้ ตั้งแต่การเขียนทั้งหมดในฐานข้อมูลไปจนถึงการดำเนินการ ในแต่ละโหนด
กฎความปลอดภัยของ Realtime Database เป็นการกำหนดค่าที่ประกาศสำหรับฐานข้อมูลของคุณ ซึ่งหมายความว่า มีการกำหนดกฎแยกจากตรรกะของผลคูณ ช่วงเวลานี้ มีข้อดีหลายอย่าง เช่น ลูกค้าไม่ต้องรับผิดชอบในการบังคับใช้การรักษาความปลอดภัย จะไม่ส่งผลกระทบต่อข้อมูลของคุณ และที่สำคัญที่สุดคือ ไม่จำเป็นต้องมีผู้ได้รับการแนะนำระดับกลาง เช่น เซิร์ฟเวอร์ ในการคุ้มครองข้อมูล จากทั่วโลก
หัวข้อนี้จะอธิบายไวยากรณ์และโครงสร้างพื้นฐานของกฎการรักษาความปลอดภัยของฐานข้อมูลเรียลไทม์ ที่ใช้เพื่อสร้างชุดกฎที่สมบูรณ์
การจัดโครงสร้างกฎความปลอดภัย
กฎความปลอดภัยของฐานข้อมูลเรียลไทม์ประกอบด้วยนิพจน์ที่คล้ายกับ 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" } } } }
การดำเนินการของกฎพื้นฐาน
มีกฎ 3 ประเภทสำหรับการบังคับใช้ความปลอดภัย โดยยึดตาม
การดำเนินการกับข้อมูล: .write
, .read
และ .validate
ที่นี่
คือสรุปวัตถุประสงค์สั้นๆ ดังนี้
ประเภทของกฎ | |
---|---|
.read | อธิบายว่าอนุญาตให้ผู้ใช้อ่านข้อมูลหรือไม่และเมื่อใด |
.write | อธิบายว่าอนุญาตให้เขียนข้อมูลได้หรือไม่และเมื่อใด |
.validate | กําหนดลักษณะของค่าที่จัดรูปแบบอย่างถูกต้อง ไม่ว่าจะเป็น มีแอตทริบิวต์ย่อย และประเภทข้อมูล |
ตัวแปรการบันทึกไวลด์การ์ด
คำสั่งกฎทั้งหมดชี้ไปที่โหนด ข้อความสามารถชี้ไปยัง
โหนดหรือใช้ไวลด์การ์ด แคปเจอร์ตัวแปร $
เพื่อชี้ไปยังชุดของโหนดที่
ของลำดับชั้น ใช้ตัวแปรการบันทึกเหล่านี้เพื่อจัดเก็บค่าของโหนด
คีย์สำหรับใช้ในคำสั่งกฎต่อๆ ไป เทคนิคนี้ช่วยให้คุณเขียน
เงื่อนไข Rules ที่ซับซ้อนมากขึ้น ซึ่งเราจะพูดถึงรายละเอียดเพิ่มเติม
ในหัวข้อถัดไป
{ "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 });
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 }];
Swift
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
เนื่องจากการดำเนินการอ่านที่ /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 });
Objective-C
FIRDatabaseReference *ref = [[FIRDatabase database] reference]; [[ref child:@"records/rec1"] observeSingleEventOfType:FEventTypeValue withBlock:^(FIRDataSnapshot *snapshot) { // SUCCESS! }];
Swift
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!
ข้อความคำสั่งซ้อนทับ
อาจมีกฎมากกว่า 1 ข้อไปใช้กับโหนดหนึ่ง ใน
ในกรณีที่นิพจน์กฎหลายนิพจน์ระบุโหนด วิธีการเข้าถึงคือ
ปฏิเสธหากเงื่อนไขใดๆ เท่ากับ 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
จะ
เนื่องจากกฎข้อที่ 2 จะเป็น false
เสมอ แม้ว่ากฎข้อแรก
กฎคือ true
เสมอ
ขั้นตอนถัดไป
คุณจะทำความเข้าใจกฎความปลอดภัยของฐานข้อมูลเรียลไทม์ของ Firebase ได้อย่างลึกซึ้งยิ่งขึ้น ดังนี้
เรียนรู้แนวคิดหลักถัดไปของภาษาRules แบบไดนามิก เงื่อนไข ซึ่งช่วยให้ Rules ตรวจสอบผู้ใช้ได้ การให้สิทธิ์ เปรียบเทียบข้อมูลที่มีอยู่กับข้อมูลขาเข้า ตรวจสอบความถูกต้องของข้อมูลที่เข้ามา ตรวจสอบ โครงสร้างของการค้นหาที่มาจากไคลเอ็นต์ และอื่นๆ
ตรวจสอบกรณีการใช้งานด้านความปลอดภัยทั่วไปและคำจำกัดความของกฎการรักษาความปลอดภัยของ Firebase ที่จัดการกับกรณีเหล่านี้