iOS Realm+MongoDB Atlas Flex Sync写入权限错误求助
问题原因与解决办法
核心问题:数据库规则配置逻辑错误
你的Realm写入权限错误,根源是MongoDB Atlas的规则配置混淆了用户身份校验和文档范围过滤的逻辑:
apply_when字段是用来判断当前用户是否具备该角色的资格,而非限制文档的归属document_filters字段才是用来限定用户能读写的文档范围
你当前的规则中,apply_when里的"accountId": "%%user.id"是在检查用户对象(而非文档)中是否存在accountId字段且等于用户ID,这不符合匿名登录用户的身份结构,导致角色无法正确匹配;同时document_filters设为true意味着允许读写所有文档,这和你实际要实现的"用户只能读写自己的Venue"逻辑冲突,最终触发权限拦截。
解决步骤
1. 修正数据库规则配置
将Atlas的规则替换为以下内容,确保逻辑正确:
{ "roles": [ { "name": "readAndWriteOwnVenues", "apply_when": { "%%user.id": {"$exists": true} }, "document_filters": { "write": {"accountId": "%%user.id"}, "read": {"accountId": "%%user.id"} }, "read": true, "write": true, "insert": true, "delete": true, "search": true } ] }
apply_when: 仅当用户已登录(存在用户ID)时,才赋予该角色document_filters: 限制用户只能读写accountId字段等于自身用户ID的文档,和你的Venue模型字段对应
2. 调整Flexible Sync订阅查询
当前的全量订阅QuerySubscription<Venue>会同步所有Venue文档,这既不符合权限规则,也浪费资源。修改订阅逻辑,只同步当前用户的Venue:
@MainActor func initialize() async throws { user = try await app.login(credentials: Credentials.anonymous) // 先获取用户的accountId,用于订阅过滤 guard let account = try await getUserAccountById() else { throw NSError(domain: "RealmManager", code: -2, userInfo: [NSLocalizedDescriptionKey: "User account not found"]) } let configuration = user?.flexibleSyncConfiguration(initialSubscriptions: { subs in if subs.first(named: "userAccounts") == nil { subs.append(QuerySubscription<UserAccount>(name: "userAccounts")) } if subs.first(named: "venues") == nil { // 订阅当前用户的Venue文档 let venueQuery = NSPredicate(format: "accountId == %@", account._id) subs.append(QuerySubscription<Venue>(name: "venues", query: venueQuery)) } }, rerunOnOpen: true) realm = try await Realm(configuration: configuration!, downloadBeforeOpen: .always) }
3. 验证写入逻辑
确认createVenue方法中,venue.accountId确实被设置为当前用户的account._id,且写入操作在正确的Realm实例中执行。你的现有代码这部分逻辑是正确的,但可以添加日志验证account._id和venue.accountId的一致性。
额外说明
- 移除规则后能写入是因为此时没有权限校验,所有操作都被允许,但这在生产环境中存在严重安全风险
- 错误码
231是Realm Flexible Sync的权限拦截错误,通常由规则不匹配或订阅范围与规则冲突导致
内容的提问来源于stack exchange,提问作者Etienne
相关产品推荐
相关产品推荐

