Qt 6.9中QWebEngine模块的5个实战技巧:从基础渲染到高级交互
Qt 6.9中QWebEngine模块的5个实战技巧从基础渲染到高级交互如果你已经用QWebEngineView加载过一个网页并且成功运行了第一个“Hello Web”应用那么恭喜你你已经跨过了第一道门槛。但接下来呢当你的应用需要处理复杂的表单交互、需要与本地C逻辑深度绑定、或者面临内存和性能的瓶颈时你会发现仅仅会load()一个URL是远远不够的。Qt 6.9中的QWebEngine模块其内核追随着Chromium的步伐功能强大但也因此带来了相当的复杂性。它不再只是一个简单的“浏览器控件”而是一个完整的、可深度定制的Web运行时环境。本文将避开泛泛而谈的架构概述直接切入五个能显著提升你开发效率和应用程序质量的实战技巧。这些技巧源于实际项目中的需求与挑战旨在帮助你从“能用”走向“精通”。1. 超越基础高效初始化与资源管理很多开发者习惯在main函数或窗口构造函数中直接创建一个QWebEngineView实例。这在演示中没问题但在实际应用中尤其是需要管理多个网页视图或注重启动性能时这种简单粗暴的方式会带来问题。1.1 延迟加载与按需创建一个常见的场景是你的应用有一个标签页界面每个标签页承载一个Web视图。如果一开始就创建所有视图并加载其首页内存占用会瞬间飙升启动速度也会变慢。正确的做法是延迟初始化。// 不好的做法在窗口构造时创建所有Web视图 MyMainWindow::MyMainWindow(QWidget *parent) : QMainWindow(parent) { for (int i 0; i 5; i) { auto *view new QWebEngineView(this); view-load(QUrl(https://example.com/tab QString::number(i))); // ... 将view添加到UI ... } } // 更好的做法按需创建和加载 MyMainWindow::MyMainWindow(QWidget *parent) : QMainWindow(parent), m_tabWidget(new QTabWidget(this)) { // 只创建第一个标签页的视图 addNewWebTab(QUrl(https://example.com/home)); // 连接标签页新建信号在用户点击时才创建新视图 connect(m_tabWidget, QTabWidget::tabBarClicked, [this](int index){ if (index m_tabWidget-count() - 1) { // 假设最后一个标签是“” addNewWebTab(QUrl(about:blank)); } }); } void MyMainWindow::addNewWebTab(const QUrl url) { auto *view new QWebEngineView(this); view-load(url); int index m_tabWidget-addTab(view, New Tab); m_tabWidget-setCurrentIndex(index); }对于单页面应用SPA内的不同视图也可以采用类似的策略使用一个QStackedWidget来管理仅在切换到时才进行Web视图的实例化和加载。1.2 精细化配置Profile与缓存策略QWebEngineProfile是管理浏览器会话、Cookie、缓存等设置的枢纽。默认情况下所有QWebEnginePage共享一个全局的QWebEngineProfile::defaultProfile()。但在以下场景你需要创建独立的Profile多用户会话应用支持多账号登录每个账号需要独立的Cookie和本地存储。隐私模式需要一个不持久化任何数据的临时会话。特殊缓存需求某个页面需要巨大的磁盘缓存而其他页面则希望禁用缓存。// 创建一个持久化存储的独立Profile QWebEngineProfile *persistentProfile new QWebEngineProfile(MyAppProfile, this); persistentProfile-setPersistentCookiesPolicy(QWebEngineProfile::ForcePersistentCookies); persistentProfile-setPersistentStoragePath(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) /webdata); persistentProfile-setHttpCacheType(QWebEngineProfile::DiskHttpCache); // 使用磁盘缓存 persistentProfile-setCachePath(persistentProfile-persistentStoragePath() /cache); // 创建一个内存缓存的Profile适合临时会话 QWebEngineProfile *memoryCacheProfile new QWebEngineProfile(MemoryProfile, this); memoryCacheProfile-setHttpCacheType(QWebEngineProfile::MemoryHttpCache); // 创建一个无痕/隐私Profile QWebEngineProfile *offTheRecordProfile new QWebEngineProfile(this); // 不传名字即为OffTheRecord // OffTheRecord Profile会自动使用内存缓存且不保存任何数据到磁盘 // 将Profile应用到Page QWebEnginePage *page1 new QWebEnginePage(persistentProfile, this); QWebEngineView view1; view1.setPage(page1); QWebEnginePage *page2 new QWebEnginePage(offTheRecordProfile, this); QWebEngineView view2; view2.setPage(page2);通过精细化管理Profile你可以有效控制应用的磁盘占用、隔离数据并提升特定场景下的加载速度。注意每个QWebEngineProfile都会在后台启动一个独立的Browser进程。创建过多Profile会增加进程开销。通常根据业务逻辑创建少数几个Profile即可。2. 深度掌控拦截、修改与响应网络活动现代Web应用离不开网络请求。QWebEngine提供了强大的钩子允许你在请求发出前、响应返回后介入整个过程实现诸如广告过滤、请求头修改、本地资源映射、API签名等高级功能。2.1 使用QWebEngineUrlRequestInterceptor这是拦截网络请求的主要入口。你需要子类化QWebEngineUrlRequestInterceptor并重写interceptRequest方法。假设我们要实现一个简单的广告拦截器阻止对特定域名如ads.example.com的请求并为所有向api.myapp.com发出的请求添加一个认证头。class MyRequestInterceptor : public QWebEngineUrlRequestInterceptor { Q_OBJECT public: explicit MyRequestInterceptor(QObject *parent nullptr) : QWebEngineUrlRequestInterceptor(parent) {} void interceptRequest(QWebEngineUrlRequestInfo info) override { QUrl url info.requestUrl(); QString host url.host(); // 1. 广告拦截 if (host.contains(ads.) || host.endsWith(.doubleclick.net)) { info.block(true); // 完全阻止此请求 qDebug() Blocked ad request to: url.toString(); return; } // 2. 修改API请求头 if (host api.myapp.com) { info.setHttpHeader(Authorization, Bearer m_authToken.toUtf8()); info.setHttpHeader(X-Client-Version, MyApp/1.0); } // 3. 将特定路径映射到本地文件开发环境调试 if (host local.dev url.path().startsWith(/assets/)) { QString localPath file:// QCoreApplication::applicationDirPath() /debug_assets url.path(); info.redirect(QUrl(localPath)); } } private: QString m_authToken your_token_here; }; // 在应用中使用 MyRequestInterceptor *interceptor new MyRequestInterceptor(this); QWebEngineProfile::defaultProfile()-setUrlRequestInterceptor(interceptor);QWebEngineUrlRequestInfo对象提供了丰富的控制选项方法作用应用场景block(bool)阻止请求广告过滤、家长控制redirect(const QUrl)重定向到新URL本地资源代理、开发环境模拟setHttpHeader(name, value)设置/修改HTTP请求头添加认证信息、自定义UAresourceType()获取资源类型主文档、图片、脚本等精细化控制如只拦截图片2.2 监听网络回复与错误处理除了拦截请求你还可以监听网络回复。这需要通过QWebEnginePage的certificateError信号来处理SSL证书错误或者通过重写QWebEnginePage::acceptNavigationRequest来更精细地控制导航。class MyWebPage : public QWebEnginePage { Q_OBJECT public: MyWebPage(QWebEngineProfile *profile, QObject *parent nullptr) : QWebEnginePage(profile, parent) { // 连接证书错误信号 connect(this, QWebEnginePage::certificateError, [this](const QWebEngineCertificateError error){ qWarning() Certificate error for error.url().toString() Error: error.errorDescription(); // 对于开发环境自签名证书可以选择忽略 if (error.url().host().contains(dev.internal)) { error.defer(); // 延迟处理 QTimer::singleShot(0, this, [error]() mutable { error.ignoreCertificateError(); // 忽略错误 }); } else { // 对于生产环境通常应该拒绝 error.rejectCertificate(); } }); } protected: // 控制是否允许导航例如禁止打开新窗口 bool acceptNavigationRequest(const QUrl url, QWebEnginePage::NavigationType type, bool isMainFrame) override { if (type QWebEnginePage::NavigationTypeLinkClicked !isMainFrame) { // 阻止非主框架的链接点击例如iframe内的广告链接 return false; } // 可以在此处记录或分析所有导航请求 qDebug() Navigating to: url Type: type; return QWebEnginePage::acceptNavigationRequest(url, type, isMainFrame); } };3. 双向通信的艺术WebChannel实战进阶WebChannel是Qt实现C与网页JavaScript双向通信的官方桥梁。基础用法是暴露一个QObject给JS调用。但在复杂应用中我们需要更健壮和灵活的方案。3.1 建立稳定的通信连接与生命周期管理通信失败的一个常见原因是对象生命周期不同步。C对象被销毁后JS端仍持有引用并尝试调用会导致错误。// 通信桥梁类 class AppBridge : public QObject { Q_OBJECT Q_PROPERTY(QString user READ user WRITE setUser NOTIFY userChanged) public: explicit AppBridge(QObject *parent nullptr) : QObject(parent) {} QString user() const { return m_user; } void setUser(const QString user) { if (m_user ! user) { m_user user; emit userChanged(m_user); } } Q_INVOKABLE void sendData(const QVariant data) { qDebug() Received from JS: data; // 处理数据... emit dataReceived(data); } signals: void userChanged(const QString user); void dataReceived(const QVariant data); void command(const QString cmd, const QVariantMap ¶ms); public slots: void executeCommand(const QString cmd, const QVariantMap ¶ms {}) { emit command(cmd, params); } private: QString m_user; }; // 在Page中设置WebChannel MyWebPage::MyWebPage(QWebEngineProfile *profile, QObject *parent) : QWebEnginePage(profile, parent) { m_bridge new AppBridge(this); // Page作为父对象生命周期绑定 m_channel new QWebChannel(this); m_channel-registerObject(QStringLiteral(appBridge), m_bridge); this-setWebChannel(m_channel); // 注入初始化脚本确保WebChannel JS API可用 QString initScript R( // 等待Qt WebChannel准备好 new Promise((resolve) { if (typeof qt ! undefined) { resolve(); } else { document.addEventListener(DOMContentLoaded, resolve); } }).then(() { new QWebChannel(qt.webChannelTransport, function(channel) { window.appBridge channel.objects.appBridge; console.log(WebChannel initialized); // 通知C端JS已就绪 if (window.appBridge window.appBridge.jsReady) { window.appBridge.jsReady(); } }); }); ); this-runJavaScript(initScript); }在网页的JavaScript中你可以安全地调用// 调用C方法 appBridge.sendData({ key: value }); // 监听C信号 new QWebChannel(qt.webChannelTransport, function(channel) { var bridge channel.objects.appBridge; bridge.userChanged.connect(function(newUser) { console.log(User changed to:, newUser); }); bridge.command.connect(function(cmd, params) { console.log(Command from C:, cmd, params); }); });3.2 处理复杂数据类型与异步调用WebChannel支持QVariant与JavaScript类型的自动转换包括列表和字典。对于异步操作例如C端需要执行一个耗时任务后返回结果给JS可以使用QFutureWatcher或信号槽。// 在AppBridge中增加一个异步方法 Q_INVOKABLE void fetchAsyncData(const QString query) { // 模拟异步操作 QTimer::singleShot(1000, this, [this, query](){ QVariantMap result; result[query] query; result[data] QStringList() Result1 Result2 Result3; result[timestamp] QDateTime::currentDateTime().toString(); // 通过信号将结果发送给JS emit asyncDataFetched(result); }); }在JS端你可以这样调用并监听结果appBridge.fetchAsyncData(search term); appBridge.asyncDataFetched.connect(function(result) { console.log(Async data received:, result); // 更新UI... });4. 性能调优与内存管理实战基于Chromium的QWebEngine功能强大但也是内存消耗大户。不当的使用会导致内存泄漏和性能下降。4.1 监控与诊断内存使用首先你需要知道内存用在了哪里。Qt本身提供的信息有限但我们可以结合一些方法和经验来定位。使用QWebEngineProfile的缓存控制如前所述合理设置HttpCacheType和PersistentStoragePath。观察进程QWebEngine采用多进程架构。在任务管理器或htop中你会看到多个进程Browser, Renderer, GPU等。Renderer进程的数量和内存占用是重点观察对象。在页面关闭时进行清理当一个QWebEnginePage不再需要时确保删除它。如果该Page有独立的Profile也需要考虑删除Profile。// 在标签页关闭时清理 void MyMainWindow::closeTab(int index) { QWidget *widget m_tabWidget-widget(index); if (auto *view qobject_castQWebEngineView*(widget)) { QWebEnginePage *page view-page(); // 1. 停止页面加载 page-triggerAction(QWebEnginePage::Stop); // 2. 断开所有可能保持页面存活的连接如WebChannel对象 // 3. 删除ViewPage会因其父对象被删除而自动删除如果Page的父对象是View // 如果Page有独立Profile且不再需要也需要删除 delete view; } m_tabWidget-removeTab(index); }4.2 针对性的优化策略懒加载与视图复用对于非活动标签页或隐藏的Web内容不要提前加载。对于频繁切换的视图考虑复用QWebEnginePage实例而不是反复创建和销毁。限制JavaScript执行对于仅展示内容的页面可以考虑通过QWebEngineSettings禁用JavaScript但这会牺牲交互性。图片与媒体控制QWebEngineSettings *settings page-settings(); settings-setAttribute(QWebEngineSettings::AutoLoadImages, true); // 默认true settings-setAttribute(QWebEngineSettings::JavascriptEnabled, true); // 可以考虑禁用插件和自动播放 settings-setAttribute(QWebEngineSettings::PluginsEnabled, false); // 通过CSS注入限制最大图片尺寸这需要更复杂的拦截和修改响应内容。使用轻量级Profile对于不需要持久化、不需要复杂功能的页面如帮助文档、关于页面使用OffTheRecord的Profile。5. 界面集成与用户体验增强将Web内容无缝集成到原生Qt界面中并提升用户交互体验是桌面应用开发的关键。5.1 自定义上下文菜单与快捷键默认的网页右键菜单可能不符合你的应用风格或者你想增加一些自定义选项。class CustomWebPage : public QWebEnginePage { protected: // 重写创建上下文菜单的方法 QMenu *createStandardContextMenu() override { QMenu *menu QWebEnginePage::createStandardContextMenu(); // 获取标准菜单 // 移除不需要的项 QListQAction* actions menu-actions(); for (QAction *action : actions) { // 例如移除“查看网页源代码”、“检查元素”开发者工具 if (action-text().contains(tr(View source)) || action-text().contains(tr(Inspect))) { menu-removeAction(action); } } // 添加自定义项 menu-addSeparator(); QAction *customAction menu-addAction(tr(Translate with MyApp...)); connect(customAction, QAction::triggered, this, CustomWebPage::onCustomAction); // 添加一个禁用/启用JavaScript的切换项 QAction *toggleJsAction menu-addAction(tr(Disable JavaScript)); toggleJsAction-setCheckable(true); toggleJsAction-setChecked(!this-settings()-testAttribute(QWebEngineSettings::JavascriptEnabled)); connect(toggleJsAction, QAction::toggled, [this](bool checked){ this-settings()-setAttribute(QWebEngineSettings::JavascriptEnabled, !checked); // 可能需要重新加载页面使设置生效 // this-triggerAction(QWebEnginePage::Reload); }); return menu; } private slots: void onCustomAction() { // 获取选中的文本 this-runJavaScript(window.getSelection().toString();, [this](const QVariant result){ QString selectedText result.toString(); if (!selectedText.isEmpty()) { qDebug() Selected text for translation: selectedText; // 调用本地翻译服务... } }); } };5.2 处理文件上传与下载网页中的文件上传对话框和下载请求需要由你的应用来处理以提供更好的集成体验。// 处理文件选择请求上传 CustomWebPage::CustomWebPage(QWebEngineProfile *profile, QObject *parent) : QWebEnginePage(profile, parent) { connect(this, QWebEnginePage::fileSelectionRequested, this, CustomWebPage::handleFileSelection); } void CustomWebPage::handleFileSelection(QWebEngineFileSelectionRequest request) { // 使用Qt原生文件对话框 QStringList fileNames; if (request.multipleFiles()) { fileNames QFileDialog::getOpenFileNames(nullptr, tr(Select Files), QDir::homePath()); } else { QString fileName QFileDialog::getOpenFileName(nullptr, tr(Select File), QDir::homePath()); if (!fileName.isEmpty()) { fileNames fileName; } } if (!fileNames.isEmpty()) { request.selectFiles(fileNames); // 确认选择 } else { request.cancel(); // 取消 } } // 处理下载请求 // 需要在Profile上设置下载路径和处理器 QWebEngineProfile *profile ...; profile-setDownloadPath(QStandardPaths::writableLocation(QStandardPaths::DownloadLocation)); profile-setHttpCacheType(QWebEngineProfile::DiskHttpCache); connect(profile, QWebEngineProfile::downloadRequested, [](QWebEngineDownloadRequest *download) { qDebug() Download started: download-url() to download-downloadFileName(); // 你可以在这里修改下载路径、暂停、取消或监听进度 connect(download, QWebEngineDownloadRequest::downloadProgress, [](qint64 bytesReceived, qint64 bytesTotal) { qDebug() Progress: bytesReceived / bytesTotal; }); connect(download, QWebEngineDownloadRequest::finished, []() { qDebug() Download finished; }); // 接受下载开始下载 download-accept(); });通过这五个方面的实战技巧你可以更从容地应对Qt WebEngine集成中的各种挑战。从资源管理的精细化到网络层的深度控制再到稳定高效的双向通信每一步的优化都能让你的应用更加健壮和流畅。记住关键在于理解其背后的Chromium架构思想——多进程、沙盒化、异步通信并在此基础上用Qt的方式去驾驭它。在实际项目中多使用调试工具观察进程和内存变化结合具体业务逻辑进行取舍和优化才能真正发挥出QWebEngine的强大威力。

相关新闻

270°数字舵机控制全解析:用STM32 HAL库实现精准角度映射(附脉冲宽度对照表)

270°数字舵机控制全解析:用STM32 HAL库实现精准角度映射(附脉冲宽度对照表)

270数字舵机控制实战:从脉冲映射到STM32 HAL库的精准驱动 最近在做一个机械臂的小项目,选用了270行程的数字舵机作为关节驱动。本以为按照数据手册上的脉冲宽度参数配置好PWM就能轻松搞定,结果上电一试,舵机的转动角度和预期差了十…

2026/5/17 9:02:03 阅读更多 →
科研效率翻倍!Python+BeautifulSoup实现arXiv论文自动抓取与PDF下载(附完整代码)

科研效率翻倍!Python+BeautifulSoup实现arXiv论文自动抓取与PDF下载(附完整代码)

科研效率革命:用Python构建你的arXiv智能论文管家 如果你也曾在深夜对着arXiv上几十篇新论文的列表发愁,手指在“Download PDF”按钮上机械地重复点击,那么这篇文章就是为你准备的。在快节奏的学术研究领域,时间是最稀缺的资源。每…

2026/7/5 7:34:28 阅读更多 →
Qlib国内数据源配置指南:从命令行下载到本地数据替换(附CN市场数据获取)

Qlib国内数据源配置指南:从命令行下载到本地数据替换(附CN市场数据获取)

Qlib数据源深度定制:从云端数据获取到本地私有数据无缝集成实战 如果你已经开始用Qlib做一些策略回测,大概率已经跑过那个经典的get_data.py脚本,下载了官方提供的中国市场数据。这确实是个不错的起点,能让你快速上手体验Qlib的整…

2026/7/5 19:20:44 阅读更多 →

最新新闻

ICM-42688-P与PIC24HJ256GP610在运动控制中的高效协同

ICM-42688-P与PIC24HJ256GP610在运动控制中的高效协同

1. ICM-42688-P与PIC24HJ256GP610的黄金组合解析在工业自动化和机器人控制领域,传感器与微控制器的协同工作能力直接决定了系统性能的上限。ICM-42688-P作为TDK InvenSense推出的6轴MEMS运动传感器,与Microchip的PIC24HJ256GP610微控制器形成的技术组合&…

2026/7/6 13:07:53 阅读更多 →
STM32与TPAFE0808实现多通道信号采集方案解析

STM32与TPAFE0808实现多通道信号采集方案解析

1. 项目背景与核心需求在工业自动化、医疗设备和精密仪器领域,多通道信号采集与系统监测一直是关键的技术挑战。传统方案往往需要复杂的模拟前端电路和多个分立元件,不仅增加了系统复杂度,还容易引入噪声和漂移问题。TPAFE0808这款8通道可编程…

2026/7/6 13:07:53 阅读更多 →
Unity MyFramework:框架里的代码简化技巧(三)

Unity MyFramework:框架里的代码简化技巧(三)

前两篇写了 MyFramework 里一些常用的代码简化技巧。 这一篇继续写第三组。 不过这次只写真正能让调用侧代码变短的东西。 也就是一眼能看到: 原本要写几行,现在一行就能表达清楚。 这类工具不是复杂系统。 它们更多是项目长期开发中沉淀出来的小函…

2026/7/6 13:03:49 阅读更多 →
终极指南:用DistroAV实现专业NDI网络音视频传输的完整方案

终极指南:用DistroAV实现专业NDI网络音视频传输的完整方案

终极指南:用DistroAV实现专业NDI网络音视频传输的完整方案 【免费下载链接】obs-ndi DistroAV (formerly OBS-NDI): NDI integration for OBS Studio 项目地址: https://gitcode.com/gh_mirrors/ob/obs-ndi DistroAV(原名OBS-NDI)是一…

2026/7/6 13:03:49 阅读更多 →
AD 23 与 AutoCAD 2024 协同:DXF 板框导入的 5 项预处理与 1 个精度保障技巧

AD 23 与 AutoCAD 2024 协同:DXF 板框导入的 5 项预处理与 1 个精度保障技巧

AD 23与AutoCAD 2024高效协同:DXF板框导入的预处理全流程与精度控制实战在硬件开发领域,PCB工程师与结构工程师的协同效率直接影响产品开发周期。当结构工程师使用AutoCAD 2024完成机箱设计后,如何确保其提供的DXF文件能够精准导入Altium Des…

2026/7/6 12:57:42 阅读更多 →
测试转大模型:AI 测试工程师的能力跃迁,把学习路线落到项目证据

测试转大模型:AI 测试工程师的能力跃迁,把学习路线落到项目证据

《测试转大模型:AI 测试工程师的能力跃迁,把学习路线落到项目证据》看起来是个大话题,但真落到项目里,常常就是几个具体选择。下面我尽量按实际开发时会遇到的问题来讲。摘要这篇面向想进入 AI 测试和质量工程方向的软件测试工程师…

2026/7/6 12:55:41 阅读更多 →

日新闻

H2 与 MySQL 单元测试兼容性:5 个关键 SQL 语句差异与规避方案

H2 与 MySQL 单元测试兼容性:5 个关键 SQL 语句差异与规避方案

H2与MySQL单元测试兼容性:5个关键SQL语句差异与规避方案1. 单元测试中的数据库兼容性挑战在Java开发领域,单元测试是保证代码质量的重要环节。当应用涉及数据库操作时,测试环境的搭建往往成为开发者的痛点。H2数据库因其轻量级、内存模式和快…

2026/7/6 0:01:17 阅读更多 →
Windows任务栏终极清理指南:用RBTray一键隐藏窗口到系统托盘

Windows任务栏终极清理指南:用RBTray一键隐藏窗口到系统托盘

Windows任务栏终极清理指南:用RBTray一键隐藏窗口到系统托盘 【免费下载链接】rbtray A fork of RBTray from http://sourceforge.net/p/rbtray/code/. 项目地址: https://gitcode.com/gh_mirrors/rb/rbtray 你是否厌倦了Windows任务栏上密密麻麻的图标&…

2026/7/6 0:01:17 阅读更多 →
Visual C++ 运行时库一键安装终极指南:告别DLL缺失烦恼

Visual C++ 运行时库一键安装终极指南:告别DLL缺失烦恼

Visual C 运行时库一键安装终极指南:告别DLL缺失烦恼 【免费下载链接】vcredist AIO Repack for latest Microsoft Visual C Redistributable Runtimes 项目地址: https://gitcode.com/gh_mirrors/vc/vcredist 你是否曾经遇到过这样的情况:下载了…

2026/7/6 0:05:19 阅读更多 →

周新闻

B站视频下载神器BiliTools:5分钟学会轻松保存任何B站内容

B站视频下载神器BiliTools:5分钟学会轻松保存任何B站内容

B站视频下载神器BiliTools:5分钟学会轻松保存任何B站内容 【免费下载链接】BiliTools A cross-platform bilibili toolbox. 跨平台哔哩哔哩工具箱,支持下载视频、番剧等等各类资源 项目地址: https://gitcode.com/GitHub_Trending/bilit/BiliTools …

2026/7/6 8:11:50 阅读更多 →
威胁模型全解析:从新手入门到实战应用,助你构建安全产品!

威胁模型全解析:从新手入门到实战应用,助你构建安全产品!

威胁模型的陌生现状在忙碌疲惫的一天里,参与了关于混合后量子密码学的讨论,应付端点攻击找茬的人,还参与留言板讨论后,发现“威胁模型”对多数人仍是陌生概念,且多被当作时髦用语。有趣的相关画作有一幅由 Embyr 创作的…

2026/7/6 8:11:52 阅读更多 →
渗透测试入门指南:从零基础到实战环境搭建

渗透测试入门指南:从零基础到实战环境搭建

1. 从“看热闹”到“入门”:我理解的渗透测试到底是什么?每次看到新闻里说某个大公司的数据被“黑”了,或者某个网站被攻击导致服务瘫痪,你是不是和我一样,心里会冒出两个念头:一是“这黑客真厉害”&#x…

2026/7/6 6:52:56 阅读更多 →

月新闻