Firestore安全规则:Android请求中的“ request.auth.uid”为空

时间:2018-09-30 02:33:43

标签: android firebase google-cloud-firestore firebase-security-rules

环境

  • Android设备:Google Pixel 2
  • Android操作系统版本:9
  • Google Play服务版本:14.3.66(100400-213742215)
  • Firebase / Play服务SDK版本:16.0.1
  • FirebaseUI版本:ui-firestore:4.2.0,ui-auth:4.1.0。

问题

使用Firestore安全规则时,

request.auth.uid != null始终为 null

uid 为空时,Android客户端上的用户既可以通过Firebase UI登录,又可以在Firestore Authentication 控制台中显示。

enter image description here

实施

示例Firestore安全规则

service cloud.firestore {
function signedInOrPublic() {
  return request.auth.uid != null;
}

match /databases/{database}/documents {
  match /{document=**} {
    allow read;
  }
}

// Add to user collection.
match /databases/{database}/documents {
  match /users/{userId}/{collectionOrAction}/{content} {
    allow create: if signedInOrPublic();
  } 
}

Android调用Firestore集合

在没有设置任何严格规则的情况下,呼叫将按预期进行。

usersCollection.document(userId).collection(actionCollection)
.document(content.id)
.set(ContentAction(Date(), content.id, content.title, content.creator,
content.qualityScore)).addOnSuccessListener {
updateUserActionCounter(userId, countType)
}.addOnFailureListener {
Log.w(LOG_TAG, "User content action update FAIL.")
}

Firebase库

implementation 'com.firebase:firebase-client-android:2.5.2'
implementation 'com.google.firebase:firebase-core:16.0.3'
implementation 'com.google.firebase:firebase-firestore:17.1.0'
implementation 'com.firebaseui:firebase-ui-firestore:4.1.0'
implementation 'com.firebaseui:firebase-ui-auth:4.0.0'

google-services.json配置

我有一个 google-services.json 版本,用于调试和发布版本,以便为该应用的生产版本保留单独的Firestore项目/环境/分析。我想知道这是否会影响Firestore规则,即使此设置到目前为止对Firebase的其他每个部分(Firestore数据库,Analytics(分析)等)都可以按预期进行。

应用 src > 调试> google-services.json

应用> src > 发布> google-services.json

尝试的解决方案

  1. 我尝试检查Firestore Rules文件中的request.auth是否不为空。
  2. 我尝试在上面的规则中使用request.auth.uid == userId,将路径路径中的request.auth.uiduserId比较。
  3. 我尝试在失败的规则中使用request.auth.token.email == userEmail
  4. 即使我已经具有如上所示的Firebase UI身份验证库,也要添加其他Firebase身份验证库:implementation 'com.google.firebase:firebase-auth:16.0.4
  5. 添加implementation 'com.google.android.gms:play-services-auth:16.0.1'
  6. 将Firestore库升级到firebase-core:16.0.4firebase-firestore:17.1.1firebase-ui-firestore:4.2.0firebase-ui-auth:4.1.0
  7. 我构建了一个simple sample app,以查看安全性规则是否可以在简单的实现中使用。

示例应用

在示例应用程序中,有一个登录/退出按钮,该按钮可根据用户当前的登录状态对其进行签入和签出。登录状态显示在左上角。 获取数据按钮会根据Firestore安全规则从Firestore数据库检索字符串字段。

enter image description here

错误

2018-10-14 15:21:59.123 24192-24192/firebase_security_sample.adamhurwitz.firebasesecuritysample         
E/AndroidRuntime: FATAL EXCEPTION: main
Process: firebase_security_sample.adamhurwitz.firebasesecuritysample, PID: 24192
com.google.android.gms.tasks.RuntimeExecutionException: com.google.firebase.firestore.FirebaseFirestoreException: PERMISSION_DENIED: Missing or insufficient permissions.
    at com.google.android.gms.tasks.zzu.getResult(Unknown Source:15)
    at firebase_security_sample.adamhurwitz.firebasesecuritysample.MainActivity$onCreate$2$1.onComplete(MainActivity.kt:63)
    at com.google.android.gms.tasks.zzj.run(Unknown Source:4)
    at android.os.Handler.handleCallback(Handler.java:873)
    at android.os.Handler.dispatchMessage(Handler.java:99)
    at android.os.Looper.loop(Looper.java:193)
    at android.app.ActivityThread.main(ActivityThread.java:6669)
    at java.lang.reflect.Method.invoke(Native Method)
    at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:858)
 Caused by: com.google.firebase.firestore.FirebaseFirestoreException: PERMISSION_DENIED: Missing or insufficient permissions.
    at com.google.firebase.firestore.obfuscated.zzhc.zza(com.google.firebase:firebase-firestore@@17.1.1:119)
    at com.google.firebase.firestore.obfuscated.zzk.zza(com.google.firebase:firebase-firestore@@17.1.1:134)
    at com.google.firebase.firestore.obfuscated.zzak.zza(com.google.firebase:firebase-firestore@@17.1.1:384)
    at com.google.firebase.firestore.obfuscated.zzm.zza(com.google.firebase:firebase-firestore@@17.1.1:235)
    at com.google.firebase.firestore.obfuscated.zzfv.zza(com.google.firebase:firebase-firestore@@17.1.1:7521)
    at com.google.firebase.firestore.obfuscated.zzfv$1.zza(com.google.firebase:firebase-firestore@@17.1.1:172)
    at com.google.firebase.firestore.obfuscated.zzgc.zzb(com.google.firebase:firebase-firestore@@17.1.1:3109)
    at com.google.firebase.firestore.obfuscated.zzfh.run(com.google.firebase:firebase-firestore@@17.1.1:1117)
    at com.google.firebase.firestore.obfuscated.zzfc$zza.zza(com.google.firebase:firebase-firestore@@17.1.1:67)
    at com.google.firebase.firestore.obfuscated.zzfc$zzc.zza(com.google.firebase:firebase-firestore@@17.1.1:110)
    at com.google.firebase.firestore.obfuscated.zzgv$1.onMessage(com.google.firebase:firebase-firestore@@17.1.1:107)
    at io.grpc.ForwardingClientCallListener.onMessage(ForwardingClientCallListener.java:33)
    at io.grpc.ForwardingClientCallListener.onMessage(ForwardingClientCallListener.java:33)
    at io.grpc.internal.ClientCallImpl$ClientStreamListenerImpl$1MessagesAvailable.runInContext(ClientCallImpl.java:526)
    at io.grpc.internal.ContextRunnable.run(ContextRunnable.java:37)
    at io.grpc.internal.SerializingExecutor.run(SerializingExecutor.java:123)
    at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:458)
    at java.util.concurrent.FutureTask.run(FutureTask.java:266)
    at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:301)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
    at com.google.firebase.firestore.obfuscated.zzgf$zza.run(com.google.firebase:firebase-firestore@@17.1.1:203)
    at java.lang.Thread.run(Thread.java:764)
 Caused by: io.grpc.StatusException: PERMISSION_DENIED: Missing or insufficient permissions.
    at io.grpc.Status.asException(Status.java:534)
    at com.google.firebase.firestore.obfuscated.zzhc.zza(com.google.firebase:firebase-firestore@@17.1.1:117)
    at com.google.firebase.firestore.obfuscated.zzk.zza(com.google.firebase:firebase-firestore@@17.1.1:134) 
    at com.google.firebase.firestore.obfuscated.zzak.zza(com.google.firebase:firebase-firestore@@17.1.1:384) 
    at com.google.firebase.firestore.obfuscated.zzm.zza(com.google.firebase:firebase-firestore@@17.1.1:235) 
    at com.google.firebase.firestore.obfuscated.zzfv.zza(com.google.firebase:firebase-firestore@@17.1.1:7521) 
    at com.google.firebase.firestore.obfuscated.zzfv$1.zza(com.google.firebase:firebase-firestore@@17.1.1:172) 
    at com.google.firebase.firestore.obfuscated.zzgc.zzb(com.google.firebase:firebase-firestore@@17.1.1:3109) 
    at com.google.firebase.firestore.obfuscated.zzfh.run(com.google.firebase:firebase-firestore@@17.1.1:1117) 
    at com.google.firebase.firestore.obfuscated.zzfc$zza.zza(com.google.firebase:firebase-firestore@@17.1.1:67) 
    at com.google.firebase.firestore.obfuscated.zzfc$zzc.zza(com.google.firebase:firebase-firestore@@17.1.1:110) 
    at com.google.firebase.firestore.obfuscated.zzgv$1.onMessage(com.google.firebase:firebase-firestore@@17.1.1:107) 
    at io.grpc.ForwardingClientCallListener.onMessage(ForwardingClientCallListener.java:33) 
    at io.grpc.ForwardingClientCallListener.onMessage(ForwardingClientCallListener.java:33) 
    at io.grpc.internal.ClientCallImpl$ClientStreamListenerImpl$1MessagesAvailable.runInContext(ClientCallImpl.java:526) 
    at io.grpc.internal.ContextRunnable.run(ContextRunnable.java:37) 
    at io.grpc.internal.SerializingExecutor.run(SerializingExecutor.java:123) 
    at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:458) 
    at java.util.concurrent.FutureTask.run(FutureTask.java:266) 
    at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:301) 
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167) 
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641) 
    at com.google.firebase.firestore.obfuscated.zzgf$zza.run(com.google.firebase:firebase-firestore@@17.1.1:203) 
    at java.lang.Thread.run(Thread.java:764) 

Firebase安全规则

service cloud.firestore {

    match /databases/{database}/documents {
        match /testCollection/testDoc {
            allow read: if request.auth.uid != null
        }
    }
}

build.gradle (应用程序)

buildscript {
ext.kotlin_version = '1.2.71'
repositories {
    google()
    jcenter()
}
dependencies {
    classpath 'com.android.tools.build:gradle:3.2.1'
    classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
    classpath 'com.google.gms:google-services:4.1.0'

    // NOTE: Do not place your application dependencies here; they belong
    // in the individual module build.gradle files
}
}

allprojects {
repositories {
    google()
    jcenter()
}
}

task clean(type: Delete) {
delete rootProject.buildDir
}

build.gradle (项目)

apply plugin: 'com.android.application'

apply plugin: 'kotlin-android'

apply plugin: 'kotlin-android-extensions'

android {
    compileSdkVersion 28
    defaultConfig {
       applicationId "firebase_security_sample.adamhurwitz.firebasesecuritysample"
    minSdkVersion 21
    targetSdkVersion 28
    versionCode 1
    versionName "1.0"
    testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
buildTypes {
    release {
        minifyEnabled false
        proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
    }
}
}

dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation"org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
    implementation 'com.android.support:appcompat-v7:28.0.0'
    implementation 'com.android.support.constraint:constraint-layout:1.1.3'

    implementation 'com.google.android.gms:play-services-auth:16.0.1'
    implementation 'com.google.firebase:firebase-core:16.0.4'
    implementation 'com.google.firebase:firebase-auth:16.0.4'
    implementation 'com.google.firebase:firebase-firestore:17.1.1'
    implementation 'com.firebaseui:firebase-ui-firestore:4.2.0'
    implementation 'com.firebaseui:firebase-ui-auth:4.1.0'

    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'com.android.support.test:runner:1.0.2'
    androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
}

apply plugin: 'com.google.gms.google-services'

MainActivity

class MainActivity : AppCompatActivity() {

lateinit var signInStatus: TextView

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)
    FirebaseApp.initializeApp(
            this,
            FirebaseOptions.Builder()
                    .setApplicationId("1:63924060965:android:c387085babd1a8a4") // Required for Analytics.
                    .setApiKey("AIzaSyCi4h6WBX495xmzaRsLYro2_Vd9UcB3bpg") // Required for Auth.
                    .setDatabaseUrl("https://security-rules-sample.firebaseio.com") // Required for RTDB.
                    .setProjectId("security-rules-sample")
                    .build(),
            "firestoreSecuritySample")
    signInStatus = findViewById(R.id.signInStatus) as TextView

    val user = FirebaseAuth.getInstance().currentUser
    if (user != null) {
        signInStatus.text = user.displayName
    } else {
        signInStatus.text = "logged out"
    }

    signInButton.setOnClickListener {
        if (FirebaseAuth.getInstance().currentUser == null) {
            val providers = Arrays.asList<AuthUI.IdpConfig>(
                    AuthUI.IdpConfig.GoogleBuilder().build())

            startActivityForResult(
                    AuthUI.getInstance()
                            .createSignInIntentBuilder()
                            .setAvailableProviders(providers)
                            .build(),
                    123)
        } else {
            AuthUI.getInstance().signOut(this).addOnCompleteListener {
                signInStatus.text = "logged out"
            }
        }
    }

    getDataButton.setOnClickListener {
        FirebaseFirestore.getInstance(FirebaseApp.getInstance("firestoreSecuritySample"))
                .collection("testCollection").document("testDoc").get().addOnCompleteListener {
                    if (FirebaseAuth.getInstance().currentUser != null) {
                        firestoreResult.text = it.result!!.get("testField").toString()
                    }
                }
    }
}

override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
    super.onActivityResult(requestCode, resultCode, data)
    if (requestCode == 123) {
        if (resultCode == Activity.RESULT_OK) {
            val user = FirebaseAuth.getInstance().currentUser
            if (user != null) {
                signInStatus.text = user.displayName
            }
        }
    }

}

1 个答案:

答案 0 :(得分:1)

感谢Google开发者关系小组的Sam Stern帮助调试!我已经针对上面创建的示例应用以及生产中的应用测试了以下修复程序。

  

我很确定您的问题来自此行:

     

FirebaseApp.getInstance(“ firestoreSecuritySample”)   您正在使用自定义的FirebaseApp与数据库进行通信,但登录时并未使用相同的FirebaseApp。每个应用程序实例都有单独的身份验证状态。

     

尝试更改登录代码,以将应用传递给getInstance:

        startActivityForResult(
                // SEE BELOW FOR CHANGE
                > AuthUI.getInstance(FirebaseApp.getInstance("firestoreSecuritySample"))
                        .createSignInIntentBuilder()
                        .setAvailableProviders(providers)
                        .build(),
                123)