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

Playwright配置SSL证书登录网站及request模块报错问题咨询

Playwright配置SSL客户端证书登录实现方案

问题背景

  • 测试需求:登录目标网站时需通过SSL客户端证书完成身份认证,已生成对应cert、key格式的pem证书文件,需将证书配置到测试代码中实现自动登录逻辑
  • 现状:Playwright官方原生支持客户端证书配置的功能尚在开发阶段,尝试社区提供的两种临时方案(动态添加证书、封装fixture注入测试类调用)均运行失败,抛出request模块未找到的错误
  • 原有问题代码如下:
import { test, expect } from '@playwright/test'

test('login test @certTest', async ({browser}) => {
    const fs = require('fs');
    const request = require('request');
    const ctx = await browser.newContext();
    await ctx.route('**/*', (route, req) => {
        const options = {
            uri: req.url(),
            method: req.method(),
            headers: req.headers(),
            body: req.postDataBuffer(),
            timeout: 10000,
            followRedirect: false,
            agentOptions: {
                ca: fs.readFileSync('./certs/user_cert.pem')
            },
        };
        let firstTry = true;
        const handler = function handler(err, resp, data) {
            if (err) {
                if (firstTry) {
                    firstTry = false;
                    return request(options, handler);
                }
                console.error(`Unable to call ${options.uri}`, err.code, err);
                return route.abort();
            } else {
                return route.fulfill({
                    status: resp.statusCode,
                    headers: resp.headers,
                    body: data,
                });
            }
        };
        return request(options, handler);
    });
    const page = await ctx.newPage();

    await page.goto('https://my.page.net')

    const title = page.locator('title')

    await expect(title).toHaveText('My Title')
})

问题排查与修复步骤

  1. 先解决request模块未找到的报错
    你代码中引用的request是第三方npm包,既不是Node.js内置模块,也不属于Playwright的自带依赖,运行报错是因为项目中未安装该依赖。如果要沿用原有逻辑,可在项目根目录执行安装命令:
    npm install request --save-dev
    注意:request包已停止官方维护,不推荐长期使用,下方提供无额外依赖的原生实现方案

  2. 修正证书配置参数错误
    原有代码中agentOptions仅配置了ca(CA根证书)参数,SSL客户端身份认证必须同时传入客户端公钥证书、客户端私钥两个必填参数,对应你持有的两个pem文件,缺省会直接导致认证失败。如果你的私钥设置了访问密码,还需要额外传入passphrase参数。

  3. 可直接运行的稳定实现代码(无额外第三方依赖)
    以下代码使用Node.js原生自带的https模块替代已废弃的request包,不需要额外安装依赖,同时补全了客户端证书的必填配置项:

import { test, expect } from '@playwright/test'
const fs = require('fs');
const https = require('https');
const path = require('path');

test('login test @certTest', async ({browser}) => {
    // 提前读取证书文件,避免每个请求重复IO,建议用绝对路径避免工作目录问题
    const clientCertConfig = {
        ca: fs.readFileSync(path.resolve(__dirname, './certs/ca_root.pem')), // 替换为你的CA根证书路径,如不需要可删除
        cert: fs.readFileSync(path.resolve(__dirname, './certs/user_cert.pem')), // 替换为你的客户端公钥证书pem路径
        key: fs.readFileSync(path.resolve(__dirname, './certs/user_key.pem')), // 替换为你的客户端私钥pem路径
        // passphrase: '你的私钥密码' // 私钥有密码时取消注释填写
    };

    const ctx = await browser.newContext();
    // 如不需要拦截静态资源,可缩小路由匹配范围仅拦截接口请求,提升运行速度
    await ctx.route('**/*', (route, req) => {
        const reqUrl = new URL(req.url());
        const options = {
            hostname: reqUrl.hostname,
            port: reqUrl.port || 443,
            path: reqUrl.pathname + reqUrl.search,
            method: req.method(),
            headers: req.headers(),
            body: req.postDataBuffer(),
            timeout: 10000,
            rejectUnauthorized: true, // 生产环境保持true,测试自签名证书可临时设为false
            agent: new https.Agent(clientCertConfig)
        };

        let firstTry = true;
        const sendRequest = () => {
            const proxyReq = https.request(options, (resp) => {
                const chunks = [];
                resp.on('data', (chunk) => chunks.push(chunk));
                resp.on('end', () => {
                    route.fulfill({
                        status: resp.statusCode,
                        headers: resp.headers,
                        body: Buffer.concat(chunks)
                    });
                });
            });

            proxyReq.on('error', (err) => {
                // 首次请求偶发连接失败时重试一次
                if (firstTry) {
                    firstTry = false;
                    return sendRequest();
                }
                console.error(`请求 ${req.url()} 失败:`, err.code, err.message);
                route.abort();
            });

            if (options.body) {
                proxyReq.write(options.body);
            }
            proxyReq.end();
        };

        sendRequest();
    });

    const page = await ctx.newPage();
    await page.goto('https://my.page.net');
    const title = page.locator('title');
    await expect(title).toHaveText('My Title');
})

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.30 03:12:23