Flutter应用Firestore权限拒绝异常排查求助
Flutter Firestore权限拒绝异常解决
问题场景
开发Flutter应用时,用户点击注册按钮后,系统会检查输入邮箱是否存在于Firestore的users集合中,不存在则跳转下一页面。但触发检查时出现未处理异常:
[ERROR:flutter/runtime/dart_vm_initializer.cc(41)] Unhandled Exception: [cloud_firestore/permission-denied] The caller does not have permission to execute the specified operation.
当前配置与代码
Firestore数据库规则
rules_version = '2'; service cloud.firestore { match /databases/{database}/documents { match /{document=**} { allow read, write: if request.auth != null; } } }
项目级build.gradle
buildscript { ext.kotlin_version = '1.6.10' repositories { google() mavenCentral() } dependencies { classpath 'com.android.tools.build:gradle:7.1.2' classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" classpath 'com.google.gms:google-services:4.3.13' } } allprojects { repositories { google() mavenCentral() } } rootProject.buildDir = '../build' subprojects { project.buildDir = "${rootProject.buildDir}/${project.name}" } subprojects { project.evaluationDependsOn(':app') } task clean(type: Delete) { delete rootProject.buildDir }
应用级build.gradle
def localProperties = new Properties() def localPropertiesFile = rootProject.file('local.properties') if (localPropertiesFile.exists()) { localPropertiesFile.withReader('UTF-8') { reader -> localProperties.load(reader) } } def flutterRoot = localProperties.getProperty('flutter.sdk') if (flutterRoot == null) { throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.") } def flutterVersionCode = localProperties.getProperty('flutter.versionCode') if (flutterVersionCode == null) { flutterVersionCode = '1' } def flutterVersionName = localProperties.getProperty('flutter.versionName') if (flutterVersionName == null) { flutterVersionName = '1.0' } apply plugin: 'com.android.application' apply plugin: 'com.google.gms.google-services' apply plugin: 'kotlin-android' apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" android { compileSdkVersion flutter.compileSdkVersion ndkVersion flutter.ndkVersion compileOptions { sourceCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8 } kotlinOptions { jvmTarget = '1.8' } sourceSets { main.java.srcDirs += 'src/main/kotlin' } defaultConfig { // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). applicationId "com.recipedia.fyp.recipedia" // You can update the following values to match your application needs. // For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-build-configuration. minSdkVersion 21 targetSdkVersion flutter.targetSdkVersion versionCode flutterVersionCode.toInteger() versionName flutterVersionName } buildTypes { release { // TODO: Add your own signing config for the release build. // Signing with the debug keys for now, so `flutter run --release` works. signingConfig signingConfigs.debug } } } flutter { source '../..' } dependencies { implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version" implementation platform('com.google.firebase:firebase-bom:31.0.3') }
注册按钮点击逻辑
onPressed: () async { if (nameTextController.text.isEmpty) { displayToastMessage( "Please enter name", context); } else if (nameTextController.text.length < 3) { displayToastMessage("Name must be atleast 3 characters", context); } else if (nameTextController.text.contains(RegExp(r'[0-9]'))) { displayToastMessage("Numbers and special characters cannot be included", context); } else if (emailTextController.text.isEmpty) { displayToastMessage("Please enter email", context); } else if (!emailTextController.text.contains('@')) { displayToastMessage("Please enter a valid email", context); } else if (passwordTextController.text.isEmpty) { displayToastMessage("Please enter password", context); } else if (passwordTextController.text.length < 6) { displayToastMessage("Password must be at-least 6 Characters", context); } else { print('Before emailExists'); emailExists = await UserModel().checkIfEmailExists(email); print('Email exist: $emailExists'); if (emailExists == true) { snackBar(context, 'Email is already registered'); } else { /*Move to next screen*/ } } }
UserModel中的邮箱检查方法
Future<bool> checkIfEmailExists(String email) async { try { var collectionReference = FirebaseFirestore.instance.collection('users'); var doc = await collectionReference.doc(email).get(); return doc.exists; } catch (e) { rethrow; } }
问题原因与解决方案
核心原因
当前Firestore规则要求所有读写操作必须在用户已认证(request.auth != null)的前提下执行,但注册流程中用户还未完成账号创建,此时request.auth为null,直接触发权限拒绝。
解决方案
调整Firestore规则,为注册场景开放邮箱存在性检查的权限,同时保证其他操作的安全性:
rules_version = '2'; service cloud.firestore { match /databases/{database}/documents { // 允许未认证用户检查users集合中指定邮箱的文档是否存在 match /users/{email} { allow get: if request.auth == null; allow write: if request.auth != null; } // 其他集合保持原有认证要求 match /{document=**} { allow read, write: if request.auth != null; } } }
额外注意事项
- 规则更新后需等待Firestore后台生效(通常几秒到几分钟)
- 生产环境中建议结合Firebase Auth的邮箱验证机制,避免恶意遍历邮箱
- 后续注册流程中写入用户数据时,需确保用户完成认证后再执行操作
内容的提问来源于stack exchange,提问作者Mr Fin
相关产品推荐
相关产品推荐

