【问题标题】:Firestore Security Rules: 'request.auth.uid' Is Null With Android RequestFirestore 安全规则:“request.auth.uid”在 Android 请求中为空
【发布时间】:2019-03-05 13:28:37
【问题描述】:

环境

  • 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。

问题

request.auth.uid != null 在使用 Firestore 安全规则时始终为 null

uid 为 null 时,Android 客户端上的用户通过 Firebase UI 登录并显示在 Firestore Authentication 控制台中。

实施

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 db、Analytics 等)都按预期工作。

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

app>src>发布>google-services.json

尝试的解决方案

  1. 我尝试检查 Firestore 规则文件中的 request.auth 是否不为空。
  2. 我尝试在上述规则中使用request.auth.uid == userId,比较路径中的request.auth.uiduserId
  3. 我尝试在失败的规则中使用request.auth.token.email == userEmail
  4. 添加额外的 Firebase Auth 库,即使我已经拥有如上所示的 Firebase UI Auth 库: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 数据库中检索字符串字段。

错误

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'

主要活动

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
            }
        }
    }

}

【问题讨论】:

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


    【解决方案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)
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2018-05-28
      • 2018-10-17
      • 2018-10-01
      • 1970-01-01
      • 2021-07-29
      • 2018-08-04
      • 2019-11-18
      • 1970-01-01
      相关资源
      最近更新 更多