身份验证后读取数据时 Firebase 权限被拒绝

Firebase Permission denied when reading data after authentication

我是 Firebase 新手。我使用电子邮件和密码对用户进行了身份验证 -

final Firebase ref = new Firebase("https://app.firebaseio.com");
ref.authWithPassword("app@firebaseio.com", "password", new Firebase.AuthResultHandler() {

    @Override
    public void onAuthenticated(AuthData authData) {
        System.out.println("User ID: " + authData.getUid());
        getData(ref);
    }

    @Override
    public void onAuthenticationError(FirebaseError firebaseError) {
    }
});

但是在我读取数据时经过身份验证后,我得到 Permission Denied

private void getData(Query ref) {
    ref.addValueEventListener(new ValueEventListener() {

        @Override
        public void onDataChange(DataSnapshot snapshot) {
            System.out.println(snapshot.getValue());
        }

        @Override
        public void onCancelled(FirebaseError firebaseError) {
            System.out.println("The read failed: " + firebaseError.getMessage());
        }
    });
}

这些是 Firebase 规则。

{
    "rules": {
        "users": {
            "$uid": {
                ".read": "auth !== null && auth.provider === 'password'"
            }
        }
    }
}

Firebase 的权限模型只允许用户访问您明确授予访问权限的数据。由于在您的安全规则中,您只授予对 /users/$uid 的访问权限,用户无法从 root / 读取。 Firebase 文档在 "rules cascade".

下对此进行了介绍

您似乎想使用安全规则来过滤数据,这在 Firebase 的安全模型中是不可能的。请参阅 Firebase 文档中的 "rules are not filters" 部分以及之前的问题和答案:

  • firebase rules not working
  • How to use Firebase rules to only give permission to certain leaf nodes

最简单的解决方案是允许读取 users 节点:

{
    "rules": {
        "users": {
            ".read": "auth !== null && auth.provider === 'password'"
        }
    }
}

然后在该级别查询:

getData(ref.child("users"));

我使用稍微不同的方法从我的数据库中读取时遇到了这个异常,但这就是我解决问题的方法。

首先,我的数据库规则是这样的:

{
"rules": {
  "student": {
    "$uid": {
      ".write": "auth != null && auth.uid == $uid",
      ".read": "auth != null && auth.uid == $uid"
      }
    }
  }
}

早些时候,为了写入学生数据库,我在 Activity:

中使用了这段代码
 mAuthListener = new FirebaseAuth.AuthStateListener() {
        @Override
        public void onAuthStateChanged(@NonNull FirebaseAuth firebaseAuth) {
            user = firebaseAuth.getCurrentUser();
            if (user != null) {
                // User is signed in
                Log.e(TAG, "onAuthStateChanged:signed_in:" + user.getUid());
            } else {
                // User is signed out
                Log.e(TAG, "onAuthStateChanged:signed_out");
            }
            // ...
        }
    };
    ...
    Student student = new Student();
    student.setPhoneNumber("+23480547455343");
    student.setCountryOfOrigin("Nigeria");
    mDatabaseReference.child("student").child(user.getUid()).setValue(student).
                addOnCompleteListener(DetailsCaptureActivity.this, 
                                   new OnCompleteListener<Void>() {
     ...
     });

请注意 child 姓名(学生)如何与 firebase 数据规则中的 child 姓名匹配?

现在要读取这个用户的数据,我是这样做的:

 //Set up an AuthStateListener that responds to changes in the user's sign-in state:
    mAuthListener = new FirebaseAuth.AuthStateListener() {
        @Override
        public void onAuthStateChanged(@NonNull FirebaseAuth firebaseAuth) {
            user = firebaseAuth.getCurrentUser();
            if (user != null) {

                   databaseReference = firebaseDatabase.getReference().child("student").child(user.getUid());
                   databaseReference.addValueEventListener(new ValueEventListener() {
                      @Override
                      public void onDataChange(DataSnapshot dataSnapshot) {
                          Student student = dataSnapshot.getValue(Student.class);
                          phoneNumberTextView.setText(student.getPhoneNumber());
                      }

                      @Override
                      public void onCancelled(DatabaseError databaseError) {

                          Log.e(TAG, databaseError.getMessage());
                      }
                  });

            } else {
                Log.e(TAG, "onAuthStateChanged:signed_out");
            }
        }
    };

如果我这样做,我得到了权限被拒绝的异常:

databaseReference = firebaseDatabase.getReference().child(user.getUid());

问题是Firebase有一个BUG,认证后,必须等用户出现在FirebaseAuth,然后才能使用。

我所做的就是等待它出现,比如

Observable.fromCallable(() -> signInImpl())
        .map(this::toFirebaseUser)
        .map(this::waitForUserToAppearInAuthenticator)
        .flatMap(this::doSomethingInDatabase);

在哪里

@NonNull
private FirebaseUser waitForUserToAppearInAuthenticator(@NonNull final FirebaseUser user) {
    final CountDownLatch cdl = new CountDownLatch(1);
    final FirebaseAuth.AuthStateListener l = firebaseAuth -> {
        final FirebaseUser cu = firebaseAuth.getCurrentUser();
        if (cu != null && user.getUid().equals(cu.getUid())) {
            cdl.countDown();
        }
    };
    mFirebaseAuth.addAuthStateListener(l);
    try {
        cdl.await(20, TimeUnit.SECONDS);
    } catch (InterruptedException e) {
        throw new RuntimeException(e);
    } finally {
        mFirebaseAuth.removeAuthStateListener(l);
    }

    return user;
}