Java “问题”;“拒绝许可”;在Firestore上根据安全规则获取
如果用户必须注册,我的Android Java应用程序首先使用Java “问题”;“拒绝许可”;在Firestore上根据安全规则获取,java,android,google-cloud-firestore,firebase-security,Java,Android,Google Cloud Firestore,Firebase Security,如果用户必须注册,我的Android Java应用程序首先使用AuthUI.getInstance()创建Firebase帐户(带密码的电子邮件)。创建帐户时,会出现一个对话框,通知用户他收到了一封验证电子邮件,他必须单击电子邮件中的验证链接。当用户完成此操作后,他可以关闭对话框并继续在Firestore中配置他的帐户 但是Firestore文档上的所有请求都受安全规则保护,如 allow read: if request.auth.uid != null && request.
AuthUI.getInstance()
创建Firebase帐户(带密码的电子邮件)。创建帐户时,会出现一个对话框,通知用户他收到了一封验证电子邮件,他必须单击电子邮件中的验证链接。当用户完成此操作后,他可以关闭对话框并继续在Firestore中配置他的帐户
但是Firestore文档上的所有请求都受安全规则保护,如
allow read: if request.auth.uid != null && request.auth.token.email != null && request.auth.token.email_verified == true;
失败
com.google.firebase.firestore.FirebaseFirestoreException:权限\被拒绝:权限缺失或不足
如果用户关闭应用程序,重新启动应用程序并重新验证,则其工作正常(Firestore请求的权限没有问题)
我做了几次测试。如果我将安全规则更改为
allow read: if request.auth.uid != null && request.auth.token.email != null;
一切正常,但从我的角度来看,它不太安全,因为没有保证电子邮件是经过验证的。Firestore似乎仍不知道该帐户已验证
以下是一项活动的示例:
package foo;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.os.Bundle;
import android.widget.Toast;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatActivity;
import com.firebase.ui.auth.AuthUI;
import com.google.android.gms.tasks.Task;
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import com.google.firebase.Timestamp;
import com.google.firebase.auth.FirebaseAuth;
import com.google.firebase.firestore.DocumentReference;
import com.google.firebase.firestore.DocumentSnapshot;
import com.google.firebase.firestore.FirebaseFirestore;
import com.google.firebase.firestore.QuerySnapshot;
import com.google.firebase.firestore.Source;
import com.google.firebase.functions.FirebaseFunctions;
import com.google.firebase.functions.HttpsCallableReference;
import com.google.firebase.functions.HttpsCallableResult;
import java.util.Collections;
import java.util.concurrent.TimeUnit;
import fr.cinfo.planartis.R;
public class ExampleActivity extends AppCompatActivity {
private static final int SIGN_IN_REQUEST_CODE = 123;
private static final String VERIFICATION_EMAIL_SENT_TIMESTAMP_KEY = "verificationEmailSentTimestamp";
private FirebaseAuth firebaseAuth;
private FirebaseFirestore firestore;
@Override
protected void onCreate(final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
firebaseAuth = FirebaseAuth.getInstance();
firestore = FirebaseFirestore.getInstance();
if (firebaseAuth.getCurrentUser() == null) {
startUserSignInActivity();
} else {
performEmailVerification();
}
}
void startUserSignInActivity() {
startActivityForResult(
AuthUI.getInstance()
.createSignInIntentBuilder()
.setAvailableProviders(Collections.singletonList(new AuthUI.IdpConfig.EmailBuilder().build()))
.setIsSmartLockEnabled(false, true)
.setTosAndPrivacyPolicyUrls("https://localhost/terms.html", "https://localhost/privacy.html")
.build(),
SIGN_IN_REQUEST_CODE);
}
@Override
protected void onActivityResult(final int requestCode, final int resultCode, @Nullable final Intent data) {
if (requestCode == SIGN_IN_REQUEST_CODE) {
if (resultCode != RESULT_OK) { // ERRORS
// ... do something
finish();
return;
}
performEmailVerification();
} else {
super.onActivityResult(requestCode, resultCode, data);
}
}
void performEmailVerification() {
if (firebaseAuth.getCurrentUser().isEmailVerified()) {
// Everything is OK
checkSomethingOnFirestore();
return;
}
final DocumentReference documentReference = firestore.document("users/" + firebaseAuth.getCurrentUser().getEmail());
documentReference.get(Source.DEFAULT).addOnCompleteListener((Task<DocumentSnapshot> task) -> {
if (task.isSuccessful()) {
final DocumentSnapshot userSnapshot = task.getResult();
if (userSnapshot.exists()) {
// Check if the first verification email was sent
final Object timestamp = userSnapshot.get(VERIFICATION_EMAIL_SENT_TIMESTAMP_KEY);
if (timestamp == null) {
firebaseAuth.getCurrentUser().sendEmailVerification().addOnCompleteListener((Task<Void> validationEmailTask) -> {
if (validationEmailTask.isSuccessful()) {
final Timestamp now = Timestamp.now();
documentReference.update(VERIFICATION_EMAIL_SENT_TIMESTAMP_KEY, now).addOnCompleteListener((Task<Void> updateUserAccountTask) -> {
if (!updateUserAccountTask.isSuccessful()) {
}
displayWarningAboutValidation();
});
}
else {
// email not sent, so display message
FirebaseAuth.getInstance().signOut();
displayInformationDialog(this, "Unable to send a verification email", null).show();
finish();
}
});
}
else {
displayWarningAboutValidation();
}
}
else {
Toast.makeText(this, "We are finalizing your account creation\nPlease wait a few seconds", Toast.LENGTH_LONG).show();
final HttpsCallableReference httpsCallableReference = FirebaseFunctions.getInstance().getHttpsCallable("finalizeUserAccount");
httpsCallableReference.setTimeout(15, TimeUnit.SECONDS);
httpsCallableReference.call(firebaseAuth.getCurrentUser().getEmail()).continueWith((Task<HttpsCallableResult> task12) -> {
if (!task12.isSuccessful()) {
displayInformationDialog(this, "The finalization of your account failed.", (DialogInterface dialog, int id) -> {
FirebaseAuth.getInstance().signOut();
finish();
}).show();
return null;
}
displayInformationDialog(this, "A new verification email was sent", (final DialogInterface dialog, final int id) -> {
// Reload current user
firebaseAuth.getCurrentUser().reload().addOnCompleteListener((Task<Void> task1) -> {
if (task1.isSuccessful()) {
performEmailVerification();
}
else {
FirebaseAuth.getInstance().signOut();
finish();
}
});
});
return null;
});
}
}
else {
displayInformationDialog(this, "Problem with the server", (DialogInterface dialog, int id) -> {
FirebaseAuth.getInstance().signOut();
finish();
}).show();
}
});
}
private void checkSomethingOnFirestore() {
firestore.collection("users").document(firebaseAuth.getCurrentUser().getEmail()).collection("documents").get().addOnCompleteListener(this, (Task<QuerySnapshot> task) -> {
if (!task.isSuccessful()) { // <======================================== PERMISSION_DENIED exception !!!!!!
displayInformationDialog(this, "Problem on Firestore", (final DialogInterface dialog, final int id) -> {
FirebaseAuth.getInstance().signOut();
finish();
})
.show();
return;
}
// Go on and do the job: for instance display the GUI or anything else
});
}
private void displayWarningAboutValidation() {
new AlertDialog.Builder(this)
.setCancelable(false)
.setMessage("Read the verification email we sent and click on the link inside the email")
.setPositiveButton("I understand", (DialogInterface dialog, int id) -> firebaseAuth.getCurrentUser().reload().addOnCompleteListener((Task<Void> task) -> {
if (task.isSuccessful()) {
performEmailVerification();
}
else {
FirebaseAuth.getInstance().signOut();
finish();
}
}))
.setNeutralButton("Send back a verification email", (final DialogInterface dialog, final int which) -> firebaseAuth.getCurrentUser().sendEmailVerification().addOnCompleteListener((final Task<Void> task) -> {
dialog.dismiss();
if (task.isSuccessful()) {
// email sent
displayInformationDialog(this, "A new verification email was sent", (final DialogInterface dialog12, final int which12) -> {
FirebaseAuth.getInstance().signOut();
finish();
}).show();
firebaseAuth.getCurrentUser().reload().addOnCompleteListener((Task<Void> task1) -> {
if (task1.isSuccessful()) {
performEmailVerification();
}
else {
FirebaseAuth.getInstance().signOut();
finish();
}
});
}
else {
// email not sent, so display message
displayInformationDialog(this, "Unable to send a new verification email", (final DialogInterface dialog1, final int which1) -> {
FirebaseAuth.getInstance().signOut();
finish();
}).show();
}
}))
.show();
}
private AlertDialog displayInformationDialog(final Context context, final CharSequence message, final DialogInterface.OnClickListener positiveButtonOnclickListener) {
return new MaterialAlertDialogBuilder(context).setCancelable(false).setMessage(message).setPositiveButton("I understand", positiveButtonOnclickListener).setTitle("Planartis").setIcon(R.drawable.ic_logo_toolbar).show();
}
}
package-foo;
导入android.content.Context;
导入android.content.DialogInterface;
导入android.content.Intent;
导入android.os.Bundle;
导入android.widget.Toast;
导入androidx.annotation.Nullable;
导入androidx.appcompat.app.AlertDialog;
导入androidx.appcompat.app.appcompat活动;
导入com.firebase.ui.auth.AuthUI;
导入com.google.android.gms.tasks.Task;
导入com.google.android.material.dialog.materialAllertDialogBuilder;
导入com.google.firebase.Timestamp;
导入com.google.firebase.auth.FirebaseAuth;
导入com.google.firebase.firestore.DocumentReference;
导入com.google.firebase.firestore.DocumentSnapshot;
导入com.google.firebase.firestore.FirebaseFirestore;
导入com.google.firebase.firestore.QuerySnapshot;
导入com.google.firebase.firestore.Source;
导入com.google.firebase.functions.FirebaseFunctions;
导入com.google.firebase.functions.httpscalableReference;
导入com.google.firebase.functions.httpscalableresult;
导入java.util.Collections;
导入java.util.concurrent.TimeUnit;
进口fr.cinfo.PLANATIS.R;
公共类ExampleActivity扩展了AppCompativity{
请求代码中的私有静态最终整数符号=123;
私有静态最终字符串验证\u电子邮件\u发送\u时间戳\u KEY=“verificationEmailSentTimestamp”;
私有FirebaseAuth FirebaseAuth;
私人FirebaseFirestore firestore;
@凌驾
创建时受保护的void(最终捆绑包savedInstanceState){
super.onCreate(savedInstanceState);
firebaseAuth=firebaseAuth.getInstance();
firestore=FirebaseFirestore.getInstance();
如果(firebaseAuth.getCurrentUser()==null){
startusersignitivity();
}否则{
performEmailVerification();
}
}
void startuservignitivity(){
startActivityForResult(
AuthUI.getInstance()
.CreateSignInEntBuilder()
.setAvailableProviders(Collections.singletonList(新的AuthUI.IdpConfig.EmailBuilder().build())
.setIsSmartLockEnabled(假、真)
.setTosAndPrivacyPolicyUrls(“https://localhost/terms.html", "https://localhost/privacy.html")
.build(),
在请求代码中签名;
}
@凌驾
受保护的void onActivityResult(最终int请求代码、最终int结果代码、@Nullable最终意图数据){
if(requestCode==在请求代码中签名){
如果(resultCode!=RESULT\u OK){//错误
//…做点什么
完成();
返回;
}
performEmailVerification();
}否则{
super.onActivityResult(请求代码、结果代码、数据);
}
}
作废PerformMailVerification(){
if(firebaseAuth.getCurrentUser().isEmailVerified()){
//一切都好
checkSomethingOnFirestore();
返回;
}
final DocumentReference=firestore.document(“users/”+firebaseAuth.getCurrentUser().getEmail());
documentReference.get(Source.DEFAULT).addOnCompleteListener((任务)->{
if(task.issusccessful()){
final DocumentSnapshot userSnapshot=task.getResult();
if(userSnapshot.exists()){
//检查是否发送了第一封验证电子邮件
最终对象时间戳=userSnapshot.get(验证\电子邮件\发送\时间戳\密钥);
if(时间戳==null){
firebaseAuth.getCurrentUser().sendEmailVerification().addOnCompleteListener((任务验证邮件任务)->{
if(validationEmailTask.isSuccessful()){
final Timestamp now=Timestamp.now();
documentReference.update(验证\电子邮件\发送\时间戳\密钥,现在)。addOnCompleteListener((任务updateUserAccountTask)->{
如果(!updateUserAccountTask.isSuccessful()){
}
显示警告BoutValidation();
});
}
否则{
//电子邮件未发送,因此显示消息
FirebaseAuth.getInstance().signOut();
displayInformationDialog(此“无法发送验证电子邮件”,null).show();
完成();
}
});
}
否则{
显示警告BoutValidation();
}
}
否则{
Toast.makeText(这是“我们正在完成您的帐户创建\n区域
void performEmailVerification() {
if (firebaseAuth.getCurrentUser().isEmailVerified()) {
// Everything is OK, perform your task
checkSomethingOnFirestore();
return;
}
//User not verified, but you still get a database reference and try to get the email.
final DocumentReference documentReference = ... //Error produced
FirebaseAuth.getInstance().getCurrentUser().reload();