【问题标题】:How to create a new Firestore user collection directly from Flutter code如何直接从 Flutter 代码创建新的 Firestore 用户集合
【发布时间】:2022-01-05 20:04:17
【问题描述】:

借助 stackoverflow 的帮助,我已经能够让 Firebase 身份验证适用于 Google 登录、匿名登录以及通过电子邮件和密码登录,包括在电子邮件和密码登录期间发送验证电子邮件。一切都按预期工作。现在我的下一步是尝试使用 Firebase 身份验证创建的 uid 在 Firestore 中创建一个用户集合。我确信我的代码编写正确,因为我已经使用(不安全的)安全规则对其进行了测试,并且该过程完全符合预期。 我已经多次查看 Firebase 文档,但我无法弄清楚我的安全规则代码有什么问题。如何修复我的安全规则以允许新用户创建将添加到 Firestore 中的用户集合的屏幕名称?在此先感谢您的帮助。

rules_version = '2';
service cloud.firestore {
  match /databases/{database}/documents {
    match /users/{uid}/jobs/{document=**} {
       allow read, write: if request.auth.uid == uid;
  }

  match /users/{uid}/{document=**} {
       allow read, write: if request.auth.uid == uid;
  }

  }
  
  }


   class HomePage extends StatefulWidget {
      const HomePage({
        Key? key,
        
      }) : super(key: key);
    
      
    
      @override
      State<HomePage> createState() => _HomePageState();
    }
    
    class _HomePageState extends State<HomePage> {
      @override
      void initState() {
        super.initState();
        createUserInFirestore();
      }
    
      Future<void> createUserInFirestore() async {
        final GoogleSignIn googleSignIn = GoogleSignIn();
        final GoogleSignInAccount? user = googleSignIn.currentUser;
        final usersRef = FirebaseFirestore.instance.collection('users');
        final DocumentSnapshot doc = await usersRef.doc(user?.id).get();
        if (!doc.exists) {
          final userName = await Navigator.push(
            context,
            MaterialPageRoute(
              builder: (context) => const CreateAccountPage(),
            ),
          );
          usersRef.doc(user?.id).set({
            'id': user?.id,
            'userName': userName,
            'photoUrl': user?.photoUrl,
            'email': user?.email,
            'displayName': user?.displayName,
            'bio': '',
            'timestamp': documentIdFromCurrentDate(),
          });
        doc = await usersRef.doc(user?.id).get();
        }
      }
    
      @override
      Widget build(BuildContext context) {
        return AdaptiveLayoutScaffold(
          drawer: const SideSheet(
            userImage: FakeUserAvatars.stacy,
            userName: 'Stacy James',
          ),
          landscapeBodyWidget: Container(),
          portraitBodyWidget: Container(),
        );
      }
    }

class CreateAccountPage extends StatefulWidget {
  const CreateAccountPage({
    Key? key,
  }) : super(key: key);

  @override
  _CreateAccountPageState createState() => _CreateAccountPageState();
}

class _CreateAccountPageState extends State<CreateAccountPage> {
  final _formKey = GlobalKey<FormState>();
  late String userName;

  void submit() {
    _formKey.currentState?.save();
    Navigator.pop(context, userName);
  }

  @override
  Widget build(BuildContext context) {
    return AdaptiveLayoutScaffold(
      appBar: const Header(
        automaticallyImplyLeading: false,
        pageName: 'User Name',
      ),
      landscapeBodyWidget: Container(),
      portraitBodyWidget: ListView(
        children: [
          Column(
            children: [
              const Padding(
                padding: EdgeInsets.only(top: 16.0),
                child: Center(
                  child: Text('Create a User Name'),
                ),
              ),
              Padding(
                padding: const EdgeInsets.all(16.0),
                child: Form(
                  key: _formKey,
                  child: TextFormField(
                    autovalidateMode: AutovalidateMode.always,
                    decoration: InputDecoration(
                      hintText: 'Must be between 3 and 20 characters',
                      labelText: 'User Name',
                      prefixIcon: Icon(
                        Icons.person,
                        color: Theme.of(context).iconTheme.color,
                      ),
                    ),
                    keyboardType: TextInputType.text,
                    onSaved: (val) => userName = val as String,
                  ),
                ),
              ),
              PlatformElevatedButton(
                onPressed: submit,
                buttonText: 'Create User Name',
              ),
            ],
          ),
        ],
      ),
    );
  }
}

【问题讨论】:

  • 当您通过命名任何集合添加数据时,Firestore 会自动创建集合。您是否收到任何权限错误?
  • 正在 Firebase 中创建新用户,但控制台显示“失败:状态{code=PERMISSION_DENIED, description=Missing 或权限不足。, cause=null。未处理的异常:[cloud_firestore/permission-denied] 调用者没有执行指定操作的权限。 @Anuj Raghuvanshi
  • 是的,没错。这是您为安全规则添加的内容。登录后允许访问该集合。但如果没有登录,您将无法访问任何集合进行操作。您可以添加match /users/{uid}/users/{document=**} { allow read, write: if true; } 让用户收藏始终可读可写并尝试这样做。
  • 添加为我拥有的规则的替代品或添加的规则? @AnujRaghuvanshi。我尝试了两种方式(我认为)并且出现了同样的错误,这很奇怪。新用户是在 Firebase 中创建的,但在 Firestore 中没有创建任何集合,应用会直接进入主页而不是创建帐户页面。
  • @CarletonY,你能检查一下这个thread,看看它是否有帮助。谢谢。

标签: firebase flutter google-cloud-firestore firebase-security


【解决方案1】:

在阅读了建议和其他一些内容后,我使用 Firestore Rules Playground 修复了我的代码,然后更新了我的 Auth 类以包含一个名为 createUserInFirestore() 的新方法来处理在 Firestore 中创建新用户的使用Firebase身份验证创建用户后的uid。

rules_version = '2';
service cloud.firestore {
  match /databases/{database}/documents {
  
    match /users/{uid}/{document=**} {
       allow read, create, update, delete: if request.auth.uid == uid;  
    }
  } 
}

abstract class AuthBase {
  User? get currentUser;

  Stream<User?> authStateChanges();
  Future<User?> signInWithGoogle();
  Future<User?> createUserWithEmailAndPassword(
    String email,
    String password,
  );
  Future<void> checkEmailVerified(BuildContext context, Timer timer);
  Future<User?> signInWithEmailAndPassword(String email, String password);
  Future<User?> signInAnonymously();
  Future<void> resetPassword(BuildContext context, String email);
  Future<void> confirmSignOut(BuildContext context);
  Future<void> signOut();
}

class Auth implements AuthBase {
  final _firebaseAuth = FirebaseAuth.instance;

  @override
  User? get currentUser => _firebaseAuth.currentUser;

  @override
  Stream<User?> authStateChanges() => _firebaseAuth.authStateChanges();

  void _createNewUserInFirestore() {
    final User? user = currentUser;
    final CollectionReference<Map<String, dynamic>> usersRef =
        FirebaseFirestore.instance.collection('users');
    usersRef.doc(user?.uid).set({
      'id': user?.uid,
      'screenName': '',
      'displayName': user?.displayName,
      'photoUrl': user?.photoURL,
      'bio': '',
      'darkMode': false,
      'timestamp': documentIdFromCurrentDate(),
    });
  }

  @override
  Future<User?> signInWithGoogle() async {
    final GoogleSignIn googleSignIn = GoogleSignIn();
    final GoogleSignInAccount? googleUser = await googleSignIn.signIn();
    if (googleUser != null) {
      final googleAuth = await googleUser.authentication;
      if (googleAuth.idToken != null) {
        final UserCredential userCredential =
            await _firebaseAuth.signInWithCredential(
          GoogleAuthProvider.credential(
            idToken: googleAuth.idToken,
            accessToken: googleAuth.accessToken,
          ),
        );
        _createNewUserInFirestore();
        return userCredential.user;
      } else {
        throw FirebaseAuthException(
          code: FirebaseExceptionString.missingGoogleIDTokenCode,
          message: FirebaseExceptionString.missingGoogleIDTokenMessage,
        );
      }
    } else {
      throw FirebaseAuthException(
        code: FirebaseExceptionString.abortedByUserCode,
        message: FirebaseExceptionString.canceledByUserMessage,
      );
    }
  }

  @override
  Future<User?> createUserWithEmailAndPassword(
    String email,
    String password,
  ) async {
    final UserCredential userCredential =
        await _firebaseAuth.createUserWithEmailAndPassword(
      email: email,
      password: password,
    );
    _createNewUserInFirestore();
    return userCredential.user;
  }

  @override
  Future<void> checkEmailVerified(BuildContext context, Timer timer) async {
    final User? user = currentUser;
    await user?.reload();
    final User? signedInUser = user;
    if (signedInUser != null && signedInUser.emailVerified) {
      timer.cancel();
      Navigator.pushReplacement(
        context,
        MaterialPageRoute(
          builder: (context) => const HomePage(),
        ),
      );
    }
  }

  @override
  Future<User?> signInWithEmailAndPassword(
    String email,
    String password,
  ) async {
    final UserCredential userCredential =
        await _firebaseAuth.signInWithCredential(
      EmailAuthProvider.credential(
        email: email,
        password: password,
      ),
    );
    return userCredential.user;
  }

  @override
  Future<void> resetPassword(
    BuildContext context,
    String email,
  ) async {
    try {
      await _firebaseAuth.sendPasswordResetEmail(email: email);
      Navigator.of(context).pop();
    } catch (e) {
      print(
        e.toString(),
      );
    }
  }

  @override
  Future<User?> signInAnonymously() async {
    final UserCredential userCredential =
        await _firebaseAuth.signInAnonymously();
    return userCredential.user;
  }

  Future<void> _signOut(BuildContext context) async {
    try {
      final AuthBase auth = Provider.of<AuthBase>(
        context,
        listen: false,
      );
      await auth.signOut();
      Navigator.pushAndRemoveUntil<dynamic>(
        context,
        MaterialPageRoute<dynamic>(
          builder: (BuildContext context) => const LandingPage(),
        ),
        (route) => false,
      );
    } catch (e) {
      print(
        e.toString(),
      );
    }
  }

  @override
  Future<void> confirmSignOut(BuildContext context) async {
    final bool? didRequestSignOut = await showAlertDialog(
      context,
      cancelActionText: DialogString.cancel,
      content: DialogString.signOutAccount,
      defaultActionText: DialogString.signOut,
      title: DialogString.signOut,
    );
    if (didRequestSignOut == true) {
      _signOut(context);
    }
  }

  @override
  Future<void> signOut() async {
    final GoogleSignIn googleSignIn = GoogleSignIn();
    await googleSignIn.signOut();
    await _firebaseAuth.signOut();
  }
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2020-04-23
    • 1970-01-01
    • 2018-10-29
    • 1970-01-01
    • 2019-11-30
    • 1970-01-01
    • 2020-06-26
    • 2020-10-16
    相关资源
    最近更新 更多