需登录权限的页面如何打印
常见痛点
管理后台的报表、工单详情、对账单等往往挂在需登录才能访问的 URL 上。你在浏览器里已登录,但直接调用 printHtmlByUrl(url) 时,客户端会用独立的无会话上下文去拉取页面,结果经常是登录页或 401,而不是业务内容。
解决办法:在
printHtmlByUrl 的第四个参数 extraOptions 里,把当前用户会话所需的 Cookie、Header 或 Storage 一并传给本地客户端,由它在抓取 URL 时携带这些凭证。printHtmlByUrl 做了什么
与 printHtml 传入 HTML 字符串不同,printHtmlByUrl 只提供一个地址,由本地客户端完成后续步骤:
- 前端通过 WebSocket 把
url、pdfOptions、printOptions、extraOptions发给客户端 - 客户端用内置引擎请求该 URL,并按
extraOptions附加 Cookies / HTTP 头 / Storage - 将渲染后的页面排版为 PDF(受
pdfOptions控制) - 按
printOptions投递到打印机,或action: 'preview'仅返回预览地址
因此鉴权信息必须写在 extraOptions 里——浏览器标签页里的登录态不会自动同步到这一步。
extraOptions 鉴权字段
以下字段在 printHtmlByUrl、printPdfByUrl、printImageByUrl 及批量任务中均可使用(批量时写在单条任务的 extraOptions 内):
| 字段 | 类型 | 鉴权场景 |
|---|---|---|
cookies |
object | 服务端 Session、JSESSIONID、token 写在 Cookie 的传统后台 |
httpHeaders |
object | JWT / API Key:Authorization: 'Bearer …'(值必须是字符串) |
localStorages |
object | Vue/React SPA 把 access_token 存在 localStorage 时 |
sessionStorages |
object | 仅当前标签会话有效的 token 或临时参数 |
requestTimeout |
number(秒) | 鉴权后页面加载慢、含大量 XHR 时适当加大,默认 15 |
action |
'print' | 'preview' |
联调阶段建议先用 preview 确认抓到的是业务页而非登录页 |
客户端还会在页面注入 localStorage._printMode_ = 'true',便于业务脚本识别打印模式、隐藏导航栏等(可在页面内读取该键做布局收缩)。
示例一:Session Cookie
适用于 Java、PHP、.NET 等把会话放在 Cookie 里的系统。在用户已登录的业务页点击打印时,把相关 Cookie 传给客户端:
import webPrintPdf from 'web-print-pdf';
/** 从 document.cookie 解析单个键(示例,可按项目封装) */
function getCookie(name) {
const m = document.cookie.match(new RegExp('(?:^|; )' + name + '=([^;]*)'));
return m ? decodeURIComponent(m[1]) : '';
}
async function printReport(reportUrl) {
const sessionId = getCookie('JSESSIONID'); // 或 SESSION、sid 等,与后端一致
await webPrintPdf.printHtmlByUrl(
reportUrl,
{ paperFormat: 'A4', printBackground: true, margin: { top: '12mm', bottom: '12mm' } },
{ paperFormat: 'A4' },
{
cookies: { JSESSIONID: sessionId },
requestTimeout: 30,
action: 'print'
}
);
}
示例二:Bearer Token(httpHeaders)
前后端分离、网关校验 Authorization 头时,从 Pinia / localStorage 取出 access_token,写入 httpHeaders:
import webPrintPdf from 'web-print-pdf';
import { useUserStore } from '@/stores/user';
async function printProtectedPage(url) {
const token = useUserStore().accessToken; // 或 localStorage.getItem('access_token')
await webPrintPdf.printHtmlByUrl(
url,
{ paperFormat: 'A4', printBackground: true },
{ paperFormat: 'A4' },
{
httpHeaders: {
Authorization: `Bearer ${token}`
},
requestTimeout: 30,
action: 'preview' // 联调时先预览,确认非登录页后再改为 print
}
);
}
示例三:localStorage / sessionStorage
若页面脚本在加载时从 localStorage 读 token 再发 API,仅传 Header 可能不够。可把键值注入被打印页:
await webPrintPdf.printHtmlByUrl(
'https://erp.example.com/print/order/10086',
{ paperFormat: 'A4', printBackground: true },
{ paperFormat: 'A4' },
{
localStorages: {
access_token: localStorage.getItem('access_token') || '',
tenant_id: localStorage.getItem('tenant_id') || ''
},
cookies: { lang: 'zh-CN' },
requestTimeout: 45
}
);
示例四:Cookie + Header 组合
部分系统同时校验会话 Cookie 与 CSRF / 自定义头,可组合传入:
await webPrintPdf.printHtmlByUrl(
orderDetailUrl,
{ paperFormat: 'A4', printBackground: true },
{ paperFormat: 'A4' },
{
cookies: {
SESSION: getCookie('SESSION'),
XSRF-TOKEN: getCookie('XSRF-TOKEN')
},
httpHeaders: {
'X-XSRF-TOKEN': getCookie('XSRF-TOKEN'),
Authorization: `Bearer ${token}`
},
requestTimeout: 30
}
);
凭证从哪来
- 当前页已登录:打印按钮的回调里读
document.cookie、Vuex/Pinia、localStorage,即时组装extraOptions(最常见) - 服务端签发:后端为「打印专用」生成短期 token 或带签名的 URL,前端只负责传给
printHtmlByUrl - 改用 printHtml:后端接口返回已渲染好的 HTML 字符串,走
printHtml,无需 URL 鉴权(见主文档)
其他方案对比
| 方式 | 适用 |
|---|---|
printHtmlByUrl + extraOptions |
已有可访问的报表 URL,希望少改后端、由前端带凭证打印 |
printHtml / printHtmlByBase64 |
后端直接输出 HTML 或 Base64,鉴权在 API 层完成 |
| 一次性打印链接 | 服务端生成带签名 query 的 URL,extraOptions 只需较短超时 |
安全建议
- 不要把长期账号密码写进前端代码;优先短期 token、打印专用权限
- 生产环境对报表 URL 使用 HTTPS;避免在日志里打印完整 Cookie / Token
- 联调时用 action: 'preview' 检查 PDF 内容,确认未泄露其他用户数据
- 失败提示统一用
formatPrintError,见前端的错误处理可参考这个标准处理
排障对照
| 现象 | 排查方向 |
|---|---|
| PDF 里是登录页或空白 | 核对 Cookie 名、Domain 是否与后端一致;是否还需 httpHeaders;用 preview 查看 |
| 401 / 403 | Token 是否过期;Authorization 值是否为字符串;是否缺少 CSRF 头 |
| 样式错乱、数据未加载 | 加大 requestTimeout;SPA 是否需要 localStorages;页面是否依赖 WebSocket |
| timeout | 内网慢查询;默认 15 秒不够时提高 requestTimeout(应大于页内 XHR 超时) |