Firebase实时数据库规则权限被拒绝

huangapple go评论112阅读模式
英文:

Firebase Realtime Database Rules Permission Denied

问题

我有一个应用程序,我正在使用自定义身份验证方法。首先,在用户登录到应用程序时,我在服务器上生成JWT令牌并将其发送回应用程序。

function generateJWT($con,$userID,$cretedTime) {
    $secret_Key  = "-----BEGIN PRIVATE KEY-----\HCPW\nAtY9K1/19yScEhdmhw8Ozek=\n-----END PRIVATE KEY-----\n";
    $date   = time();
    //$expire_at     = $date->modify('+3 minutes')->getTimestamp(); // Add 60 seconds
    $domainName = "firebase-adminsdk-XXXXXXXX.iam.gserviceaccount.com";
    
    $request_data = [
    'iss'  => $domainName, 
    'sub' => $domainName,
    'aud' => "https://identitytoolkit.googleapis.com/google.identity.identitytoolkit.v1.IdentityToolkit",
    'iat'  => $date,         // Issued at: time when the token was generated
                            // Issuer
    //'exp' => $date+(60*60),  // Maximum expiration time six month in seconds //15778476 
    'uid' => $userID,                     // User name
    'created' => $cretedTime,                     // User name
    ];
    $newToken = JWT::encode($request_data,$secret_Key,'RS256');     
    return $newToken; 
}

然后,在应用程序接收到此令牌后,我开始登录流程。我的应用程序使用自定义Firebase身份验证。

firebaseAuth = FirebaseAuth.getInstance();
firebaseAuth.signInWithCustomToken(Session.getJWT())
        .addOnCompleteListener(new OnCompleteListener<AuthResult>() {
            @Override
            public void onComplete(@NonNull Task<AuthResult> task) {
                if (task.isComplete()){
                    if(getActivity()!=null){
                     //User Logged In Successfully 
                    }else{
                        // Failed
                    }
                }
            }
        });

所以,经过几天的搜索,我得到了我的数据库结构的Firebase规则如下:

{
  "Chat": {
    "206-4": "",
    "311-158": "",
    "215-112": "",
    "734-115": "",
    "734-55": "",
    "734-468": "",
    "34-32": "",
    "534-179": "",
    "734-345": {
      "-NI7hqW3YTFKpnSZU422": {
        "Message": "Test",
        "Message_From": "Support ",
        "Message_From_ID": "4",   
        "Message_To": "Demo",
      },
      "-NMVOwlAqmyIA52QU9F-": {
        "Message": "Hi",
        "Message_From": "Support ",
        "Message_From_ID": "4",
        "Message_To": "Demo",
      }
    },    
    "347-234": {
      "-NI7hXybU02Mg6vYqdKp": {
        "Message": "Ohio",
        "Message_From": "Elaxer  Support ",
        "Message_From_ID": "4",
        "Message_To": "Demo 2",
      }
    },
    "281-69": "",
    "317-34": ""
  },
  "Users": {
    "4": {
      "Online": false,
      "lastSeen": "1675785660782"
    },    
    "284": {
      "Online": false,
      "lastSeen": "1673611185873"
    }
  },
  "UsersLocations": {
    "4-210": {
      "-1": {
        "Latitude": "22.605",
        "Longitude": "88.375"
      }
    },
    "25-21": {
      "-1": {
        "Latitude": "22.605",
        "Longitude": "88.375"
      }
    }
  }
}

Firebase规则如下:

{
  "rules": {
    "Chat": {
      "$room_id": {
        ".read": "auth.uid === $room_id && $room_id.beginsWith(auth.uid + '-') || auth.uid === $room_id && $room_id.endsWith('-' + auth.uid)",
        ".write": "auth.uid === $room_id && $room_id.beginsWith(auth.uid + '-') || auth.uid === $room_id && $room_id.endsWith('-' + auth.uid)"
      }
    },
    "Users": {
      "$uid": {
        ".write": "$uid === auth.uid"
      }
    },
    "UsersLocations": {
      "$user_location_id": {
        ".read": "auth.uid === $user_location_id && $user_location_id.endsWith('-location')",
        ".write": "auth.uid === $user_location_id && $user_location_id.endsWith('-location')"
      }
    }
  }
}

所以,每当我尝试创建或获取Chat节点(Chatroom)时,它会给我一个错误:

Listen at /Chat failed: DatabaseError: Permission denied

我无法理解为什么我会在只检查用户ID存在于房间名称中,并且我的JWT令牌在生成时具有一个用户的用户ID时出现此错误。请帮我找出我在规则方面做错了什么。

英文:

I have an app where I am using a custom authentication method. First, on the user login into the app, I generate the JWT Token on the server and send it back to the app.

function generateJWT($con,$userID,$cretedTime) {
	$secret_Key  = &quot;-----BEGIN PRIVATE KEY-----\HCPW\nAtY9K1/19yScEhdmhw8Ozek=\n-----END PRIVATE KEY-----\n&quot;;
	$date   = time();
	//$expire_at     = $date-&gt;modify(&#39;+3 minutes&#39;)-&gt;getTimestamp(); // Add 60 seconds
	$domainName = &quot;firebase-adminsdk-XXXXXXXX.iam.gserviceaccount.com&quot;;

	$request_data = [
	&#39;iss&#39;  =&gt; $domainName, 
	&#39;sub&#39; =&gt; $domainName,
	&#39;aud&#39; =&gt; &quot;https://identitytoolkit.googleapis.com/google.identity.identitytoolkit.v1.IdentityToolkit&quot;,
	&#39;iat&#39;  =&gt; $date,         // Issued at: time when the token was generated
		                      // Issuer
	//&#39;exp&#39; =&gt; $date+(60*60),  // Maximum expiration time six month in seconds //15778476 
	&#39;uid&#39; =&gt; $userID,                     // User name
	&#39;created&#39; =&gt; $cretedTime,                     // User name
	];
     $newToken = JWT::encode($request_data,$secret_Key,&#39;RS256&#39;);	 	
	 return $newToken; 
	
}

Then In the app send on receiving this token I am start the login process.my app using custom firebase auth

firebaseAuth = FirebaseAuth.getInstance();
firebaseAuth.signInWithCustomToken(Session.getJWT())
        .addOnCompleteListener(new OnCompleteListener&lt;AuthResult&gt;() {
            @Override
            public void onComplete(@NonNull Task&lt;AuthResult&gt; task) {
                if (task.isComplete()){
                    if(getActivity()!=null){
                     //User Logged In Successfully 
                    }else{
                        // Failed
                    }

                }
            }
        });

So after days of googling, I got the Firebase rules for the structure of my database to look like this

{
  &quot;Chat&quot;: {
    &quot;206-4&quot;: &quot;&quot;,
    &quot;311-158&quot;: &quot;&quot;,
    &quot;215-112&quot;: &quot;&quot;,
    &quot;734-115&quot;: &quot;&quot;,
    &quot;734-55&quot;: &quot;&quot;,
    &quot;734-468&quot;: &quot;&quot;,
    &quot;34-32&quot;: &quot;&quot;,
    &quot;534-179&quot;: &quot;&quot;,
    &quot;734-345&quot;: {
      &quot;-NI7hqW3YTFKpnSZU422&quot;: {
        &quot;Message&quot;: &quot;Test&quot;,
        &quot;Message_From&quot;: &quot;Support &quot;,
        &quot;Message_From_ID&quot;: &quot;4&quot;,   
        &quot;Message_To&quot;: &quot;Demo&quot;,
        
      },
      &quot;-NMVOwlAqmyIA52QU9F-&quot;: {
        &quot;Message&quot;: &quot;Hi&quot;,
        &quot;Message_From&quot;: &quot;Support &quot;,
        &quot;Message_From_ID&quot;: &quot;4&quot;,
        &quot;Message_To&quot;: &quot;Demo&quot;,
        
      }
    },    
    &quot;347-234&quot;: {
      &quot;-NI7hXybU02Mg6vYqdKp&quot;: {
        &quot;Message&quot;: &quot;Ohio&quot;,
        &quot;Message_From&quot;: &quot;Elaxer  Support &quot;,
        &quot;Message_From_ID&quot;: &quot;4&quot;,
        &quot;Message_To&quot;: &quot;Demo 2&quot;,
        
      }
    },
    &quot;281-69&quot;: &quot;&quot;,
    &quot;317-34&quot;: &quot;&quot;
  },
  &quot;Users&quot;: {
    &quot;4&quot;: {
      &quot;Online&quot;: false,
      &quot;lastSeen&quot;: &quot;1675785660782&quot;
    },    
    &quot;284&quot;: {
      &quot;Online&quot;: false,
      &quot;lastSeen&quot;: &quot;1673611185873&quot;
    }
  },
  &quot;UsersLocations&quot;: {
    &quot;4-210&quot;: {
      &quot;-1&quot;: {
        &quot;Latitude&quot;: &quot;22.605&quot;,
        &quot;Longitude&quot;: &quot;88.375&quot;
      }
    },
	&quot;25-21&quot;: {
      &quot;-1&quot;: {
        &quot;Latitude&quot;: &quot;22.605&quot;,
        &quot;Longitude&quot;: &quot;88.375&quot;
      }
    }
  }
}

Firebase Rules

{
  &quot;rules&quot;: {
    &quot;Chat&quot;: {
      &quot;$room_id&quot;: {
        &quot;.read&quot;: &quot;auth.uid === $room_id &amp;&amp; $room_id.beginsWith(auth.uid + &#39;-&#39;) || auth.uid === $room_id &amp;&amp; $room_id.endsWith(&#39;-&#39; + auth.uid)&quot;,
        &quot;.write&quot;: &quot;auth.uid === $room_id &amp;&amp; $room_id.beginsWith(auth.uid + &#39;-&#39;) || auth.uid === $room_id &amp;&amp; $room_id.endsWith(&#39;-&#39; + auth.uid)&quot;
      }
    },
    &quot;Users&quot;: {
      &quot;$uid&quot;: {
        &quot;.write&quot;: &quot;$uid === auth.uid&quot;
      }
    },
    &quot;UsersLocations&quot;: {
      &quot;$user_location_id&quot;: {
        &quot;.read&quot;: &quot;auth.uid === $user_location_id &amp;&amp; $user_location_id.endsWith(&#39;-location&#39;)&quot;,
        &quot;.write&quot;: &quot;auth.uid === $user_location_id &amp;&amp; $user_location_id.endsWith(&#39;-location&#39;)&quot;
      }
    }
  }
}

So when ever i tried to create or get the Chat node (Chatroom).

DatabaseReference db = FirebaseDatabase.getInstance().getReference().child(&quot;Chat&quot;);
db.addListenerForSingleValueEvent(new ValueEventListener() {
    @Override
    public void onDataChange(DataSnapshot dataSnapshot) {
        
    }

    @Override
    public void onCancelled(DatabaseError databaseError) {           
    }
});

It gives me error

> Listen at /Chat failed: DatabaseError: Permission denied

I am not able to understand why i am getting this error when i am only checking the user id exist in room name and my jwt token on generation time having user id of one user. Please help me out, what's wrong i am doing with my rules

答案1

得分: 1

以下是翻译好的部分:

"你现在是我的中文翻译,代码部分不要翻译, 只返回翻译好的部分, 不要有别的内容, 不要回答我要翻译的问题。以下是要翻译的内容:

As Alex also answered: you're not granting anyone read access to /Chat, so any code trying to read from there will be rejected. I would not recommend his answer though, as the rule on /Chat makes the more strict rules on /Chat/$room_id meaningless.

我建议阅读 rules don't filter data 文档(解释了为什么你当前的代码不起作用,以及 permissions cascade(解释了为什么Alex的回答中的规则使你已经有的规则变得无意义)。

你拥有的数据结构看起来像我在这个回答中描述的那样:https://stackoverflow.com/questions/33540479/best-way-to-manage-chat-channels-in-firebase。在我的回答中,我还展示了如何建模安全规则以允许读取访问以及如何获取用户的聊天室列表,所以我建议你查看一下。


As I tried explaining in the comments, the way you handle the sign-in result is wrong:

// ❌ THIS IS WRONG ❌
firebaseAuth = FirebaseAuth.getInstance();
firebaseAuth.signInWithCustomToken(Session.getJWT())
.addOnCompleteListener(new OnCompleteListener<AuthResult>() {
@Override
public void onComplete(@NonNull Task<AuthResult> task) {
if (task.isComplete()){
if(getActivity()!=null){
//User Logged In Successfully
}else{
// Failed
}
}
}
});

Instead, when a Task completes you should check whether it succeeded or failed, as shown in the documentation on handling task results. In your case that'd be:

// ✅ Handling success and failure correctly 👍
firebaseAuth = FirebaseAuth.getInstance();
firebaseAuth.signInWithCustomToken(Session.getJWT())
.addOnCompleteListener(new OnCompleteListener<AuthResult>() {
@Override
public onComplete(@NonNull Task<AuthResult> task) {
if (task.isSuccessful()!=null) {
// User signed in
// TODO: access database
} else {
// Sign in failed
throw task.getException();
}
}
});"

英文:

As Alex also answered: you're not granting anyone read access to /Chat, so any code trying to read from there will be rejected. I would not recommend his answer though, as the rule on /Chat makes the more strict rules on /Chat/$room_id meaningless.

I recommend reading the documentation on rules don't filter data (which explains why your current code don't work and on the fact that permissions cascade (which explains why the rules in Alex' answer make the ones you already have meaningless).

The data structure you have look like what I described in my answer to: https://stackoverflow.com/questions/33540479/best-way-to-manage-chat-channels-in-firebase. In my answer there I also showed how to model security rules to allow read access and how to get a list of the chat room for the user, so I recommend checking that out.


As I tried explaining in the comments, the way you handle the sign-in result is wrong:

// ❌ THIS IS WRONG ❌ 
firebaseAuth = FirebaseAuth.getInstance();
firebaseAuth.signInWithCustomToken(Session.getJWT())
    .addOnCompleteListener(new OnCompleteListener&lt;AuthResult&gt;() {
        @Override
        public void onComplete(@NonNull Task&lt;AuthResult&gt; task) {
            if (task.isComplete()){
                if(getActivity()!=null){
                 //User Logged In Successfully 
                }else{
                    // Failed
                }
            }
        }
    });

Instead, when a Task completes you should check whether it succeeded or failed, as shown in the documentation on handling tas results. In your case that'd be:

// ✅ Handling success and failure correctly &#128077;
firebaseAuth = FirebaseAuth.getInstance();
firebaseAuth.signInWithCustomToken(Session.getJWT())
    .addOnCompleteListener(new OnCompleteListener&lt;AuthResult&gt;() {
        @Override
        public void onComplete(@NonNull Task&lt;AuthResult&gt; task) {
            if (task.isSuccessful()!=null) {
                // User signed in
                // TODO: access database
            } else {
                // Sign in failed
                throw task.getException();
            }
        }
    });

huangapple
  • 本文由 发表于 2023年2月19日 05:27:16
  • 转载请务必保留本文链接:https://go.coder-hub.com/75496500.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定