التعرف على البنية الأساسية للغة قواعد أمان قاعدة البيانات في الوقت الفعلي

تسمح لك "قواعد أمان قاعدة بيانات Firebase في الوقت الفعلي" بالتحكّم في الوصول إلى البيانات المخزَّنة. في قاعدة البيانات لديك. تسمح لك بنية القواعد المرنة بإنشاء من القواعد التي تطابق أي شيء، بدءًا من جميع عمليات الكتابة إلى قاعدة البيانات وحتى العمليات على العُقد الفردية.

قواعد أمان قاعدة البيانات في الوقت الفعلي هي تهيئة بيانية لقاعدة بياناتك. يعني ذلك أن يتم تحديد القواعد بشكل منفصل عن منطق المنتج. هذا النمط العديد من المزايا: العملاء غير مسؤولين عن فرض الأمان والأخطاء عمليات التنفيذ الأخرى لبياناتك، وربما الأهم من ذلك ليست هناك حاجة إلى جهة وسيطة، مثل الخادم، لحماية البيانات من حول العالم

يصف هذا الموضوع البنية الأساسية لقواعد أمان قاعدة البيانات في الوقت الفعلي وبنيتها. المستخدمة لإنشاء مجموعات قواعد كاملة.

تنظيم قواعد الأمان

تتكون قواعد أمان قاعدة البيانات في الوقت الفعلي من تعبيرات تشبه جافا سكريبت مضمنة في مستند 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"
      }
    }
  }
}

عمليات القواعد الأساسية

هناك ثلاثة أنواع من القواعد لفرض الأمان استنادًا إلى نوع العملية التي يتم إجراؤها على البيانات: .write و.read و.validate. هنا ملخصًا سريعًا لأغراضها:

أنواع القواعد
.read تصف ما إذا كان يُسمح للمستخدمين بقراءة البيانات ومتى يُسمح لهم بقراءة البيانات.
.write تصف ما إذا كان يُسمح بكتابة البيانات ومتى يُسمح بذلك.
.Verifyate تحدد الشكل الذي ستبدو عليه القيمة المنسقة بشكل صحيح، سواء كانت وتضم سمات فرعية ونوع البيانات.

متغيرات التقاط أحرف البدل

تشير جميع عبارات القواعد إلى العُقد. يمكن أن تشير العبارة إلى عبارة محددة أو استخدام متغيّرات الالتقاط لحرف البدل $ للإشارة إلى مجموعات من العُقد في مستوى التسلسل الهرمي. استخدام متغيرات الالتقاط هذه لتخزين قيمة العقدة للاستخدام داخل عبارات القواعد اللاحقة. يتيح لك هذا الأسلوب كتابة شروط قواعد أكثر تعقيدًا، وسنتناولها بمزيد من التفصيل في الموضوع التالي.

{
  "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
})
جافا
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!
})
جافا
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!

العبارات المتداخلة

من الممكن تطبيق أكثر من قاعدة على عقدة. في جلسة المعمل، الحالة التي تحدد فيها تعبيرات القواعد المتعددة عقدة، فإن طريقة الوصول هي يتم الرفض إذا كان أي من الشروط هو 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 تم الرفض لأن القواعد الثانية هي دائمًا false، على الرغم من أن القاعدة الأولى هي true دائمًا.

الخطوات التالية

يمكنك تعميق فهمك لقواعد أمان قاعدة بيانات Firebase في الوقت الفعلي:

  • تعرف على المفهوم الرئيسي التالي للغة القواعد، الديناميكية الشروط، التي تسمح لـ "القواعد" بالتحقّق من المستخدم التفويض، ومقارنة البيانات الحالية والواردة، والتحقق من صحة البيانات الواردة، والتحقق وبنية الاستعلامات القادمة من العميل، والمزيد.

  • مراجعة حالات الاستخدام النموذجي للأمان وتعريفات قواعد أمان Firebase التي تعالج هذه الحالات