如何解决Firebase RTDB认证后立即读取权限被拒问题?
解决Firebase认证后立即读取数据库权限拒绝问题
问题现象
Web端完成Firebase邮箱密码认证后,立即读取数据库会触发权限拒绝错误,但重新加载页面后再登录,就能正常读取数据。
错误信息
Error: permission_denied at /sandboxusers/KEY_USER_ID/companies:
Client doesn't have permission to access the desired data.
当前安全规则
"sandboxusers" : { "$user_id" : { ".read": "auth != null", ".write": false } },
已尝试的临时方案
- 将
.read设为true:代码可正常运行,但数据库完全暴露给任意用户,存在严重安全隐患 - 认证流程后添加500ms延迟:能临时绕过问题,但方案不可靠且不符合开发规范
现有认证代码
页面调用认证的代码
Map info = await userProvider.signIn(user.user!, user.password!); if (info['ok']) { await Future.delayed(const Duration(milliseconds: 500), () {}); // 需要移除的延迟 Navigator.of(context).pushNamedAndRemoveUntil('parameters', (route) => false); // 跳转到读取数据库的参数页面 } else { // 认证失败提示信息 }
UserProvider中的signIn方法
Future <Map<String, dynamic>> signIn(String email, String password) async { try { UserCredential result = await FirebaseAuth.instance.signInWithEmailAndPassword(email: email, password: password); User user = result.user!; return {'ok' : true, 'localId': user.uid, 'email' : user.email}; } on FirebaseAuthException catch (e) { log('$e'); return {'ok': false, 'code': e.code, 'message': '${e.message}' }; } }
无效的尝试
曾尝试通过authStateChanges回调跳转,但未生效:
if (info['ok']) { FirebaseAuth.instance.authStateChanges().listen((User? user) { if (user != null) { Navigator.of(context).pushNamedAndRemoveUntil('parametros', (route) => false); } }); } else { // 认证失败提示信息 }
解决方案
原因分析
问题核心:signInWithEmailAndPassword返回成功时,Firebase Auth的本地认证状态还未完全同步到Realtime Database客户端,此时发起数据库请求,数据库端无法识别到有效认证信息,导致规则校验失败。
正确实现方式
方式1:通过idTokenChanges监听确保状态同步
idTokenChanges会在用户认证状态变更且token就绪时触发,比authStateChanges更适合确保数据库权限校验通过:
userProvider.signIn(user.user!, user.password!).then((info) { if (info['ok']) { StreamSubscription? subscription; subscription = FirebaseAuth.instance.idTokenChanges().listen((User? user) { if (user != null) { // 取消订阅避免多次触发跳转 subscription?.cancel(); Navigator.of(context).pushNamedAndRemoveUntil('parameters', (route) => false); } }); } else { // 认证失败提示信息 } }).catchError((error) { // 处理异常 });
方式2:强制刷新ID Token
直接调用getIdToken(true)强制刷新token,确保认证状态完全同步后再跳转:
Map info = await userProvider.signIn(user.user!, user.password!); if (info['ok']) { // 强制刷新并获取最新token,确保数据库客户端已同步认证状态 await FirebaseAuth.instance.currentUser?.getIdToken(true); Navigator.of(context).pushNamedAndRemoveUntil('parameters', (route) => false); } else { // 认证失败提示信息 }
安全规则优化建议
为进一步提升安全性,建议将规则限制为仅登录用户能访问自己的数据:
"sandboxusers" : { "$user_id" : { ".read": "auth != null && auth.uid == $user_id", ".write": false } },
内容的提问来源于stack exchange,提问作者David L
相关产品推荐
相关产品推荐

