request.auth.uid 在 Firestore 安全规则中不起作用
request.auth.uid is not working in firestore security rule
我有如下所示的 firestore 数据库,
uid 字段包含添加文档的经过身份验证的用户的 uid
安全角色:
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /crush/{doc} {
allow read: if request.auth != null && request.auth.uid == resource.data.uid;
allow write : if request.auth != null;
}
}
}
模拟读取规则有效
然而,在实际的flutter App中,只有DB写入工作,读取失败并出现以下错误
/Firestore(6538): (24.0.1) [Firestore]: 监听查询(target=Query(crush order by name);limitType =LIMIT_TO_FIRST) 失败:状态{code=PERMISSION_DENIED, description=Missing or insufficient permissions., cause=null}
这让我相信 request.auth.uid 对于传入读取是空白的
程序如下所示
# landing.dart
# Authentication class is helper for FirebaseAuth.instance
import 'home.dart';
class Landing extends StatefulWidget {
const Landing({Key? key}) : super(key: key);
@override
State<Landing> createState() => _LandingState();
}
class _LandingState extends State<Landing> {
dynamic authStream = Authentication().authState;
@override
Widget build(BuildContext context) {
return StreamBuilder(
stream: authStream,
builder: (BuildContext context, AsyncSnapshot<AppUser?> snapshot) {
if (snapshot.data != null) {
return Home(snapshot.data);
}
else {
return SignIn(); //via google
}
});
}
}
# home.dart
# Database class is helper for FirebaseFirestore.instance
import 'package:flutter/material.dart';
import 'package:password_safe/models/crush.dart';
import 'package:password_safe/models/user.dart';
import 'package:password_safe/services/authentication.dart';
import 'package:password_safe/services/database.dart';
class Home extends StatefulWidget {
AppUser? user;
Home(this.user, {Key? key}) : super(key: key);
@override
_HomeState createState() => _HomeState();
}
class _HomeState extends State<Home> {
dynamic streamCrush = Database().getCrushSnapshot;
@override
Widget build(BuildContext context) {
return Scaffold(
floatingActionButton: FloatingActionButton(
onPressed: () {
provideInput(context, widget.user);
},
child: Icon(Icons.add),
),
body: StreamBuilder(
stream: streamCrush,
builder: (BuildContext context, AsyncSnapshot<List<Crush>> snapshot) {
if (snapshot.hasError) {
return Center(child: Text('Error Ocurred'));
}
if (snapshot.connectionState == ConnectionState.waiting) {
return CircularProgressIndicator();
}
return myList(snapshot.data!);
},
),
);
}
}
\ database.dart
import 'dart:developer' as developer;
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:password_safe/models/crush.dart';
class Database {
late FirebaseFirestore _db;
late CollectionReference crushCollection;
final String _logName = "Database";
static final Database _instance = Database._internal();
Database._internal() {
try {
_db = FirebaseFirestore.instance;
crushCollection = _db.collection('crush');
} catch (e) {
// do something
}
}
factory Database() {
return _instance;
}
Future<void> addCrush(Crush crush) async {
await crushCollection
.add(crush.toMap())
.then(
(value) => developer.log('adding crush to backend', name: _logName),
)
.catchError(
(error) => developer.log('Error while adding crush',
name: _logName, error: error),
);
}
Stream<List<Crush>> get getCrushSnapshot {
return crushCollection
.snapshots()
.map((event) => _crushListFromSnapshot(event));
}
List<Crush> _crushListFromSnapshot(QuerySnapshot snapshot) {
return snapshot.docs.map((e) {
Map<String, dynamic> data = e.data() as Map<String, dynamic>;
data.addAll({'id': e.id});
return Crush.fromMap(data);
}).toList();
}
Future<void> deleteCrush(Crush crush) {
return crushCollection
.doc(crush.id)
.delete()
.then((value) =>
developer.log('deleting crush from backend', name: _logName))
.onError((error, stackTrace) => developer.log(
'error occured while deleting data from crush',
name: _logName,
error: error,
stackTrace: stackTrace));
}
}
pubspec.yaml
dependencies:
cloud_firestore:
google_sign_in: '^4.5.1'
firebase_auth:
firebase_core:
flutter:
sdk: flutter
您的查询约束与安全规则约束不匹配。您正在尝试获取 /crush/*
(根据您的数据库查询),但根据您的 Firestore 安全规则,您只能访问 /crush/{doc}
。
即使当前用户实际上是每个文档的作者,查询也会失败。出现此行为的原因是,当 Cloud Firestore 应用您的安全规则时,它会根据其潜在结果集而不是数据库中文档的实际属性来评估查询。如果查询可能包含违反安全规则的文档,则查询将失败。
因此您需要过滤数据库查询以匹配文档的 uid 字段与 auth.uid。像这样:
var user = FirebaseAuth.instance.currentUser;
db.collection("crush").where(“uid”, "==", user.uid).get()
您可以阅读更多关于基于 auth.id here.
保护和查询文档的信息
我有如下所示的 firestore 数据库, uid 字段包含添加文档的经过身份验证的用户的 uid
安全角色:
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /crush/{doc} {
allow read: if request.auth != null && request.auth.uid == resource.data.uid;
allow write : if request.auth != null;
}
}
}
模拟读取规则有效
然而,在实际的flutter App中,只有DB写入工作,读取失败并出现以下错误
/Firestore(6538): (24.0.1) [Firestore]: 监听查询(target=Query(crush order by name);limitType =LIMIT_TO_FIRST) 失败:状态{code=PERMISSION_DENIED, description=Missing or insufficient permissions., cause=null}
这让我相信 request.auth.uid 对于传入读取是空白的
程序如下所示
# landing.dart
# Authentication class is helper for FirebaseAuth.instance
import 'home.dart';
class Landing extends StatefulWidget {
const Landing({Key? key}) : super(key: key);
@override
State<Landing> createState() => _LandingState();
}
class _LandingState extends State<Landing> {
dynamic authStream = Authentication().authState;
@override
Widget build(BuildContext context) {
return StreamBuilder(
stream: authStream,
builder: (BuildContext context, AsyncSnapshot<AppUser?> snapshot) {
if (snapshot.data != null) {
return Home(snapshot.data);
}
else {
return SignIn(); //via google
}
});
}
}
# home.dart
# Database class is helper for FirebaseFirestore.instance
import 'package:flutter/material.dart';
import 'package:password_safe/models/crush.dart';
import 'package:password_safe/models/user.dart';
import 'package:password_safe/services/authentication.dart';
import 'package:password_safe/services/database.dart';
class Home extends StatefulWidget {
AppUser? user;
Home(this.user, {Key? key}) : super(key: key);
@override
_HomeState createState() => _HomeState();
}
class _HomeState extends State<Home> {
dynamic streamCrush = Database().getCrushSnapshot;
@override
Widget build(BuildContext context) {
return Scaffold(
floatingActionButton: FloatingActionButton(
onPressed: () {
provideInput(context, widget.user);
},
child: Icon(Icons.add),
),
body: StreamBuilder(
stream: streamCrush,
builder: (BuildContext context, AsyncSnapshot<List<Crush>> snapshot) {
if (snapshot.hasError) {
return Center(child: Text('Error Ocurred'));
}
if (snapshot.connectionState == ConnectionState.waiting) {
return CircularProgressIndicator();
}
return myList(snapshot.data!);
},
),
);
}
}
\ database.dart
import 'dart:developer' as developer;
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:password_safe/models/crush.dart';
class Database {
late FirebaseFirestore _db;
late CollectionReference crushCollection;
final String _logName = "Database";
static final Database _instance = Database._internal();
Database._internal() {
try {
_db = FirebaseFirestore.instance;
crushCollection = _db.collection('crush');
} catch (e) {
// do something
}
}
factory Database() {
return _instance;
}
Future<void> addCrush(Crush crush) async {
await crushCollection
.add(crush.toMap())
.then(
(value) => developer.log('adding crush to backend', name: _logName),
)
.catchError(
(error) => developer.log('Error while adding crush',
name: _logName, error: error),
);
}
Stream<List<Crush>> get getCrushSnapshot {
return crushCollection
.snapshots()
.map((event) => _crushListFromSnapshot(event));
}
List<Crush> _crushListFromSnapshot(QuerySnapshot snapshot) {
return snapshot.docs.map((e) {
Map<String, dynamic> data = e.data() as Map<String, dynamic>;
data.addAll({'id': e.id});
return Crush.fromMap(data);
}).toList();
}
Future<void> deleteCrush(Crush crush) {
return crushCollection
.doc(crush.id)
.delete()
.then((value) =>
developer.log('deleting crush from backend', name: _logName))
.onError((error, stackTrace) => developer.log(
'error occured while deleting data from crush',
name: _logName,
error: error,
stackTrace: stackTrace));
}
}
pubspec.yaml
dependencies:
cloud_firestore:
google_sign_in: '^4.5.1'
firebase_auth:
firebase_core:
flutter:
sdk: flutter
您的查询约束与安全规则约束不匹配。您正在尝试获取 /crush/*
(根据您的数据库查询),但根据您的 Firestore 安全规则,您只能访问 /crush/{doc}
。
即使当前用户实际上是每个文档的作者,查询也会失败。出现此行为的原因是,当 Cloud Firestore 应用您的安全规则时,它会根据其潜在结果集而不是数据库中文档的实际属性来评估查询。如果查询可能包含违反安全规则的文档,则查询将失败。
因此您需要过滤数据库查询以匹配文档的 uid 字段与 auth.uid。像这样:
var user = FirebaseAuth.instance.currentUser;
db.collection("crush").where(“uid”, "==", user.uid).get()
您可以阅读更多关于基于 auth.id here.
保护和查询文档的信息