在浏览代码之前,让我简要说明结构:
用户表
Users
|-- user uid (e.g., Wqnxj20ZosOTkfe4D7dwQc9iygp2)
| |-- userBusiness1Id : (e.g., 10553)
| |-- userBusiness2Id : (e.g., 10552)
| |-- userUid : (e.g., Wqnxj20ZosOTkfe4D7dwQc9iygp2)
|
|-- user uid (e.g., v6HBc00RL1RQdysCsc09mhKdItD2)
|-- userUid : (e.g., v6HBc00RL1RQdysCsc09mhKdItD2)
用户 - 消息表
描述: 仅在标准用户和业务配置文件之间进行消息传递。为了区分谁在发送消息,我使用以下格式:
对于标准用户:UID
let businessUserId = "Wqnxj20ZosOTkfe4D7dwQc9iygp2_10553"
let standardUserId = "v6HBc00RL1RQdysCsc09mhKdItD2"
User-Messages
|-- businessUserId
| |-- standardUserId
| |-- chatLogId
| |-- fromId: standardUserId
| |-- toId: businessUserId
|
|-- standardUserId
|-- businessUserId
|-- chatLogId
|-- fromId: standardUserId
|-- toId: businessUserId
Firebase写代码:
func sendMessage(text: String, fromId: String, toId: String) {
let trimmedText = text.trimmingCharacters(in: .whitespacesAndNewlines)
if trimmedText.isEmpty { return }
let timeStamp = Int64(Date().timeIntervalSince1970)
let reference = Database.database().reference().child("User-Messages").child(fromId).child(toId).childByAutoId()
let toReference = Database.database().reference().child("User-Messages").child(toId).child(fromId).childByAutoId()
let chatModel = ChatModel(chatId: reference.key, text: trimmedText, fromId: fromId, toId: toId, timestamp: timeStamp, messageIsRead: true)
let chatModelForRecipient = ChatModel(chatId: toReference.key, text: trimmedText, fromId: fromId, toId: toId, timestamp: timeStamp, messageIsRead: false)
reference.setValue(chatModel.toDictionary())
toReference.setValue(chatModelForRecipient.toDictionary())
}
"User-Messages": {
"$userId": {
".read": "auth != null && (
auth.uid === $userId ||
auth.uid + '_' + root.child('Users').child(auth.uid).child('userBusiness1Id').val() === $userId ||
auth.uid + '_' + root.child('Users').child(auth.uid).child('userBusiness2Id').val() === $userId
)",
"$recipientId": {
".write": "auth != null && (
auth.uid === $userId || // Sender can write to their own path
auth.uid === $recipientId || // Sender can write to the recipient's path
auth.uid + '_' + root.child('Users').child(auth.uid).child('userBusiness1Id').val() === $userId || // Business1 sender
auth.uid + '_' + root.child('Users').child(auth.uid).child('userBusiness2Id').val() === $userId || // Business2 sender
auth.uid + '_' + root.child('Users').child(auth.uid).child('userBusiness1Id').val() === $recipientId || // Business1 recipient
auth.uid + '_' + root.child('Users').child(auth.uid).child('userBusiness2Id').val() === $recipientId // Business2 recipient
)"
}
}
},
我应该如何使用这种相对复杂的结构保护实时数据库? 我设法进步了。现在我限制了规则,因此用户只能阅读自己的消息(包括他们的业务配置文件),并发送消息将为用户和反聚会写作。仍然缺少的是,当发送者为业务(UID_BUSINSESID)时,使用当前规则,Firebase允许将其写入发送者的路径,而不是收件人的路径。 NOSQL数据库的合适结构不像数据库,这不大取决于数据的实际结构。相反,这取决于应用程序的用例,通常在应用程序开始时您不会完全知道。
i通常建议遵循最低特权的原则,这意味着您的安全规则应仅允许允许允许应用程序当前用例的最小访问。
{
"rules": {
".read": false,
".write": false
}
}
在编写任何代码之前,这意味着规则不允许任何访问。
现在您为第一个用例添加代码。在您的情况下,似乎正在添加一个儿童节点。 注:您的问题中的SWIFT代码实际上尚未编写任何数据库。我认为您想在新的
User-Messages
您一旦您将在上一步中设置的规则运行代码,这正是您想要看到的。
您在问题中的规则是:"User-Messages": {
".write": "auth != null",
".read": "auth != null"
}
下写下他们想要的任何东西。虽然允许添加新消息,但它比您的代码实际做的要广泛得多。因此,它不遵循至少特权的原则。对于您的规则,任何用户都可以向任何其他用户(可能不需要的)写任何消息。或更糟糕的是,他们可以通过为顶级节点写一个空值来删除所有用户之间的所有消息。即使这些是您想要在某个时候允许应用程序的有效用例,当前代码也不需要该级别的访问级别 - 因此,安全规则也不应允许。
如果我们建模您的代码在安全规则中所做的工作,我们最终得到了类似的内容:
User-Messages
我希望您能看到此模拟您的代码的近距离:这使用WildCard变量,以便用户现在只能写一条消息。
更多,因此,只有在其UID匹配的两个父键时才允许写入,我认为这是您想要的用例。
您通常还希望向安全规则添加一些data验证,例如消息的最大长度,也许谁可以向谁发送消息。 如上所述,此处的目标是确保安全规则仅允许您的代码当前所做和需要的事情,而不再允许。
一旦您完成所有这些,请在下一个用例中移动代码。