如何在WebView中嵌入显示带JWT权限验证的PDF文件?
可行实现方案
方案1:带签名的临时URL + iframe(推荐)
解决Android WebView无法加载直接路径、避免JWT暴露的问题。
后端(PHP 8.2)实现:
- 新增
/api/pdf/token接口,接收请求头Authorization: Bearer {token},验证用户身份与角色权限。 - 验证通过后,生成包含文件ID、过期时间(如10分钟)、HMAC签名的临时URL(用服务器密钥签名参数,防止篡改),返回给前端。
- 新增
/pdf/access接口,校验请求中的file_id、exp和sign:确认签名有效且未过期后,输出PDF文件流并设置响应头:
header('Content-Type: application/pdf'); header('Content-Disposition: inline; filename="document.pdf"'); readfile('/path/to/stored/pdf/123.pdf'); exit;
前端/WebView配置:
- 前端拿到临时URL后,用iframe嵌入:
<iframe src="临时URL" width="100%" height="100%" frameborder="0"></iframe>
- Android WebView调整配置:
webView.settings.apply { javaScriptEnabled = true allowFileAccess = true mixedContentMode = WebSettings.MIXED_CONTENT_ALWAYS_ALLOW // 正式环境建议用MIXED_CONTENT_NEED_USER_ACTION loadWithOverviewMode = true useWideViewPort = true }
- iOS WKWebView默认支持iframe加载PDF,确保临时URL为HTTPS(正式环境)即可。
方案2:后端返回PDF Base64 + PDF.js加载
解决之前PDF.js加载失败的问题,核心是确保WebView配置与后端数据格式正确。
后端(PHP)实现:
- 接口验证JWT后,读取PDF文件转为Base64返回:
$pdfPath = '/path/to/stored/pdf/123.pdf'; $pdfBase64 = base64_encode(file_get_contents($pdfPath)); echo json_encode(['pdf_base64' => $pdfBase64]);
前端/WebView实现:
- 将PDF.js的
viewer.html及相关脚本部署到自己的Web服务器(避免CDN跨域问题)。 - 前端获取Base64后转为Uint8Array,调用PDF.js渲染:
const pdfData = atob(res.pdf_base64); const uint8Array = new Uint8Array(pdfData.length); for (let i = 0; i < pdfData.length; i++) { uint8Array[i] = pdfData.charCodeAt(i); } pdfjsLib.getDocument(uint8Array).promise.then(pdf => { pdf.getPage(1).then(page => { const canvas = document.getElementById('pdf-canvas'); const context = canvas.getContext('2d'); const viewport = page.getViewport({ scale: 1.5 }); canvas.height = viewport.height; canvas.width = viewport.width; page.render({ canvasContext: context, viewport: viewport }); }); });
- WebView必须开启JavaScript:
- Android:
webView.settings.javaScriptEnabled = true,并设置allowFileAccess = true。 - iOS:确保
WKWebViewConfiguration.preferences.javaScriptEnabled = true。
- Android:
方案3:WebView调用原生能力加载PDF
利用移动端原生PDF渲染能力,规避Web端兼容性问题。
实现流程:
- 前端Web页面触发事件,调用原生方法(如
window.android.loadPDF(fileId)或iOS的消息发送)。 - 移动端原生携带JWT令牌(从本地存储读取或前端传入),调用后端接口获取PDF流。
- 原生将PDF流保存到本地临时目录(Android用
getCacheDir(),iOS用NSTemporaryDirectory())。 - 通知WebView加载本地PDF路径:
- Android:
webView.loadUrl("file://" + localFilePath) - iOS:
webView.loadFileURL(localFileURL, allowingReadAccessTo: localFileURL.deletingLastPathComponent())
- Android:
关键代码示例:
- Android(Kotlin):
webView.addJavascriptInterface(object { @JavascriptInterface fun loadPDF(fileId: String) { val request = Request.Builder() .url("https://your-backend/api/pdf/$fileId") .addHeader("Authorization", "Bearer $jwtToken") .build() OkHttpClient().newCall(request).enqueue(object : Callback { override fun onResponse(call: Call, response: Response) { response.body?.byteStream()?.let { inputStream -> val file = File(cacheDir, "temp.pdf") FileOutputStream(file).use { outputStream -> inputStream.copyTo(outputStream) } runOnUiThread { webView.loadUrl("file://${file.absolutePath}") } } } override fun onFailure(call: Call, e: IOException) { /* 错误处理 */ } }) } }, "android")
- iOS(Swift):
let configuration = WKWebViewConfiguration() configuration.userContentController.add(self, name: "loadPDF") let webView = WKWebView(frame: view.bounds, configuration: configuration) func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) { if message.name == "loadPDF", let fileId = message.body as? String { var request = URLRequest(url: URL(string: "https://your-backend/api/pdf/\(fileId)")!) request.addValue("Bearer \(jwtToken)", forHTTPHeaderField: "Authorization") URLSession.shared.dataTask(with: request) { data, _, _ in guard let data = data else { return } let tempPath = NSTemporaryDirectory() + "temp.pdf" try? data.write(to: URL(fileURLWithPath: tempPath)) DispatchQueue.main.async { let fileURL = URL(fileURLWithPath: tempPath) self.webView.loadFileURL(fileURL, allowingReadAccessTo: fileURL.deletingLastPathComponent()) } }.resume() } }
之前问题的补充说明:
- Android WebView无法加载直接路径:多因WebSettings未配置(如未开启文件访问、混合内容模式),或路径为本地绝对路径而非Web可访问URL。
- Google Docs Viewer暴露JWT:存在泄露风险,不建议使用,临时URL方案更安全。
- PDF.js加载失败:可能是后端未正确返回PDF数据、WebView未开启JS,或PDF.js资源路径配置错误(如
viewer.html找不到对应脚本)。
内容的提问来源于stack exchange,提问作者Elysianitez Zuhair
相关产品推荐
相关产品推荐

