ดูข้อมูลเกี่ยวกับไวยากรณ์หลักของภาษาของกฎความปลอดภัยของฐานข้อมูลแบบเรียลไทม์

กฎความปลอดภัยของฐานข้อมูลเรียลไทม์ของ 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
หมายเหตุ: ผลิตภัณฑ์ Firebase นี้ไม่พร้อมใช้งานในเป้าหมาย App Clip
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
หมายเหตุ: ผลิตภัณฑ์ Firebase นี้ไม่พร้อมใช้งานในเป้าหมาย App Clip
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
หมายเหตุ: ผลิตภัณฑ์ Firebase นี้ไม่พร้อมใช้งานในเป้าหมาย App Clip
FIRDatabaseReference *ref = [[FIRDatabase database] reference];
[[ref child:@"records/rec1"] observeSingleEventOfType:FEventTypeValue withBlock:^(FIRDataSnapshot *snapshot) {
    // SUCCESS!
}];
Swift
หมายเหตุ: ผลิตภัณฑ์ Firebase นี้ไม่พร้อมใช้งานในเป้าหมาย App Clip
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 ได้อย่างลึกซึ้งยิ่งขึ้น ดังนี้