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

拦截WKWebView实现PDF下载:iOS WebView下载PDF获登录页面问题排查

iOS WebView下载PDF返回登录页面的解决方案

哥们,你遇到的这个问题我之前帮好几个开发者踩过坑——核心就是WebView的登录会话和下载任务的会话没打通!你在WebView里登录后,登录状态存在WebView专属的存储里,但默认发起下载时用的是全新的URLSession,这个会话没带任何登录信息,服务器自然把你当成未授权用户,返回登录页面了。

下面给你几个针对性的解决方案,你可以根据网站的认证方式选:

方案一:共享WebView的Cookie(Cookie认证场景)

如果你的网站是靠Cookie维持登录状态的,直接让下载会话复用WebView的Cookie就行:

1. 先拿到WebView里的所有Cookie

对于WKWebView,可以通过它的网站数据存储获取Cookie:

// 获取WebView绑定的网站数据存储
let webViewDataStore = webView.configuration.websiteDataStore
// 异步获取所有Cookie
webViewDataStore.httpCookieStore.getAllCookies { cookies in
    self.startDownloadWithCookies(cookies: cookies)
}

2. 用这些Cookie创建下载会话

把Cookie注入到URLSessionConfiguration里,再发起下载任务:

func startDownloadWithCookies(cookies: [HTTPCookie]) {
    let config = URLSessionConfiguration.default
    let sharedCookieStorage = HTTPCookieStorage.shared
    // 把WebView的Cookie全部导入到会话的Cookie存储中
    cookies.forEach { sharedCookieStorage.setCookie($0) }
    config.httpCookieStorage = sharedCookieStorage
    
    // 创建会话并发起下载(记得设置delegate处理下载完成后的文件保存)
    let session = URLSession(configuration: config, delegate: self, delegateQueue: nil)
    guard let pdfUrl = URL(string: "你的PDF下载链接") else { return }
    let downloadTask = session.downloadTask(with: pdfUrl)
    downloadTask.resume()
}

进阶:拦截WebView的下载请求

更优雅的方式是直接拦截WebView里的PDF下载请求,不让它走默认流程:
实现WKNavigationDelegate的拦截方法:

func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) {
    guard let url = navigationAction.request.url else {
        decisionHandler(.allow)
        return
    }
    // 判断是不是PDF下载请求(可以根据后缀或MIME类型调整)
    if url.pathExtension.lowercased() == "pdf" {
        // 拿到Cookie后用我们的会话下载
        webView.configuration.websiteDataStore.httpCookieStore.getAllCookies { cookies in
            self.startDownloadWithCookies(cookies: cookies, url: url)
        }
        decisionHandler(.cancel) // 取消WebView默认的下载行为
        return
    }
    decisionHandler(.allow)
}

方案二:传递认证令牌(Token认证场景)

如果网站是用Bearer Token或者自定义令牌认证的,就得从WebView里拿到令牌,再放到下载请求的Header里:

1. 从WebView的JS环境获取令牌

通过evaluateJavaScript读取localStorage或sessionStorage里的令牌:

webView.evaluateJavaScript("localStorage.getItem('authToken')") { result, error in
    guard let token = result as? String, error == nil else {
        print("拿令牌失败:\(error?.localizedDescription ?? "未知错误")")
        return
    }
    self.startDownloadWithToken(token: token)
}

2. 给下载请求添加认证Header

创建请求时把令牌放到Authorization Header里:

func startDownloadWithToken(token: String) {
    guard let pdfUrl = URL(string: "你的PDF下载链接") else { return }
    var downloadRequest = URLRequest(url: pdfUrl)
    // 按后端要求设置Header,比如Bearer Token格式
    downloadRequest.setValue("Bearer \(token)", forHTTPHeaderField: "Authorization")
    
    let session = URLSession(configuration: .default, delegate: self, delegateQueue: nil)
    let downloadTask = session.downloadTask(with: downloadRequest)
    downloadTask.resume()
}

方案三:复用WebView的进程池(高级尝试)

如果上面两种方法都不行,可以试试让下载会话复用WebView的WKProcessPool,这样会话上下文和WebView完全一致:

let config = URLSessionConfiguration.default
// 复用WebView的进程池
config.processPool = webView.configuration.processPool
let session = URLSession(configuration: config, delegate: self, delegateQueue: nil)
// 发起下载任务...

这个方法不一定适配所有场景,但可以尝试,因为它让下载会话和WebView共享同一个运行上下文,大概率会自动带上认证信息。

为啥你之前的代码没生效?

你之前试的是Basic Auth(把用户名密码转成Base64放在Header),但如果你的网站不是用Basic Auth,而是靠Session Cookie或Token维持登录,那这种方式自然没用。得先确认网站的认证机制,再选对应的方案。

内容的提问来源于stack exchange,提问作者Sanoj Kashyap

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.26 10:45:51