Firestore访问规则拒绝访问时无报错及缓存相关技术咨询
解答你的Firestore权限规则与Flutter应用异常问题
先结合你的测试场景和Firestore的工作机制,逐个拆解你的问题:
问题1:访问被拒绝时,我是否应该收到提示?
是的,正常情况下应该触发onError回调抛出权限拒绝的错误,但你遇到的静默无响应是FlutterFire(Firebase的Flutter SDK)的已知bug:当通过snapshots()订阅实时数据流时,如果权限被拒绝,SDK不会触发错误回调,只会静默终止数据流。
你提到用getDocuments().asStream()能正常触发错误,是因为单次文档/集合请求的错误处理逻辑和实时流的处理逻辑不同。这个问题已经有不少开发者反馈过,官方正在跟进修复。
在正式应用中,为了避免用户一直处于加载状态,你可以做这些临时处理:
- 先通过
FirebaseAuth.instance.currentUser()判断用户是否已登录,未登录时直接引导用户进入登录页面,不发起数据请求 - 可以在订阅流的同时,发起一个单次
getDocuments()请求作为兜底检测,如果这个请求报错,就触发登录引导逻辑
问题2:修改规则后,应用为何仍能获取数据?为何必须卸载重装?
这是Firestore客户端的本地持久化缓存机制导致的:
- 当你使用允许所有访问的规则时,应用已经获取到数据并缓存到了本地设备上
- 修改规则后,客户端默认会优先读取本地缓存的数据,而不会立刻去服务器验证新规则
- 卸载重装会清除本地的Firestore缓存,此时应用会重新向服务器请求数据,新的权限规则才会生效
你通过添加await Firestore.instance.settings(persistenceEnabled: false);禁用缓存后数据不再显示,也验证了这个机制。不过正式环境不建议禁用缓存,它能提升离线体验。
另外,Firebase规则变更确实需要最多1分钟生效,但你遇到的延迟主要是缓存的问题,不是规则生效的延迟。
问题3:我哪里操作有误?
你的代码和测试流程本身没有错误,主要是遇到了SDK的已知bug和Firestore缓存机制的正常行为。不过可以给你几个优化建议:
- 测试权限规则时,可以临时禁用持久化,避免缓存干扰测试结果
- 正式应用中,监听
FirebaseAuth的状态变化(比如用authStateChanges()),只有当用户登录后再发起Firestore数据请求,从根源上避免权限拒绝的情况 - 可以在数据流订阅中添加超时逻辑,如果一段时间内没有收到数据或错误,就判定为权限问题,引导用户登录
附你测试用的代码与规则
应用测试代码
import 'dart:async'; import 'package:cloud_firestore/cloud_firestore.dart'; import 'package:firebase_auth/firebase_auth.dart'; import 'package:flutter/material.dart'; void main() { runApp( MaterialApp( title: 'Test', home: Screen(), ), ); } class Screen extends StatefulWidget { @override _ScreenState createState() => _ScreenState(); } class _ScreenState extends State<Screen> { StreamSubscription<dynamic> _sub; @override void initState() { super.initState(); _initAsync(); } Future<void> _initAsync() async { final user = await FirebaseAuth.instance.currentUser(); print('>>> currentUser.uid: ${user?.uid}'); print('will start listening'); _sub = Firestore.instance.collection('categories').snapshots().listen( (querySnapshot) { print('>>> got data: $querySnapshot'); querySnapshot.documents.forEach((documentSnapshot) { print('>>> ${documentSnapshot.data['name']}'); }); }, onError: (error) { print('!!! got error: $error'); }); } @override void dispose() { _sub?.cancel(); super.dispose(); } @override Widget build(BuildContext context) { return Container(); } }
允许所有访问的规则
rules_version = '2'; service cloud.firestore { match /databases/{database}/documents { match /{document=**} { allow read, write; } } }
要求认证的规则
rules_version = '2'; service cloud.firestore { match /databases/{database}/documents { match /{document=**} { allow read, write: if request.auth != null; } } }
内容的提问来源于stack exchange,提问作者wujek
相关产品推荐
相关产品推荐

