Catch up on everthing we announced at this year's Firebase Summit. Learn more

استخدم الشروط في قواعد قاعدة البيانات في الوقت الفعلي

هذا دليل يبني على تعلم قواعد الأمن Firebase اللغة الأساسية دليل لإظهار كيفية إضافة شروط لبك Firebase الحقيقي قاعدة بيانات قواعد الأمن.

اللبنة الأولي للقواعد أمان قاعدة البيانات في الوقت الحقيقي هو الشرط. الشرط هو تعبير منطقي يحدد ما إذا كان يجب السماح بعملية معينة أو رفضها. للقواعد الأساسية، وذلك باستخدام true و false الحرفية كشروط يعمل بشكل جيد بريفيكتلي. لكن لغة قواعد أمان قاعدة البيانات Realtime تمنحك طرقًا لكتابة شروط أكثر تعقيدًا يمكنها:

  • تحقق من مصادقة المستخدم
  • تقييم البيانات الموجودة مقابل البيانات المقدمة حديثًا
  • الوصول ومقارنة أجزاء مختلفة من قاعدة البيانات الخاصة بك
  • تحقق من صحة البيانات الواردة
  • استخدم هيكل الاستعلامات الواردة لمنطق الأمان

استخدام المتغيرات $ لالتقاط مقاطع المسار

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

{
  "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 مع قاعدة بيانات Firebase Realtime للسماح لك بالتحكم في الوصول إلى البيانات على أساس كل مستخدم باستخدام الشروط. مرة واحدة في يصادق المستخدم، و auth سوف يتم ملؤها المتغير في قاعدة البيانات في الوقت الحقيقي قواعد قواعد الأمان مع معلومات المستخدم. وتشمل هذه المعلومات المعرف الفريد ( uid )، فضلا عن بيانات الحساب مرتبط، مثل معرف الفيسبوك أو عنوان البريد الإلكتروني، وغيرها من المعلومات. إذا قمت بتنفيذ موفر مصادقة مخصص ، فيمكنك إضافة الحقول الخاصة بك إلى حمولة المصادقة الخاصة بالمستخدم.

يوضح هذا القسم كيفية دمج لغة قواعد أمان قاعدة بيانات Firebase Realtime مع معلومات المصادقة الخاصة بمستخدميك. من خلال الجمع بين هذين المفهومين ، يمكنك التحكم في الوصول إلى البيانات بناءً على هوية المستخدم.

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

هيكلة قاعدة البيانات الخاصة بك لدعم شروط المصادقة

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

{
  "rules": {
    "users": {
      "$uid": {
        ".read": "auth != null && auth.uid == $uid"
      }
    }
  }
}

العمل مع مطالبات المصادقة المخصصة

للتطبيقات التي تتطلب مراقبة الوصول المخصصة لمختلف المستخدمين، مصادقة Firebase تسمح للمطورين ل مطالبات يقع على المستخدم Firebase . هذه المطالبات هي accesible في 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 Realtime القوية. ضع في اعتبارك القواعد التالية:

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

مع وضع هذا المتغير في الاعتبار ، انظر إلى نتائج عمليات الكتابة التالية:

جافا سكريبت
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);
ج موضوعية
ملاحظة: هذا المنتج Firebase لا يتوفر على كليب هدف التطبيقات.
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];
سويفت
ملاحظة: هذا المنتج Firebase لا يتوفر على كليب هدف التطبيقات.
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);
جافا
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);
استراحة
# 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()"
      }
    }
  }
}

في هذا المتغير ، ستنجح أي من العمليات التالية:

جافا سكريبت
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);
ج موضوعية
ملاحظة: هذا المنتج Firebase لا يتوفر على كليب هدف التطبيقات.
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];
سويفت
ملاحظة: هذا المنتج Firebase لا يتوفر على كليب هدف التطبيقات.
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)
جافا
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);
استراحة
# 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

يمكنك أيضًا استخدام القواعد المستندة إلى الاستعلام لتحديد مقدار البيانات التي يقوم العميل بتنزيلها من خلال عمليات القراءة.

على سبيل المثال ، تحد القاعدة التالية من الوصول للقراءة إلى أول 1000 نتيجة فقط من استعلام ما ، كما هو مرتب حسب الأولوية:

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. تتوفر التعبيرات في قواعد أمان قاعدة البيانات في الوقت الفعلي.

تعبيرات القواعد المستندة إلى الاستعلام
تعبير نوع وصف
الاستعلام. OrderByKey
الاستعلام. ترتيب حسب الأولوية
الاستعلام. OrderByValue
قيمة منطقية صواب للاستعلامات المرتبة حسب المفتاح أو الأولوية أو القيمة. خطأ خلاف ذلك.
الاستعلام. OrderByChild سلسلة
باطل
استخدم سلسلة لتمثيل المسار النسبي لعقدة فرعية. على سبيل المثال، query.orderByChild == "address/zip" . إذا لم يتم ترتيب الاستعلام بواسطة عقدة فرعية ، فستكون هذه القيمة خالية.
query.startAt
query.endAt
query.equalTo
سلسلة
عدد
قيمة منطقية
باطل
يسترجع حدود الاستعلام المنفذ ، أو يعيد قيمة فارغة إذا لم تكن هناك مجموعة محددة.
الاستعلام.حد إلى الأول
الاستعلام. LimitToLast
عدد
باطل
يسترجع الحد على الاستعلام المنفذ ، أو يعيد القيمة فارغة إذا لم يكن هناك حد معين.

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

بعد هذه المناقشة حول الشروط ، لديك فهم أكثر تعقيدًا للقواعد وأنت مستعد لما يلي:

تعرف على كيفية التعامل مع حالات الاستخدام الأساسية ، وتعلم سير العمل لتطوير واختبار ونشر القواعد:

تعلم ميزات القواعد الخاصة بقاعدة بيانات Realtime: