You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Firebase实时数据库PERMISSION_DENIED权限拒绝问题求助

Firebase实时数据库PERMISSION_DENIED权限拒绝问题求助

我现在正在开发一个聊天应用,使用Firebase实时数据库,但一直遇到PERMISSION_DENIED: Permission denied的错误。我已经花了6小时尝试各种数据库规则,还是没能解决问题!

当前我的数据库规则配置如下:

{
  "rules": {
    "chats": {
      "$chatId": {
        ".read": "auth != null",
        ".write": "auth != null"
      }
    }
  }
}

以下是我的聊天组件代码(ChatScreen):

import { database, auth } from '../../../firebaseConfig';
import { ref, onValue, push } from 'firebase/database';
import { useLocalSearchParams, useRouter } from 'expo-router';
import { MaterialIcons } from '@expo/vector-icons';
import { useNetInfo } from '@react-native-community/netinfo';
import { useUser } from '@clerk/clerk-expo'; 
import { signInWithCustomToken } from 'firebase/auth';
const ChatScreen = () => {
  const { user } = useUser(); // 获取Clerk用户
  const { buyerId, sellerId, shopData, sellerStatus } = useLocalSearchParams();
  const [messages, setMessages] = useState([]);
  const [newMessage, setNewMessage] = useState('');
  const router = useRouter();
  const netInfo = useNetInfo();
  const isConnected = netInfo?.isConnected;


  useEffect(() => {
    if (!user || !user.session) return; // 如果用户未认证则直接返回

    // 获取Clerk会话并获取token
    const getClerkJwtToken = async () => {
      try {
        // 直接访问Clerk会话
        const session = user.session; // 获取Clerk会话
        const token = session?.access_token; // 从会话中获取token
        console.log('Clerk user:', user);
        const clerkToken = await user.getToken();
        console.log('Clerk Token:', clerkToken);
        
        if (!token) {
          Alert.alert('Error', 'Failed to retrieve Clerk token');
          return;
        }

        // 使用token登录Firebase认证
        await signInWithCustomToken(auth, token); // 登录Firebase
        console.log("Firebase user authenticated with Clerk JWT");

      } catch (error) {
        console.error("Error linking Clerk with Firebase", error.message);
        Alert.alert('Error', 'Failed to authenticate with Firebase');
      }
    };

    getClerkJwtToken(); // 调用函数完成Firebase认证

    const chatId = `${buyerId}_${sellerId}`;
    const messagesRef = ref(database, `chats/${chatId}/messages`);

    const unsubscribe = onValue(messagesRef, (snapshot) => {
      const data = snapshot.val();
      const messagesData = data ? Object.values(data) : [];
      setMessages(messagesData);
    });

    return () => unsubscribe();
  }, [buyerId, sellerId, user]);

  const sendMessage = async () => {
    if (newMessage.trim() === '') {
      Alert.alert('Error', 'Message cannot be empty.');
      return;
    }
    if (!isConnected) {
      Alert.alert('Error', 'You are offline. Please check your connection.');
      return;
    }

    const chatId = `${buyerId}_${sellerId}`;
    const messagesRef = ref(database, `chats/${chatId}/messages`);

    try {
      // 使用Clerk的user.id作为发送者ID
      await push(messagesRef, {
        senderId: user.id, // 使用Clerk用户ID作为发送者ID
        text: newMessage,
        timestamp: new Date().toISOString(),
      });
      setNewMessage('');
    } catch (e) {
      console.error('Error sending message:', e.message);
      Alert.alert('Error', e.message);
    }
  };

  return (
    <KeyboardAvoidingView
      style={styles.container}
      behavior={Platform.OS === 'ios' ? 'padding' : undefined}
    >
      {/* 头部 */}
      <View style={styles.header}>
        <TouchableOpacity onPress={() => router.back()} style={styles.backButton}>
          <MaterialIcons name="arrow-back" size={24} color="#333" />
        </TouchableOpacity>
        <Image source={{ uri: shopData?.images?.profilePhoto }} style={styles.profilePic} />
        <View style={styles.headerTextContainer}>
          <Text style={styles.sellerName}>{shopData?.shopName}</Text>
          <View style={styles.statusContainer}>
            <View
              style={[styles.statusIndicator, { backgroundColor: sellerStatus === 'online' ? 'green' : 'gray' }]}
            />
            <Text style={styles.statusText}>
              {sellerStatus === 'online' ? 'Online' : 'Offline'}
            </Text>
          </View>
        </View>
      </View>

      {/* 消息列表 */}
      <FlatList
        data={messages}
        renderItem={({ item }) => (
          <View style={item.senderId === user.id ? styles.buyerMessage : styles.sellerMessage}>
            <Text style={styles.messageText}>{item.text}</Text>
          </View>
        )}
        keyExtractor={(item, index) => `${item.timestamp}-${index}`}
        contentContainerStyle={styles.messagesContainer}
      />

      {/* 输入框 */}
      <View style={styles.inputContainer}>
        <TextInput
          style={styles.input}
          value={newMessage}
          onChangeText={setNewMessage}
          placeholder="Type a message..."
          placeholderTextColor="#888"
        />
        <TouchableOpacity style={styles.sendButton} onPress={sendMessage}>
          <MaterialIcons name="send" size={24} color="#fff" />
        </TouchableOpacity>
      </View>
    </KeyboardAvoidingView>
  );
};

const styles = StyleSheet.create({
  ..
});

export default ChatScreen;

我用Clerk做用户认证系统,通过自定义token登录Firebase,日志里能看到Firebase user authenticated with Clerk JWT的输出,说明认证流程似乎是通的,但发送消息时还是触发权限拒绝错误。有没有大佬能帮我排查一下问题出在哪?

备注:内容来源于stack exchange,提问作者Mahmoud Hlal

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.14 15:05:26