ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

kkFileView HTTPS预览配置:Nginx反代与证书信任链

kkFileView HTTPS预览配置:Nginx反代与证书信任链 上个月把内网一套文档管理平台从 HTTP 整体切到 HTTPS最先炸的不是登录而是预览。控制台一片Mixed Content: The page at https://xxx was loaded over HTTPS, but requested an insecure resourceWord、Excel 点开是白屏PDF 连页面都不跳图片预览直接裂图。折腾了差不多两天才把kkfile 配置 https 预览文件这条链路彻底跑通——说实话这事儿真不是装个证书就完事坑全在地址拼接、转发头、信任站点、以及 Java 侧的证书信任链上四个地方任何一个没对上表现都是白屏。这篇就把整个过程按我实际踩坑的顺序写下来这套组合到底是什么、卡点在哪、每一步为什么这么配、出问题怎么三分钟定位。适合两类人看——正在用 RuoYi 之类的后台框架集成 kkfileview、或者刚给预览服务上 HTTPS 的后端/运维同学也适合完全没接触过 kkfileview 但被预览不出来折磨过的同学因为每一步我都写了为什么这么写照着抄不会翻车。1. 先把问题看准预览为什么一换 HTTPS 就出问题很多同学的第一反应是kkFileView 不支持 HTTPS其实不是。kkFileView 本身跑 HTTP 完全没问题它自己也能开 SSL。真正的问题是它现在被夹在一个 HTTPS 页面和一堆 HTTP 资源之间而这个夹缝正好踩中浏览器最严格的两条规则。1.1 混合内容拦截是第一道坎浏览器把 HTTPS 页面里的资源分成三六九等。图片、音频、视频这类属于勉强能忍的可选阻断内容Chrome 会先尝试自动升级成 HTTPS而iframe、script、link relstylesheet属于必须阻断的内容一旦发现是http://直接拦掉控制台报 Mixed Content页面上就是一块白。kkFileView 的预览页恰恰就是靠 iframe 撑起来的。它的/onlinePreview返回的那个 HTML 里PDF 走的是内嵌的 pdf.js viewerOffice 文档是先转成 PDF 或图片再塞进容器图片预览、视频预览基本都是 iframe 或者动态注入的资源。所以只要你是HTTPS 的后台页面 iframe 指向http://ip:8012必挂没有例外。有人会想到在页面里加一句meta http-equivContent-Security-Policy contentupgrade-insecure-requests。我的建议是别用它。这玩意儿是让浏览器把 http 请求强行改写成 https前提是目标服务真的监听 443 且有有效证书否则就是从白屏变成连接被拒绝错误更难查。而且它只对当前页面生效不是根治方案。1.2 kkFileView 的资源地址是由它自己拼出来的这是第二个、也是更隐蔽的坑。kkFileView 的预览页在加载图片、字体、js、以及转换后的分页图片时地址并不总是相对路径有些地方是它根据当前请求的Host、协议、端口现算出来的。正常直连时它算出来是http://192.168.1.10:8012/...没问题。但一旦你把请求交给 Nginx 反代Nginx 转发给后端时用的是 HTTPSpring Boot 拿到的request.getScheme()就是http于是它拼出来的地址还是http://你的域名/...——而这个地址会被浏览器当成混合内容拦掉或者更隐蔽地被拦掉一部分、剩下一部分能显示于是你看到页面出来了但图是裂的。解决办法就是让后端知道外面其实是 HTTPS。标准做法是反代时加上X-Forwarded-Proto请求头同时让 Spring Boot 认这个头# Spring Boot 2.x认 X-Forwarded-Proto / X-Forwarded-Host 等头 server.forward-headers-strategyframework这一个配置项能解决我遇到过的至少三种路径诡异的问题重定向跳回 http、静态资源 404、预览页里的图片地址协议不对。很多人配完 Nginx 死活不行八成就是漏了这一行。2. 部署方案选型三种 HTTPS 落地姿势怎么选搞清楚原理之后方案其实就三条路。我把当时评估的三套方案摆在一起对比过最后选了第二条但先说清楚各自的代价你可以按自己的环境对号入座。2.1 三种方案横向对比方案做法优点代价A. kkFileView 自己开 SSL配server.ssl.*直接监听 https 端口不用动 Nginx链路最短证书要转成 p12/jks 格式证书续期要改配置重启多个服务重复维护证书B. Nginx 同域子路径反代主站https://域名/file-view/转到127.0.0.1:8012同源无跨域、无 cookie 问题前端只改一个 baseURLNginx 配置和几个请求头必须对齐配错就是白屏C. 独立子域 通配符证书https://preview.域名/单独一套服务解耦独立扩容跨子域要处理 CORS、iframe 放行、cookie 域证书成本更高方案 A 最大的麻烦不是技术难是运维成本。证书一般 90 天或 1 年一续你要是把证书塞进 Java 的 keystore 里每次续期都得重新打包、重启服务而且要在每个环境里重复一遍。如果只有一个 kkFileView 实例、又完全不想碰 Nginx那 A 也能用我在第 3 节也会给出完整配置。2.2 我为什么优先选 Nginx 同域反代核心就一个词同源。方案 B 下主站是https://oa.xxx.com预览是https://oa.xxx.com/file-view/onlinePreview?url...浏览器认为它们是同一个源。同源意味着不需要配任何 CORS 头不会触发预检请求iframe 不会被X-Frame-Options或frame-ancestors拦cookie 天然共享如果 kkFileView 那侧有任何基于会话的逻辑都不用额外折腾前端集成时把原来的http://ip:8012全局替换成/file-view就完事了连域名都不用写。方案 C 每一条都得单独处理。跨子域时浏览器默认不发送SameSiteLax的 cookie得改成SameSiteNone; Secure跨子域 iframe 需要在 kkFileView 那侧放行frame-ancestors上传或预览接口如果带自定义头还会触发 OPTIONS 预检。能用但纯属自找工作量除非你有明确的解耦和独立扩容需求。提示如果已经有统一的网关或负载均衡比如 K8s 的 Ingress、云厂商的 SLB优先复用那一层做 TLS 卸载不要再叠一层 Nginx。多一层转发就多一个X-Forwarded-Proto可能丢的地方。3. 动手实战Nginx 反代 kkFileView 关键配置这一节是完整的可抄作业部分。假设主站域名是oa.example.comkkFileView 在本机127.0.0.1:8012希望对外统一用https://oa.example.com/file-view/访问。3.1 证书准备与两个方向的信任问题先说一个容易被忽略的点HTTPS 配置里其实有两个方向的信任关系很多人只解决了第一个。方向一浏览器信任你的服务。这个用正规 CA 签发的证书就行。如果是内网自签证书浏览器会报不安全警告而且 iframe 里的资源加载会被降级处理所以我强烈建议内网也走内部 CA 或者干脆申请免费证书别用 openssl 随手签的那种一年期自签。方向二kkFileView 作为客户端去拉取文件时要信任文件所在的服务器证书。这一条特别容易漏RuoYi 集成场景里kkFileView 拿到的是一个文件直链比如https://oa.example.com/prod-api/profile/upload/2024/xxx.docx它是需要自己去把这个文件下载下来的。如果这个地址用的是自签证书或内部 CA 签的证书Java 会直接抛异常javax.net.ssl.SSLHandshakeException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target解决办法是把内部 CA 的根证书导入 JDK 的信任库# 1. 确认 kkFileView 用的是哪个 JDK ps -ef | grep kkfileview | grep java # 2. 导入 CA 根证书到该 JDK 的 cacerts 里默认口令是 changeit $JAVA_HOME/bin/keytool -import -trustcacerts -alias internal-ca \ -file /opt/certs/ca.crt \ -keystore $JAVA_HOME/lib/security/cacerts \ -storepass changeit -noprompt # 3. 验证是否导入成功 $JAVA_HOME/bin/keytool -list -keystore $JAVA_HOME/lib/security/cacerts -storepass changeit | grep internal-ca # 4. 重启 kkFileView 生效如果你不想动全局信任库比如同一个 JVM 上跑着别的服务怕影响面太大可以在启动参数里单独指定java -Djavax.net.ssl.trustStore/opt/kkfileview/config/truststore.jks \ -Djavax.net.ssl.trustStorePassword你的口令 \ -jar kkfileview.jar如果走的是方案 AkkFileView 自己开 SSL还需要把 pem 格式的证书转成 Java 认的 PKCS12openssl pkcs12 -export \ -in /opt/certs/fullchain.pem \ -inkey /opt/certs/privkey.pem \ -out /opt/kkfileview/config/kkfileview.p12 \ -name kkfileview \ -passout pass:换成你自己的口令然后在application.properties里补上server.port8443 server.ssl.enabledtrue server.ssl.key-storefile:/opt/kkfileview/config/kkfileview.p12 server.ssl.key-store-typePKCS12 server.ssl.key-store-password换成你自己的口令 server.ssl.key-aliaskkfileview注意key-store这里我写的是file:前缀的绝对路径。用classpath:的话证书得打进 jar 包续期的时候要重新构建非常难受不推荐。3.2 Nginx 反代配置逐行拆解server { listen 443 ssl; server_name oa.example.com; ssl_certificate /etc/nginx/ssl/fullchain.pem; ssl_certificate_key /etc/nginx/ssl/privkey.pem; ssl_protocols TLSv1.2 TLSv1.3; # 关键location 带斜杠 proxy_pass 也带斜杠 剥掉 /file-view 前缀 location /file-view/ { proxy_pass http://127.0.0.1:8012/; proxy_http_version 1.1; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; proxy_set_header X-Forwarded-Host $host; proxy_set_header X-Forwarded-Port $server_port; # 预览大文件/慢转换超时给足 proxy_connect_timeout 30s; proxy_send_timeout 300s; proxy_read_timeout 300s; # 上传和附件体积 client_max_body_size 500m; # 预览是流式输出关掉响应缓冲避免转圈半天然后一次性刷出来 proxy_buffering off; } }几条解释一下为什么X-Forwarded-Proto和X-Forwarded-Host / Port是解决第 1.2 节那个地址拼错的核心。少了它们kkFileView 拼出来的资源地址还是http://前面所有的活儿都白干。proxy_read_timeout 300s不是随便写的。Office 文档转 PDF 走的是 LibreOffice一份带复杂表格和嵌入图片的 50 页 Word冷启动转换大概 20 到 40 秒如果碰上几百页的文档冷转换上两分钟很正常。默认 60 秒的话页面表现是点预览转圈一分钟然后 504。但也不是越大越好Timeout 设成 3600 只会让卡住的连接长时间占资源300 到 600 秒是我实测比较舒服的区间。client_max_body_size要和 kkFileView 里的spring.servlet.multipart.max-file-size对齐而且 Nginx 这一侧要略大一点。因为 multipart 请求除了文件本身还有边界和表单字段大概留 10% 到 20% 的余量。比如你允许 500MB 的文件Java 侧可以设 500MBNginx 侧设 550MB 或 600MB反过来设会导致大文件在 Nginx 那层就被 413 掉日志里很难找到原因。3.3 三个必须联动的参数路径、base.url、转发策略第一个联动是 Nginx 的斜杠规则。这是最容易配错的地方四种组合的结果完全不同locationproxy_pass结果/file-view/http://127.0.0.1:8012/剥离前缀kkFileView 收到/onlinePreview/file-view/http://127.0.0.1:8012保留前缀kkFileView 收到/file-view/onlinePreview/file-viewhttp://127.0.0.1:8012/剥离前缀但^~匹配更麻烦不建议/file-view/http://127.0.0.1:8012 后端 context-path/file-view保留前缀且后端认得也能跑通我一般选第一种Nginx 剥掉前缀kkFileView 侧不设 context-path改起来最省事。如果你非要两边都保留前缀那 kkFileView 里就得配server.servlet.context-path/file-viewSpring Boot 1.x 是server.context-path看你的版本两个地方少配一个就是 404。第二个联动是 kkFileView 自己的基础地址配置。翻一下application.properties你会看到一项跟外部访问地址有关的配置不同版本叫法有出入4.x 里常见的是base.url也有版本是kkfileview.base.url之类的名字它决定了 kkFileView 在某些场景下拼链接时用哪个前缀。上了反代之后这一项要改成对外的真实地址base.urlhttps://oa.example.com/file-view不确定自己版本里到底叫什么名字有个很土但很准的办法在解压出来的 jar 里搜ConfigConstants这个类或者直接在配置文件里翻凡是值形如http://127.0.0.1:8012的项都重点看一遍。别嫌笨比瞎试快多了。第三个联动是前面提过的server.forward-headers-strategyframework。这三项配齐之后kkFileView 拼出来的所有地址才会是https://oa.example.com/file-view/...链路才算真正闭环。配完先别急着开浏览器用 curl 分三段验证能把问题范围瞬间缩小# 第一段kkFileView 自己活着吗 curl -I http://127.0.0.1:8012/ # 第二段走 https 反代通不通注意 -k 只在自签证书时用 curl -I https://oa.example.com/file-view/ # 第三段直接打一个在线预览接口看返回里有没有 http:// 的地址残留 curl -s https://oa.example.com/file-view/onlinePreview?url$(python3 -c \ import urllib.parse;print(urllib.parse.quote(https://oa.example.com/prod-api/profile/upload/test.docx))) | grep -o http://[^]* | head第三段那个grep是我最常用的一招只要还能 grep 出http://开头的资源地址就说明协议头没传对不用去浏览器里猜。4. RuoYi 集成场景下的地址拼接与信任站点RuoYi 系的框架集成 kkFileView 是最常见的用法也是最容易出问题的场景因为多了一层后台把文件地址传给预览服务的转发逻辑。链路上多一环能出错的地方就翻倍。4.1 前端拼 URL 的两种正确写法RuoYi 的常规做法是后端返回一个文件访问地址前端拼成 kkFileView 的预览地址再打开。常见写法有两种各有适用场景。第一种是直接跳转最简单// 假设 previewBase 从环境变量里读开发环境是 http://127.0.0.1:8012生产是 /file-view const previewBase process.env.VUE_APP_PREVIEW_BASE; function openPreview(fileUrl) { // 关键fileUrl 必须整体编码否则带 或 # 的地址会被截断 const target ${previewBase}/onlinePreview?url${encodeURIComponent(fileUrl)}; window.open(target, _blank); }这里encodeURIComponent绝对不能省。文件地址里只要出现或者#不编码的话后面整段会被浏览器当成参数截掉表现就是预览页打开了但提示文件不存在。中文文件名、空格、加号同理。第二种是弹窗内嵌 iframe适合不想跳页面的后台// fileUrl 已经是完整可访问地址 const src ${previewBase}/onlinePreview?url${encodeURIComponent(fileUrl)}; this.previewUrl src; this.previewVisible true;用 iframe 的时候要注意一个点如果预览地址和主站不同源方案 Ciframe 会被拦。方案 B 同源就完全没这个问题这也是我推荐方案 B 的又一个理由。另外较新版本的 kkFileView 支持把 url 参数做 base64 编码后传递形式上大概是?url后面跟一段 base64 串。这个特性在不同版本里开关和写法有差异你要用的话最快的确认方式是把在线预览页的前端 js 打开看一眼它怎么解析 url 参数的别照着网上的老帖子硬套。4.2 高版本加了信任站点校验跨域直连会被拦这是个很隐蔽的行为。kkFileView 在较新的版本里加了防 SSRF 的机制如果请求里带的是完整的文件地址不是本机上传的文件 id它会检查请求来源是否在信任列表里。不在的话请求会被拒绝或者被降级处理日志里通常能看到和trust.host、Referer 相关的提示。RuoYi 集成时来源页面的域名就是你的后台域名所以要么把后台域名加进信任列表# 具体键名按你版本的配置文件为准多个用逗号分隔 trust.hostoa.example.com,127.0.0.1,localhost要么干脆绕开这个机制让 RuoYi 后端提供一个转发接口把文件内容以流的形式吐给 kkFileView。这个方案的好处是文件地址不外露、可以做权限校验坏处是流量多绕一跳还得处理流式传输的超时。我的经验是如果文件不需要特别严格的权限控制直接配信任列表最省事如果文件涉及敏感数据就走后端转发。注意改完信任列表务必重启服务并且清一次浏览器缓存再测。kkFileView 的预览结果有缓存机制转换过的文档会缓存下来不清缓存的话你看到的是旧结果会误判成配置没生效。4.3 后端侧的两个运维前提kkFileView 拉文件这件事本质是它作为客户端去访问一个 URL所以有两个前提必须成立。一个是网络可达。听起来像废话但真有人把文件地址配成127.0.0.1然后 kkFileView 部署在另一台机器上怎么都不行。容器化部署更要注意容器里的127.0.0.1是容器自己不是宿主机。这类问题的排查方式很简单进 kkFileView 所在的机器或容器里执行一条 curl 试试# 在 kkFileView 所在环境执行验证它能不能拿到文件 curl -I https://oa.example.com/prod-api/profile/upload/test.docx能通说明网络和证书都没问题问题在前端拼接不通就是这一层的问题跟前端一点关系都没有。这一条判断能帮你省掉大量来回扯皮的时间。另一个是认证。如果文件地址需要带 token而这个 token 是短期有效的比如 5 分钟那 kkFileView 拿到地址后如果再重试或者做二次请求就可能因为 token 过期而失败。这类问题的表现是第一次能预览刷新一下就 401。解决办法是给预览场景单独发一个有效期稍长的临时直链或者用后端转发方案。5. 常见问题与排查技巧实录配 HTTPS 预览这件事出问题的表现形式高度集中在几种。我把这一年多碰到过的都整理成了速查表遇到问题先对号入座能省下大量瞎试的时间。5.1 问题速查表现象大概率原因处理方式控制台报 Mixed Content页面白屏iframe 指向的地址是 http按第 3 节配 Nginx 反代和X-Forwarded-Proto页面出来了但图片/分页裂掉后端拼出的资源地址还是 http加server.forward-headers-strategyframework点预览一直转圈然后 504转换超时调大proxy_read_timeout并确认 LibreOffice 可用提示文件不存在但用浏览器能打开该地址url 参数没编码被/#截断用encodeURIComponent整体编码日志报PKIX path building failedJava 不信任文件服务器的证书导入 CA 到 cacerts见 3.1 节刷新后无限重定向或跳回 http缺少转发头Spring Boot 生成的跳转地址是 http检查X-Forwarded-Proto和转发策略配置大文件上传 413Nginx 的client_max_body_size小于文件按 Java 侧限制的 1.2 倍设置中文文件名预览失败编码链路有两次或零次编码全链路只 encode 一次服务端 decode 一次第一次预览特别慢之后很快Office 冷转换 结果缓存属正常现象可用预转换或预热缓解跨子域 iframe 被拦X-Frame-Options或 CSPframe-ancestors同域反代可规避跨域需显式放行5.2 几个我实打实踩过的坑第一个坑证书链不完整。某些环境下只配了server.crt没配中间证书浏览器在电脑上打开正常因为系统里有中间证书缓存但在手机上或者别人电脑上就报不安全。这类问题特别阴——你自己测永远好的别人一打开就崩。解决办法是把完整链证书fullchain配上用openssl s_client -connect oa.example.com:443 -showcerts看一下返回了几张证书正常应该是 2 到 3 张。第二个坑proxy_buffering忘了关。预览页在浏览器里表现为转圈很久然后一瞬间整页刷出来而不是流式加载。小文档无所谓几百页的 PDF 会让人以为服务挂了。关掉这个缓冲响应就是流式的体验差别很大。第三个坑LibreOffice 的 soffice 进程堆积。预览服务跑一段时间后内存上涨、转换变慢查下来是转换进程没被回收干净。这不是 HTTPS 的问题但上生产之后一定会遇到。我的处理是定时清理长时间空闲的转换进程同时在看门狗脚本里监控soffice的进程数超过阈值就告警。第四个坑只看浏览器不看服务端日志。很多同学卡住的时候一直在 F12 里翻其实 kkFileView 的日志里写得非常清楚——哪个文件、哪个 URL、什么异常一眼就能定位。我的习惯是先把日志 tail 起来再操作复现比在浏览器里猜快十倍。# kkFileView 日志一般在 logs 目录下切到 HTTPS 后先盯这个 tail -f /opt/kkfileview/logs/kkFileView.log | grep -Ei error|exception|preview6. 预览体验与客户端的那点事服务端全部配好之后还有一类看起来像 bug、其实是客户端行为的问题专门说一下因为来问你的人会很多。6.1 Windows 那个可能对您的计算机有害的提示预览某些文件时Windows 会弹一句您尝试预览的文件可能对您的计算机有害如果您信任此文件以及其来源请打开此文件。这个提示和你的 HTTPS 配置没有任何关系它是 Windows 的附件管理器和预览窗格在起作用本质是给下载来的文件打了一个来自互联网的标记。服务端能做的只有一件事别让浏览器走下载而是就地渲染。核心是响应头里的Content-Disposition用inline而不是attachment# 静态文件场景让浏览器尝试就地打开 location ~* \.(pdf|jpg|png|mp4)$ { add_header Content-Disposition inline; # 注意要让 kkFileView 那侧的 Content-Type 正确否则 inline 也会被下载 }如果文件类型本身浏览器就渲染不了比如 docx那必然要下载这时候提示是躲不掉的。可以给使用方一个说明让他们在本地解除锁定# 解除某个文件的来自互联网标记 Unblock-File -Path D:\下载\示例文档.docx # 批量解除某个目录下的所有文件 Get-ChildItem -Path D:\下载 -Recurse | Unblock-File或者在文件上右键属性里勾一下解除锁定。这个操作在预览窗格里是没法做的得看文件属性所以用户会找不到得提前告诉人家。6.2 大文件、并发与缓存HTTPS 本身对传输性能的影响很小TLS 握手那点开销在长连接面前可以忽略但如果预览服务上了 HTTPS 之后你觉得变慢了大概率是这几个原因。一是转发的跳数变多了。原来前端直连 8012 端口现在中间多了一层 Nginx。日志、缓冲、连接复用都要多走一轮影响不大但确实存在。二是缓存没配起来。kkFileView 转换过的文档是可以缓存的配好缓存目录和过期时间能显著降低重复预览的开销file.preview.cache.enabledtrue # 缓存目录要放到空间够的分区上转换后的 PDF 和图片体积不小 file.preview.cache.dir/opt/kkfileview/cache三是并发上来了。转换这类任务非常吃 CPU 和内存一台 4 核 8G 的机器同时转几份大文档就会卡。生产环境我一般会把转换任务做成队列控制并发数在 CPU 核数的 1 到 1.5 倍之间宁可排队也别把机器拖死。7. 上线前的一份自检清单最后给一份我自己每次上线都会过一遍的清单照着勾一遍基本能避开九成的坑。第一域名和证书。用curl -I https://你的域名/file-view/确认 200再用openssl s_client -connect 你的域名:443 -showcerts确认证书链完整。自签证书不要上生产。第二转发头。确认 Nginx 里X-Forwarded-Proto、X-Forwarded-Host都配了并且 Spring Boot 侧开了server.forward-headers-strategyframework。用第 3.3 节那个 grep 命令验证页面里没有http://残留。第三路径对齐。Nginx 的 location 和 proxy_pass 斜杠规则、kkFileView 的 context-path 三者对齐别出现多一层前缀变 404的情况。第四信任链。明确 kkFileView 要访问的文件服务器证书是公网 CA 还是内部 CA。内部 CA 就提前把根证书导入 Java 信任库别等到上线当天发现PKIX path building failed。第五容量参数。client_max_body_size、proxy_read_timeout、multipart 上限三者互相匹配大文件和慢转换都单独测一遍别只测一个几十 KB 的 txt。第六客户端体验。确认下载行为符合预期预览路径是 inline并且提前把 Windows 那个提示的解除方法写到用户手册里。最后分享一个小的习惯把预览服务的地址做成环境变量或者配置中心里的一个字段开发、测试、生产各配一份。我见过太多项目把http://192.168.x.x:8012硬编码在前端源码里一上 HTTPS 就要翻遍代码改地址改漏一处就是一个白屏工单。这个习惯不费事但省的心是真多。
返回列表