在 macos 应用沙盒中访问沙盒外文件需通过 apple 官方扩展权限机制,严格遵循三步:正确声明 info.plist 中的 entitlements(如 user-selected.read-write 和 app-scope bookmarks)、由用户通过 nsopenpanel 主动授权、生成并持久化存储安全书签至沙盒内目录,缺一不可。

在 macOS 应用沙盒中,访问沙盒外文件不能靠“绕过”或“提权”,而是通过 Apple 官方认可的扩展权限机制实现。核心是三步协同:正确声明 entitlements、触发用户授权、持久化安全书签。缺一不可。
必须配置的 Info.plist 权限键
沙盒应用默认只能读写自身容器目录(如 ~/Library/Containers/com.yourapp/Data)。要访问用户文档、桌面等位置,需在 Info.plist 中显式声明:
-
com.apple.security.files.user-selected.read-write:设为true,允许通过打开/保存对话框获取文件夹读写权 -
com.apple.security.files.bookmarks.app-scope:设为true,支持跨重启复用安全书签 - 若需照片库、通讯录等,还需对应添加
com.apple.security.assets.photos、com.apple.security.personal-information.contacts等
Xcode Capabilities 中勾选 “User Selected File Access” 会自动生成前两项,但建议手动检查 plist 文件确认存在且值为 YES。
用 NSOpenPanel 触发用户授权
不能硬编码路径(如 ~/Documents),必须由用户主动选择:
- 调用
NSOpenPanel,设置canChooseDirectories = true、resolvesAliases = true - 用户确认后,立即执行
url.startAccessingSecurityScopedResource() - 完成读写操作后,必须配对调用
url.stopAccessingSecurityScopedResource() - 若需长期访问(比如后台任务再次读取),立刻生成书签:
url.bookmarkData(options: .withSecurityScope)
安全书签必须存放在沙盒内
书签数据本身也是受沙盒保护的资源,存错位置会导致下次启动无法恢复:
- 存储路径只能是应用自身容器内的目录,例如:
FileManager.default.urls(for: .applicationSupportDirectory, in: .userDomainMask).first - 不要存到
/tmp、~/Desktop或任意全局路径,沙盒应用无权读取 - 恢复时使用
URL.resolvingBookmarkData(_:),并再次调用startAccessingSecurityScopedResource()才能真正访问
验证与排错关键点
常见失败不是权限没开,而是流程断在某个环节:
- 报错
Operation not permitted时,先运行ls -lOe ~/Documents,看是否含restricted字样——这是沙盒拦截,不是 chmod 问题 - 检查
/Library/Logs/TCC/tcc.log(需提前启用日志),搜索你的 Bundle ID,确认拒绝类型是kTCCServiceFileProviderDomain还是kTCCServiceSystemPolicyAllFiles - 修改 Info.plist 或 entitlements 后,必须重新签名:
codesign --force --deep --sign - /Applications/YourApp.app











