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

macOS SwiftUI打开新窗口时如何验证深层链接 区分内外来源?

解决方案

方案1:新增内部链接标识+全局状态控制(区分来源首选)

应用内部触发URL跳转时,通过全局标志位标记当前操作是内部发起的,处理完成后立即重置标志位,即可在onOpenURL中准确区分链接来源,同时统一加入验证逻辑:

  1. 定义全局共享的状态管理类
class AppGlobalState: ObservableObject {
    static let shared = AppGlobalState()
    // 标记是否为应用内部发起的URL跳转
    var isInternalURLLaunch: Bool = false
}
  1. 内部跳转时先修改标志位再打开链接
// 内部触发打开书籍详情的逻辑
func openBookInternally(bookId: String) {
    guard let url = URL(string: "fancyLibraryApp:viewer/book/\(bookId)") else { return }
    AppGlobalState.shared.isInternalURLLaunch = true
    NSWorkspace.shared.open(url)
}
  1. 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,验证通过后再通知视图更新:

  1. 实现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
    }
}
  1. 视图层监听通知更新即可
@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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.27 05:06:04