Firestore安全规则阻止文档重创建失效,应用端仍可覆盖文档求助
Got it, let's break down why your security rules aren't stopping the overwrite in your Android app, and fix it properly.
First, the root issue: Your current rules only restrict the create operation (blocking it when the document exists), but when you call set() on an existing document in Firestore, that triggers an update operation—not a create. Your update rule only checks for user authentication, so it allows full document overwrites without any restrictions. That's why your set() call keeps resetting the document to default values.
Here are two tailored solutions depending on your exact needs:
Solution 1: Allow updates only to specific fields (recommended if you need to modify usage later)
If you want to let users update the usage value after the document is created, but prevent overwriting the entire document (like resetting usage to 0), adjust your security rules to restrict updates to only the fields you intend to modify:
match /UserData/{uid}/DAILY_USAGES/{day} { allow create: if !exists(/databases/$(database)/documents/UserData/$(request.auth.uid)/DAILY_USAGES/$(day)); allow read: if request.auth.uid == uid; allow update: if request.auth.uid == uid // Only allow updates to the 'usage' field && request.resource.data.keys().hasOnly(['usage']) // Optional: Ensure usage isn't reset to 0 (adjust based on your logic) && request.resource.data.usage > 0; }
Then, in your Android code, use update() instead of set() to modify only the usage field (this is more efficient anyway):
// Update only the usage value, no overwrite FirebaseFirestore.getInstance() .collection(Constants.USER_DATA_ROOT) .document(uid) .collection(Constants.FIRESTORE_CHILD_DAILY_USAGES) .document(today) .update("usage", newUsageValue);
If you prefer to keep using set(), use the merge option to limit changes to specific fields:
DailyUsage usage = new DailyUsage(); usage.setUsage(newUsageValue); FirebaseFirestore.getInstance() .collection(Constants.USER_DATA_ROOT) .document(uid) .collection(Constants.FIRESTORE_CHILD_DAILY_USAGES) .document(today) .set(usage, SetOptions.mergeFields("usage"));
Solution 2: Block all modifications after document creation
If you never need to update the document once it's created (only read it), just block update and delete operations entirely:
match /UserData/{uid}/DAILY_USAGES/{day} { allow create: if !exists(/databases/$(database)/documents/UserData/$(request.auth.uid)/DAILY_USAGES/$(day)); allow read: if request.auth.uid == uid; // Block all updates and deletes to prevent overwrites allow update, delete: if false; }
Now, when you call set() on an existing document, it will fail because the update operation is blocked—exactly what you want, no pre-check needed in your UI.
Why did the console simulator work? Because you were testing the create operation specifically. The simulator doesn't automatically test update when you run a write on an existing document, so you missed that gap in your rules.
内容的提问来源于stack exchange,提问作者ertuzun

