You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Firestore批量写入触发权限拒绝,request.resource.data是否含子集合数据?

Firestore批量写入触发权限拒绝:字段校验规则与子集合操作冲突问题

问题场景

在Firestore中执行批量写入时遇到权限拒绝错误:

  • foo集合的安全规则中,update操作通过onlyHasAllowedFields函数校验文档字段仅能包含指定列表
  • 单独更新foo文档(修改hasName/insensitiveName/name字段)或单独在bar子集合创建文档均正常
  • 将两个操作合并到同一条批量写入中时,即使把bar的创建权限设为if (true),仍触发权限拒绝
  • 改用事务操作则可正常执行

安全规则代码

match /foo/{fooId} {

function onlyHasAllowedFields(data) {
                return data.diff({}).affectedKeys().hasOnly(['availableWithoutSub', 
                                                            'dateAdded', 'dateDeleted', 
                                                            'hasName', 
                                                            'insensitiveName', 'name',
                                                            'userId']);
}

allow create: if (
    ... 其他校验逻辑
    &&
    onlyHasAllowedFields(request.resource.data)
);

allow update: if (
    ... 其他校验逻辑
    &&
    onlyHasAllowedFields(request.resource.data)
);

    match /bar/{barId} {
        allow create: if (
            ... 原校验逻辑
        )
   }
}

批量写入代码

const updateName = async (newName: string, fooId: string) => {
    const batch = firestore().batch();
    
    // 更新foo文档
    const fooData = {
        hasName: newName.length > 0,
        name: newName,
        insensitiveName: newName.toLowerCase(),
    };

    batch.update(db.collection('foo').doc(fooId), fooData);

    // 在bar子集合创建新文档
    const barData = {
        fieldName: 'name',
        fieldValue: newName,
        userId: uid
    }
    batch.set(db
        .collection('foo')
        .doc(fooId)
        .collection('bar')
        .doc(), barData);

    batch.commit();
}

问题原因

onlyHasAllowedFields函数使用data.diff({}).affectedKeys()获取所有被修改的字段,但在批量写入包含子集合操作时,Firestore安全规则会误将子集合的写入操作纳入主文档的request.resource.data差异检查中,导致affectedKeys()返回的字段列表包含隐性的子集合相关键,超出了允许的字段范围,最终触发权限拒绝。

而事务操作会独立校验每个读写请求,主文档的更新校验不会被子集合操作干扰,因此能通过规则检查。

解决方案

修改字段校验函数,避免使用对比空对象的方式获取差异字段,改为直接校验字段或对比原文档与更新后文档的差异:

方案1:直接校验所有字段(适合Create操作)

function onlyHasAllowedFields(data) {
    return data.keys().hasOnly(['availableWithoutSub', 
                                'dateAdded', 'dateDeleted', 
                                'hasName', 
                                'insensitiveName', 'name',
                                'userId']);
}

方案2:仅校验实际修改的字段(适合Update操作)

function onlyHasAllowedFields(data) {
    // 对比原文档与更新后文档的差异字段
    const changedFields = data.diff(resource.data).affectedKeys();
    return changedFields.hasOnly(['availableWithoutSub', 
                                'dateAdded', 'dateDeleted', 
                                'hasName', 
                                'insensitiveName', 'name',
                                'userId']);
}

验证结果

修改规则后,批量写入操作可正常执行,无需改用事务。

内容的提问来源于stack exchange,提问作者Reagankm

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.22 12:02:06