首页/新闻资讯/正文详情

Cyrus SASL 2.1.21源码编译与认证服务部署实践

发布时间:2026/9/25 5:35:14 来源:云帆数科 栏目:资讯中心
Cyrus SASL 2.1.21源码编译与认证服务部署实践
简介Cyrus SASL 2.1.21 是一套面向邮件服务场景的开源认证与安全层库主要服务于 Postfix 等 MTA 的运维人员、邮件系统管理员以及需要对接 SMTP/IMAP/POP3 认证的开发者。它内置 PLAIN、CRAM-MD5、DIGEST-MD5 等多种安全机制可有效防范中间人攻击与未授权访问是构建安全邮件链路的关键组件。这份源码压缩包共包含 620 个文件以 155 个 C 源码、138 个头文件为主便于阅读和二次编译同时附带大量 autotools 配置脚本、makefile、构建辅助文件与 man 手册便于在 Linux/FreeBSD 等环境下完成定制化编译和集成。整体体积仅 1.51MB虽然小巧但源码、文档与配置样例一应俱全。目前已有 390 人学习下载该资源尤其适合正在为 Postfix 部署 SASL 认证、排查认证异常或希望深入理解 SASL 框架实现的读者。借助源码结构、函数接口手册和模块化目录可以快速理清认证机制的选择与调用流程为二次开发、安全加固或邮件系统排错提供直接参考。1. cyrus-sasl-2.1.21.tar.gz一个老认证库为什么值得手动编译一次第一次碰上“cyrus-sasl”这个词往往不是在安装界面而是在排障现场Postfix 的 SMTP 认证突然报SASL authentication failureOpenLDAP 的 GSSAPI 绑定连不上自研服务想接统一账号体系却不知道从哪儿下手。翻文档翻到最后所有线索都指向同一个底层库——Cyrus SASL。而cyrus-sasl-2.1.21.tar.gz这个源码包正是 2.1 分支里一个被大量生产环境长期使用的版本坐标。它不是功能最全的但很多旧系统的编译脚本、运维手册都锁在这个版本上换版本意味着整条认证链路要重新回归测试。这篇内容给两类人看一类是在 RHEL 系老系统、国产化操作系统或嵌入式环境里手工编译依赖库的运维另一类是接手旧项目、想弄明白旧构建脚本里每个参数为什么那样写的开发。后面所有内容只围绕一件事把这个 tar.gz 从解压、编译、装库到机制插件真正被业务调用这条路径完整走通顺带交代哪些开关不能随手乱改。2. 动手编译前先摸清 cyrus-sasl 源码结构和版本边界2.1 解压前先做两件事校验和与目录确认拿到cyrus-sasl-2.1.21.tar.gz直接tar -xzf是最常见的做法但我在内网传输环境下吃过文件被截断的亏。压缩包看起来能解压解压到一半报unexpected EOF或者某个头文件少了几行后续 configure 阶段出现莫名其妙的状态。所以现在拿到 tar.gz第一步永远是校验和。cd /data/build sha256sum cyrus-sasl-2.1.21.tar.gz checksum.txt tar -tzf cyrus-sasl-2.1.21.tar.gz | head -30sha256sum的比对值要和发布页给的一致。如果不一致先别解压八成是下载过程出了问题重新拉一次更省时间。tar -tzf只看不解列出包内文件清单重点确认所有文件是不是都包在cyrus-sasl-2.1.21/这个统一前缀之下。如果解压出来直接是散落的目录会污染当前构建目录这是后面一连串奇怪问题的源头。校验通过后再解压tar -xzf cyrus-sasl-2.1.21.tar.gz cd cyrus-sasl-2.1.21这里顺手回答一个高频疑问tar.gz 到底怎么解压。-x是解压-z表示 gzip 压缩-f指定文件名三个参数组合成tar -xzf对绝大多数源码包都适用。解压后先看一眼目录里是不是有configure和Makefile.in有这两个文件才说明是 autotools 标准的源码发布包。2.2 源码目录里每个目录是干什么的进入cyrus-sasl-2.1.21之后先把几个关键目录对上号后面排查问题时会反复用到。include/对外的 API 头文件都在这里。sasl.h是最核心的业务代码通过#include sasl/sasl.h就能调用客户端和服务端接口saslplug.h是写自定义机制插件时才需要碰的。lib/库主体源码编译后产出libsasl2.so。它负责机制调度、配置文件加载、回调分发这些底层工作。plugins/每个认证机制一个子目录比如plain、login、crammd5、digestmd5、gssapi各自编译成一个动态插件库。业务能不能用某种机制取决于这里编译出来了什么。utils/管理工具源码saslpasswd2用来维护 sasldb 用户sasldblistusers2查看用户列表saslpluginviewer查看当前加载了哪些插件。sasldb/用户数据库读写层默认后端是 BerkeleyDB。configure 时也能指定 ndbm、lmdb这个选择决定了用户数据最终落在哪种格式的文件里。doc/RFC 实现对照和运维说明遇到机制协商问题时要回来翻。还要留意配置目录这个角色。Cyrus SASL 的应用配置叫smtpd.conf、openldap.conf这类以应用名命名的文件统一放在 configure 指定的目录里。这里有两个容易混淆的路径--sysconfdir决定配置文件安装到哪里--with-configdir决定库运行时去哪个目录查找配置文件。两个参数没对齐就会出现“配置写了但服务不读”的怪事。2.3 版本对比2.1.21 该不该继续用很多人第一反应是问为什么不直接装 2.1.26 或者 2.2.x。我用一张表把取舍说清楚对比对象与 2.1.21 的差异什么情况下坚持用 2.1.212.1.26 / 2.1.27修复了 DIGEST-MD5 和 SASLprep 相关实现问题GSSAPI 插件在某些平台的回调行为有调整生产环境已稳定运行多年升级后面临机制协商行为变化需要重新回归测试2.2.x部分内部 API 和插件接口变更有自己维护的基于 2.1 API 的机制插件升到 2.2 需要改代码重编发行版自带 cyrus-sasl 包编译参数和 sasldb 后端由发行版决定不受你控制需要精确复现旧环境行为或业务指定了某一版编译参数说白了坚持用 2.1.21 通常不是因为新特性而是因为老项目的构建脚本锁死了这个版本或者目标机器的 glibc、openssl 兼容性停留在那个年代。编译这类老版本心理预期应该是“稳定复现”而不是“获得新能力”。2.4 编译前依赖准备按认证机制决定不盲目装全家桶依赖清单要从最终用途倒推Cyrus SASL 的机制分成几类每类依赖不一样只用 PLAIN 和 LOGIN基本不需要额外依赖libc 就够。用 CRAM-MD5、DIGEST-MD5需要 OpenSSL 的libcrypto开发头文件和库。用 GSSAPI需要 MIT Kerberos 的krb5-devel。用 sasldb 存用户需要 BerkeleyDB 开发包或 configure 时换成--with-dblibndbm。用 saslauthd 对接系统账号可以选 PAM 作为认证后端需要pam-devel。在 RHEL 兼容发行版上的安装命令通常是sudo yum install -y gcc make openssl-devel krb5-devel pam-devel db4-develdb4-devel这个包在某些新发行版里改名叫libdb-devel或干脆不提供了。看到找不到包时说明需要按发行版的包名规则调整不必死守这一条命令。2.5 configure 的哲学哪些参数不能交给自动探测autotools 项目喜欢“自动探测”但 Cyrus SASL 的自动探测在缺依赖时经常静默禁用某个机制而不是报错退出。这就是很多人编译完了才发现 GSSAPI 插件没出来的原因。下面这张表是编译前必须理解的参数清单参数作用不显式指定的后果--prefix/opt/cyrus-sasl安装根目录默认/usr/local后续升级或并行装版本容易冲突--sysconfdir/etc/sasl2应用配置文件的安装目录配置文件落在 prefix/etc 下业务代码可能找不到--with-configdir/etc/sasl2库运行时查找配置的目录和 sysconfdir 不一致时改了配置不生效--with-plugindir/opt/cyrus-sasl/lib/sasl2机制插件安装目录默认跟随 prefix换前缀后插件位置会变--with-saslauthd/var/run/saslauthdsaslauthd 的 socket 路径自动探测的值和 init 脚本不一致连接失败--with-dblibberkeleysasldb 数据库后端自动探测可能选到不存在的后端运行时才暴露--enable-auth-sasldb开放 sasldb 认证能力默认不开PLAIN 无法直接查用户库--enable-gssapi编译 GSSAPI 机制插件缺 krb5 头文件时自动禁用且不报错--enable-login、--enable-plain单独开某些简单机制默认按 configure 探测结果决定未必全开这些参数看着多其实核心逻辑就一条不要把关键行为交给探测显式指定每个路径和开关让最终环境可预期。3. 编译并安装到指定前缀一套可以照抄的最小落地路径3.1 完整构建脚本把上一章的参数落成一段可以直接执行的脚本。下面这套以/opt/cyrus-sasl作为独立前缀避免污染系统默认目录也方便日后卸载。#!/usr/bin/env bash # cyrus-sasl-2.1.21 编译脚本源码根目录为 /data/build/cyrus-sasl-2.1.21 set -euo pipefail SRC/data/build/cyrus-sasl-2.1.21 PREFIX/opt/cyrus-sasl cd $SRC # 清掉上次失败构建可能留下的 config.cache 和 Makefile make distclean /dev/null 21 || true ./configure \ --prefix$PREFIX \ --sysconfdir/etc/sasl2 \ --with-configdir/etc/sasl2 \ --with-plugindir$PREFIX/lib/sasl2 \ --with-saslauthd/var/run/saslauthd \ --with-dblibberkeley \ --enable-auth-sasldb \ --enable-login \ --enable-plain \ --enable-crammd5 \ --enable-digestmd5 \ --enable-gssapi make -j$(nproc) make install脚本的逻辑说明set -euo pipefail保证任何一个命令失败就立即退出不会带着残缺的 configure 结果继续往下编译make distclean是为了清掉前一次失败构建留下的config.cache这个文件残留会导致明明改了参数configure 却依旧沿用旧值。参数层面的解读--prefix选/opt/cyrus-sasl而不是/usr/local是为了和系统自带的 SASL 库隔离将来要卸掉或切换版本时直接删目录。--sysconfdir和--with-configdir必须指向同一个/etc/sasl2这两个不一致是配置不生效的最常见来源。--with-plugindir显式指定插件目录不跟随 prefix 隐式变化。--with-saslauthd把 socket 路径固定下来后面启动 saslauthd 服务时要以这个路径为准。--with-dblibberkeley明确数据库后端防止自动探测选错。--enable-login、--enable-plain这些机制开关按需开。生产环境如果只用 PLAIN就把 CRAM-MD5、DIGEST-MD5、GSSAPI 关掉插件数量和攻击面都更小。我上面列这些主要是为了展示全开是方便测试不是生产推荐配置。3.2 动态库路径和配置目录的两次确认编译安装成功不等于运行时能找到库。libsasl2.so装在/opt/cyrus-sasl/lib下但系统默认的库搜索路径里没有这个目录必须让 ldconfig 认出来。echo /opt/cyrus-sasl/lib /etc/ld.so.conf.d/cyrus-sasl.conf ldconfig ldconfig -p | grep sasl执行完后ldconfig -p的输出里应该能看到libsasl2.so.3的身影。看不到就说明配置文件没生效或者目录写错了。接下来确认插件和配置目录ls -l /opt/cyrus-sasl/lib/sasl2/ ls -l /etc/sasl2/这里要留意插件目录里必须有.so文件配置目录里至少要有一个应用命名的 conf 文件才会被加载。两个目录的权限也要检查服务运行用户对配置目录至少要有读权限。3.3 用 saslauthd 跑通一次密码校验如果业务要对接系统账号或 PAM走 saslauthd 是最直接的方式。先在/etc/sasl2/下建一个应用配置以 Postfix 的 SMTP 认证为例文件名必须是smtpd.conf# /etc/sasl2/smtpd.conf pwcheck_method: saslauthd mech_list: PLAIN LOGIN saslauthd_path: /var/run/saslauthd/mux log_level: 3参数含义pwcheck_method指定密码校验方式这里用 saslauthdmech_list限制对外协商的机制避免暴露不必要的认证方式saslauthd_path对应编译时的--with-saslauthd参数log_level调到 3 让问题阶段能看到更多日志。接着启动 saslauthd 并做一次测试systemctl enable saslauthd systemctl start saslauthd testsaslauthd -u testuser -p secrettestsaslauthd是 cyrus-sasl 自带的测试工具输出OK说明 saslauthd 链路通NO则要去查系统日志或/var/log/messages。这一步通过之后Postfix 这类调用方只要把认证请求指向这个 socket 即可。3.4 用 sasldb 兜底没有外部认证源时自建用户库有些内网环境没有 LDAP、没有 PAM这时候直接用 sasldb 存密码最省事。注意前提是 configure 时开了--enable-auth-sasldb并且--with-dblib选了实际存在的后端。/opt/cyrus-sasl/bin/saslpasswd2 -c -u example.com testuser /opt/cyrus-sasl/bin/sasldblistusers2saslpasswd2 -c创建用户-u指定 realm没有指定 realm 时默认取主机名这点在业务对接时很容易踩坑。sasldblistusers2用来验证用户是否真的写进去了。还要确认数据库文件的属主root 创建的库文件用服务进程身份去读可能没权限这是下一章要展开的坑。4. 避坑cyrus-sasl-2.1.21 编译部署中的五个翻车点4.1 坑一configure 提示找不到 openssl 头文件但系统明明装了 openssl现象configure 输出停在checking for EVP_md5... no随后报libcrypto not found。原因系统装了 openssl 运行库但缺少openssl-devel开发包头文件根本没装全。另一个常见原因是机器上存在多个 openssl 版本pkg-config 优先指到了一个不兼容的 include 路径。解决先补开发包再显式指定 openssl 路径sudo yum install -y openssl-devel ./configure --with-openssl/usr/include/openssl注意 2.1.21 的代码比较老在 OpenSSL 3.x 上编译可能触发EVP_MD_CTX相关的不兼容。我一般会在 old system 上尽量用它自带的 openssl 开发包而不是强行统一到新版本。4.2 坑二make 阶段报 yacc 或 lex 不存在现象编译lib/目录时报yacc: command not found或者提示parse.c生成失败。原因Cyrus SASL 2.1.x 的部分解析器依赖 bison/flex 重新生成最小化安装的 Linux 默认不带这两个工具。解决补装后重新编译sudo yum install -y bison flex make clean make -j$(nproc)注意顺序make clean而不是make distclean因为 configure 已经跑完了只需要清掉编译中间产物。4.3 坑三配置目录不一致改了 smtpd.conf 不生效现象/etc/sasl2/smtpd.conf里的mech_list改了半天服务端日志依然显示generic failure机制列表没变化。原因应用在它的默认路径找配置而库又在另一个路径找配置。最常见的是编译时--sysconfdir设了/etc/sasl2但应用自己的 SASL 配置路径指向/usr/lib/sasl2两边根本不读同一个文件。解决先用strace或查看应用文档确认应用实际查找的配置目录再把编译参数对齐strace -f -e openat -o /tmp/sasl_trace.log 启动命令然后看/tmp/sasl_trace.log里smtpd.conf的实际查找路径。对齐后重新 configure 一遍不要试图用软链接绕过路径不统一迟早还会出问题。4.4 坑四sasldb 建的用户在服务进程里认证失败现象root 在终端执行saslpasswd2 -c -u example.com testuser成功但服务进程用同一套密码报SASL database is unavailable。原因sasldb 主文件默认生成在/etc/sasldb2root 建的库权限通常只有 root 可读写服务进程以sasl或postfix用户身份跑读不到也写不了。解决检查属主和权限并考虑把数据库文件放到服务用户可写的路径chown root:sasl /etc/sasldb2 chmod 660 /etc/sasldb2如果路径也要改用saslpasswd2 -d /var/lib/sasl/sasldb2 -c -u example.com testuser重新建库同时确认编译时的--with-dblib后端和实际文件格式一致。BerkeleyDB 后端和 LMDB 后端生成的库文件是不兼容的混用会直接读不了。4.5 坑五在 Rocky Linux、麒麟 V10 这类新发行版上编译老源码包运行时意外失败现象configure、make、make install 全部成功但服务启动后所有机制都报SASL_FAIL系统日志又没有明显错误。原因老版本源码在较新的 glibc 和编译环境下某些 configure 检测结果和实际头文件不一致比如宏_GNU_SOURCE没有默认定义导致部分结构体或函数声明没被编译器正确识别。解决给 CPPFLAGS 补上宏定义后重新 configure./configure --prefix/opt/cyrus-sasl \ CPPFLAGS-D_GNU_SOURCE \ ... 其他参数照旧 ...麒麟 V10 这类基于 RHEL 生态的系统还容易缺kernel-headers和glibc-headersconfigure 阶段就会表现出异常。最稳妥的办法是先安装发行版的开发工具组再加-D_GNU_SOURCE重编。如果问题依旧就把目光投向同为 2.1 分支的新版本在兼容性和安全修复之间重新做一次取舍。5. 让应用真正用上这套认证库验证工具、最小客户端与调试习惯5.1 三步验证插件加载装完库先别急着接业务用三个命令验证插件状态ls -l /opt/cyrus-sasl/lib/sasl2/ /opt/cyrus-sasl/bin/saslpluginviewer /opt/cyrus-sasl/bin/sasldblistusers2ls看插件文件是否齐全saslpluginviewer能列出当前编译进库的机制列表sasldblistusers2确认 sasldb 用户数据可读。这三个输出和你预期的机制清单对上再进下一步。5.2 写一个最小客户端程序验证机制协商调用业务环境太大我用一个几行的小程序来验机制列表#include sasl/sasl.h #include stdio.h int main(void) { const char *mechs NULL; sasl_conn_t *conn NULL; if (sasl_client_init(NULL) ! SASL_OK) { printf(client init failed\n); return 1; } if (sasl_client_new(testapp, server.example.com, NULL, NULL, NULL, NULL, 0, conn) ! SASL_OK) { printf(client new failed\n); return 1; } sasl_listmech(NULL, NULL, NULL, , NULL, mechs, NULL, NULL); printf(client mechs: %s\n, mechs ? mechs : no mech); sasl_dispose(conn); sasl_client_done(); return 0; }编译运行gcc -o sasl_probe sasl_probe.c -I/opt/cyrus-sasl/include -L/opt/cyrus-sasl/lib -lsasl2 LD_LIBRARY_PATH/opt/cyrus-sasl/lib ./sasl_probe输出里能看到PLAIN LOGIN CRAM-MD5这些名字说明库、插件、动态库路径已经全部打通。这个程序也适合在接业务之前先跑一遍排除“业务代码问题”和“SASL 库问题”的干扰。5.3 我的接入习惯最后说一个长期养成的习惯接到新服务时先把LD_LIBRARY_PATH写进 systemd unit 的Environment里做临时验证确认无误后立刻改成/etc/ld.so.conf.d/下的配置文件并执行ldconfig。这样既能在验证阶段快速切换库版本又能保证服务正式跑起来时不依赖容易被忽略的全局变量。Postfix 的接入配置里记得写smtpd_sasl_type cyrusOpenLDAP 则要确认sasl-host和机制名称的大小写。真到了排查环节第一件事永远是看日志saslauthd 有自己的 log应用也有自己的 log两边时间戳一对比问题基本就能定位到是库加载、配置路径还是密码源的问题。希望这条路径能帮你少走几步弯路。本文还有配套的精品资源点击获取

相关推荐

PTA 7-32 交换实数整数部分:C语言浮点数精度与类型转换避坑指南
PTA 7-32 交换实数整数部分:C语言浮点数精度与类型转换避坑指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/25 5:35:14

医疗支架涂层技术解析与成本优化策略
医疗支架涂层技术解析与成本优化策略

1. 医疗支架涂层行业现状与核心需求2026年的医疗支架涂层加工领域正面临技术迭代的关键期。随着介入治疗普及率提升,国内每年冠状动脉支架植入量已突破150万例,对涂层技术提出了更高要求。目前主流涂层类型包括:药物洗脱涂层(DES&… · 2026/9/25 5:35:14

html-anything 技能模板详解:用 SKILL.md 打造 Spotify Now-Playing 正在播放卡
html-anything 技能模板详解:用 SKILL.md 打造 Spotify Now-Playing 正在播放卡

AI 应用人工智能AI AgentAI 写作媒体生成 【免费下载链接】html-anything ✨ The agentic HTML editor — your local AI agent writes the HTML, you ship it. 🚀 75 Skills 9 Surfaces (magazine deck poster XHS / tweet prototype data report Hyperfram… · 2026/9/25 5:35:08

linuxkit init 中的 TOML 解析底座:go-toml 库的功能、用法与源码导读
linuxkit init 中的 TOML 解析底座:go-toml 库的功能、用法与源码导读

操作系统云原生容器运行时 【免费下载链接】linuxkit A toolkit for building secure, portable and lean operating systems for containers 项目地址: https://gitcode.com/gh_mirrors/li/linuxkit 点击查看 免费下载 本文围绕 linuxkit 仓库中随 pkg/init 服务 … · 2026/9/25 6:00:58

在 Hypothesis 中组合使用 pytest fixtures 与 @given 的完整指南
在 Hypothesis 中组合使用 pytest fixtures 与 @given 的完整指南

测试开发工具 【免费下载链接】hypothesis The property-based testing library for Python 项目地址: https://gitcode.com/gh_mirrors/hy/hypothesis 点击查看 免费下载 导读 pytest 的 fixture 系统与 Hypothesis 的 given 装饰器都是现代 Python 测试中高频使… · 2026/9/25 6:00:58

PaddleNLP FlashMask 灵活注意力掩码完全指南:列式稀疏表示与长序列训练加速实战
PaddleNLP FlashMask 灵活注意力掩码完全指南:列式稀疏表示与长序列训练加速实战

人工智能大模型预训练微调LoRARLHF强化学习分布式训练 【免费下载链接】PaddleNLP Easy-to-use and powerful LLM and SLM library with awesome model zoo. 项目地址: https://gitcode.com/gh_mirrors/pa/PaddleNLP 点击查看 免费下载 本文聚焦 PaddleNLP 中集成的… · 2026/9/25 6:00:58

7 项参数调优让 Motrix 下载提速 50%:新手也能照做的性能实战指南
7 项参数调优让 Motrix 下载提速 50%:新手也能照做的性能实战指南

7 项参数调优让 Motrix 下载提速 50%:新手也能照做的性能实战指南 【免费下载链接】Motrix A full-featured download manager. 项目地址: https://gitcode.com/GitHub_Trending/mo/Motrix Motrix 下载速度优化不必靠猜。Motrix 是内置 Aria2 引擎的下载管理… · 2026/9/25 6:00:58

Read the Docs 构建器安全之 AWS STS 临时凭证:最小化构建产物存储的访问范围
Read the Docs 构建器安全之 AWS STS 临时凭证:最小化构建产物存储的访问范围

后端文档 【免费下载链接】readthedocs.org The source code that powers readthedocs.org 项目地址: https://gitcode.com/gh_mirrors/re/readthedocs.org 点击查看 免费下载 本文基于 Read the Docs 源码仓库中的 aws-temporary-credentials 设计文档 展开&#… · 2026/9/25 6:00:58

第 9 章 H 桥与刹车:反电动势、滑行和刹车的物理
第 9 章 H 桥与刹车:反电动势、滑行和刹车的物理

本章把"刹车"讲到底层物理: ①为什么断电后电机还会溜(惯性 反电动势);②反电动势从哪来 (电磁感应);③滑行和刹车在电路上分别做了什么、为什么刹车停得快; ④能量去哪了… · 2026/9/25 6:00:52

数值优化(Numerical Optimization)学习系列-03-共轭梯度方法(Conjugate Gradient)
数值优化(Numerical Optimization)学习系列-03-共轭梯度方法(Conjugate Gradient)

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/25 1:00:31

创维E900V22D刷机全攻略:S905L3SB芯片兼容性解析与救砖实战
创维E900V22D刷机全攻略:S905L3SB芯片兼容性解析与救砖实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/25 1:00:31

MQTT协议原理与Broker服务器搭建实战:从Mosquitto到EMQX
MQTT协议原理与Broker服务器搭建实战:从Mosquitto到EMQX

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/25 1:00:37

了解更多?预约专属演示

我们的顾问将为您一对一讲解产品与方案

企业微信二维码