macOS SwiftUI打开新窗口时如何验证深层链接 区分内外来源?
解决方案
方案1:新增内部链接标识+全局状态控制(区分来源首选)
应用内部触发URL跳转时,通过全局标志位标记当前操作是内部发起的,处理完成后立即重置标志位,即可在onOpenURL中准确区分链接来源,同时统一加入验证逻辑:
- 定义全局共享的状态管理类
class AppGlobalState: ObservableObject { static let shared = AppGlobalState() // 标记是否为应用内部发起的URL跳转 var isInternalURLLaunch: Bool = false }
- 内部跳转时先修改标志位再打开链接
// 内部触发打开书籍详情的逻辑 func openBookInternally(bookId: String) { guard let url = URL(string: "fancyLibraryApp:viewer/book/\(bookId)") else { return } AppGlobalState.shared.isInternalURLLaunch = true NSWorkspace.shared.open(url) }
onOpenURL中先做验证再更新视图,不要直接赋值ID
@State private var currentBookId: String? @ObservedObject private var globalState = AppGlobalState.shared var body: some Scene { Group { if let bookId = currentBookId, let book = getBookById(bookId) { Text("Title of \(book.name)") } else { Text("无效链接或无访问权限") } } .onOpenURL { url in // 先取来源标识,再立即重置避免影响后续请求 let isFromInternal = globalState.isInternalURLLaunch defer { globalState.isInternalURLLaunch = false } // 通用校验:所有链接必须经过的检查 guard url.scheme == "fancyLibraryApp", url.pathComponents.count >= 3, url.pathComponents[1] == "viewer", url.pathComponents[2] == "book", let bookId = url.pathComponents.last, // 登录校验 UserAuthManager.shared.isLogin, // 权限校验 PermissionManager.shared.checkBookAccess(bookId: bookId) else { // 校验不通过直接忽略,或跳转错误提示 currentBookId = nil return } // 可根据来源做差异化处理 if isFromInternal { // 内部链接可跳过部分额外安全校验 } else { // 外部链接额外做恶意参数检测、防注入等校验 } // 所有校验通过再更新ID触发视图刷新 currentBookId = bookId } }
方案2:弃用onOpenURL,完全走NSApplicationDelegate处理(控制处理时机首选)
如果你需要完全控制URL的处理时机,避免onOpenURL调用过早的问题,可以直接弃用视图层的onOpenURL修饰符,统一在AppDelegate中处理所有URL,验证通过后再通知视图更新:
- 实现AppDelegate代理方法
class AppDelegate: NSObject, NSApplicationDelegate { func application(_ application: NSApplication, open urls: [URL]) { urls.forEach { url in // 此处先完成所有验证逻辑:登录校验、权限校验、恶意参数校验 guard let validatedBookId = validateURLAndGetBookId(url) else { return } // 验证通过后发通知携带书籍ID NotificationCenter.default.post( name: NSNotification.Name("ValidBookOpenRequest"), object: validatedBookId ) } } private func validateURLAndGetBookId(_ url: URL) -> String? { // 实现你的校验逻辑,校验通过返回书籍ID,不通过返回nil } }
- 视图层监听通知更新即可
@State private var currentBookId: String? var body: some Scene { Text("Title of \(book)") .onReceive(NotificationCenter.default.publisher(for: NSNotification.Name("ValidBookOpenRequest"))) { notify in guard let bookId = notify.object as? String else { return } currentBookId = bookId } }
补充说明
如果不想用全局标志位,也可以在内部生成的URL中加入只有应用内部知道的单次有效签名参数,比如fancyLibraryApp:viewer/book/42?inner_sign=xxxx,处理URL时校验参数是否存在且合法,即可判断是否为内部来源,该方案也适用于跨进程的内部联动场景。
内容的提问来源于stack exchange,提问作者user965972
相关产品推荐
相关产品推荐

