将 Firebase 身份验证与 Firestore 一起使用的正确方法是啥?

Posted

技术标签:

【中文标题】将 Firebase 身份验证与 Firestore 一起使用的正确方法是啥?【英文标题】:What's the proper way to use Firebase Authentication along with Firestore?将 Firebase 身份验证与 Firestore 一起使用的正确方法是什么? 【发布时间】:2021-07-04 09:02:17 【问题描述】:

大家好 - 我正在使用 Next.js 和 Firebase 构建一个应用程序,这两种技术对我来说都是全新的。这是一个简单的应用程序,用户创建一个帐户并且必须登录。如果用户不创建一个帐户,该应用程序将毫无用处——他们无法进入下一个屏幕,即仪表板。无论如何,当他们登录时,他们就可以创建旅行/度假行程。我将 Firebase Auth 用于身份验证,并将 Firestore(不是实时数据库)用作我的数据库。我的目标是,当用户登录时,用户可以看到他们创建的每个行程,而没有其他人的。它应该是完整的 CRUD。这也是我第一次进行这种身份验证,所以这可能会增加我的困惑。

我知道我的代码不正确,但它确实有效。不断发生的是,用户登录和注销时似乎存在延迟。我已经在本地副本上对此进行了测试。当我注销然后以其他用户身份重新登录时,它告诉我 uid 为空。 1 到 30 分钟后(严重),页面突然加载了我登录时使用的 uid!我读过的所有内容都表明身份验证存在延迟,但除了指出问题之外,我真的找不到解决方案——所以基本上写了一个控制台日志,说明当时谁登录了,然后同样当他们注销时。另外,我观看/阅读了大量教程,所以也许这是我的代码?对于这部代码小说,我提前感到非常抱歉——我会尽我所能组织起来!


这是我的配置信息,所以我将 Firebase 称为火。登录方法是电子邮件和密码,就在身份验证屏幕上捕获该信息而言,一切在 Firebase 中看起来都应该如此。

import firebase from 'firebase';

const firebaseConfig = 
  apiKey: 'AIzaSyB-xEPETXSfjboKe5H0kPUu-ZdRDGfszmA',
  authDomain: "where-to-jess.firebaseapp.com",
  databaseURL: 'https://where-to-jess-default-rtdb.firebaseio.com/',
  projectId: "where-to-jess",
  storageBucket: "where-to-jess.appspot.com",
  messagingSenderId: "914509599583",
  appId: "1:914509599583:web:80cdf3e4090417b0f35cea"
;
try 
  firebase.initializeApp(firebaseConfig);
 catch(err)
  if (!/already exists/.test(err.message)) 
    console.error('Firebase initialization error', err.stack)

const fire = firebase;
export default fire;

当用户创建帐户时,他们也会被添加到我的数据库中的集合“用户”中。我也在使用 React Hooks(第一次)。他们的电子邮件是他们登录的用户名,但我正在数据库中捕获他们的电子邮件。他们也会在创建帐户后立即登录。这部分也有效。

const handleSubmit = (e) => 
    e.preventDefault();

    setPassword('');

    fire.auth().createUserWithEmailAndPassword(userName, password)
    .then(() => 
      fire.firestore().collection('users').doc(fire.auth().currentUser.uid)
      .set(
        firstName: firstName,
        lastName: lastName,
        email: userName
      )
      .catch(error => 
        console.log('user wasn\'t added to db: ', error);
      )
    )
    .catch(error => 
      console.log('user wasn\'t able to create an account: ', error);
    )
    router.push('/users/dashboard')
  ;

这是我的登录代码:

const handleLogin = (e) => 
    e.preventDefault();
    
    fire.auth()
      .signInWithEmailAndPassword(username, password)
      .catch((error) => 
        console.log('user wasn\'t able to login: ', error);
      )

    setUsername('')
    setPassword('')
    router.push('/users/dashboard')
  ;

现在是有趣的部分!这是我的行程表单提交代码。我在这里想要实现的是将这个新创建的行程附加到他们在“用户”数据库中的 uid。我省略了所有表单内容,因为它超长。这似乎也有效——无论我使用哪个帐户,我都可以在数据库中看到它。

const handleSubmit = (e) => 
    e.preventDefault();
    
    fire.firestore()
    .collection('users').doc(fire.auth().currentUser.uid).collection('itineraries')
    .add(

      //
    )

    .catch(error => 
      console.log('itinerary not added to db ', error)
    )
    router.push('/users/dashboard')
  

这就是它发生的地方!我怀疑这是因为我在偷工减料,我将在下面解释。此仪表板应仅显示当前登录用户创建的行程。如果当前登录的用户没有创建任何行程,我会收到一个错误,说 uid 为空。所以,我的解决方法是在他们帐户的数据库中手动创建一个假行程(因为我正在测试)并将 tripName 值设为空。这似乎可行,但这就是奇怪的登录/注销事情发生的地方。

export default function Dashboard() 
  const router = useRouter();
  const [itineraries, setItineraries] = useState([]);
  const [loggedIn, setLoggedIn] = useState(false);

  fire.auth()
  .onAuthStateChanged((user) => 
    if (user) 
      console.log(user.email + " is logged in!");
      setLoggedIn(true)
     else 
      setLoggedIn(false)
      console.log('User is logged out!');
    
  )

  useEffect(() => 
    const unsubscribe =
    fire.firestore()
    .collection('users').doc(fire.auth().currentUser.uid).collection('itineraries').where('tripName', '!=', 'null')
    .onSnapshot(snap => 
      const itineraries = snap.docs.map(doc => (
        id: doc.id,
        ...doc.data()
      ));
      setItineraries(itineraries);
      return () => 
        unsubscribe();
      ;
    );
  , []);


  const handleLogout = () => 
    fire.auth()
      .signOut()
    router.push('/')
  ;

最后,这是我在 db 上的一条规则。我在阅读规则文档时感到困惑,我觉得我在这里偷工减料。

rules_version = '2';
service cloud.firestore 
  match /databases/database/documents 
    match /document=** 
      allow read
      allow write
    
  


再次,我真的很抱歉所有这些代码。这是我第一次使用 React Hooks、Next 和 Firebase——所以它是 Firebases 的文档、教程和我自己的代码的混搭。如有任何帮助或建议,我将不胜感激。

【问题讨论】:

【参考方案1】:

该规则将允许所有访问当前数据库中的所有文档。你想要这样的东西:

    rules_version = '2';
    service cloud.firestore 
      match /databases/database/documents 
        match /users/user_id
           allow read, write: if request.auth != null && request.auth.uid == user_id;
        
    

这将只允许经过身份验证的用户访问

【讨论】:

非常感谢 - 如此基本的事情!这就是我尝试同时学习下一个、firebase 和 hooks 的结果......结合重新排序我上面的代码完全解决了这个问题。我没有将 useEffect 或 onAuthStateChanged 放在正确的位置。 Firebase 可能很难上手,我仍然每天都在学习更多。坚持不懈,你的果实终会开花结果:)【参考方案2】:

这是我 100% 的用户错误,但我想分享一下,因为我的一些问题似乎很常见。除了上述 AspiringApollo 的建议外,我的功能完全失灵(正如我所提到的,钩住新手)。上面加上像这样构造我的函数修复了它:

useEffect(() => 
    const unsubscribe = 
    fire.auth()
    .onAuthStateChanged((user) => 
      if (user) 
        let uid = user.uid
        console.log(user.email + ' is logged in!');
        setLoggedIn(true)
        // all the things you want to do while the user is logged in goes here
       else 
        setLoggedIn(false)
        console.log('user is logged out!');
      
      return () => 
        unsubscribe();
      ;
    );
  , []);

仍然愿意接受建议和更多的目光,因为我知道这有点混乱!

【讨论】:

以上是关于将 Firebase 身份验证与 Firestore 一起使用的正确方法是啥?的主要内容,如果未能解决你的问题,请参考以下文章

如何将 Google Play 游戏登录与 Firebase 身份验证结合使用

将 Google 身份验证令牌与 Firebase 一起使用

将 Firebase 身份验证与新的 @App 协议一起使用

将 Firebase 身份验证与 Firestore 一起使用的正确方法是啥?

如何将 Flutter Bloc 与 Firebase 电话身份验证一起使用

css 将Stripe订阅与firebase身份验证集成