
简介面向Windows与Linux运维人员的SSL证书导入脚本工具包聚焦HTTPS通信前证书落地的常见需求适合需要快速完成系统级证书配置、验证与排错的系统管理员及开发人员参考。压缩包共2个文件以1个sh脚本和1个bat批处理为主整体仅578B前者对应Linux下的证书目录与缓存更新场景后者适配Windows证书存储导入轻量易携带便于按平台取用。已有700人学习说明其跨平台脚本思路具有一定参考价值。脚本内容围绕certutil导入pfx、openssl格式转换与证书链处理等环节展开涉及私钥匹配、存储位置选择及服务生效等易错点可作为运维模板帮助梳理导入流程、定位配置问题并减少手工操作带来的遗漏。此外对证书链完整性、带密码pfx文件的保管等注意事项亦有覆盖便于部署前自检提升HTTPS配置效率。1. SSL 证书导入脚本不是一条命令而是一套信任链的搬运流程一份自签或内网 CA 签出来的证书在浏览器里点两下就能信任可一旦落到命令行、服务进程、容器里报错就变成了unable to get local issuer certificate、SSL: CERTIFICATE_VERIFY_FAILED、PKIX path building failed这几种看起来毫不相干的提示。很多团队的做法是让人手工往系统里塞一遍下次换机器再塞一遍时间一长谁也说不清哪台机器导过、导的是哪张。所以“SSL 证书导入脚本”这件事的价值不在于省几条命令而在于把 Windows 和 Linux 上分散的信任库位置、格式要求、权限边界收敛成一个可重复执行的动作。这篇内容面向需要交付离线环境、内网服务或多运行时项目的同学从证书格式对齐讲到 Linux 与 Windows 两套脚本再到踩过的坑和验证办法照着改就能用。2. 先把证书链捋直PEM、DER、PKCS12 和信任库的对应关系导入失败十次里有六次不是脚本写错而是手里那个文件根本不是系统信任库想吃的东西。系统信任库要的是“CA 证书”而且是能一路追到自签根的完整链而运维从服务端导出来的往往是“服务器证书 中间证书”里面没有根。先把格式和语义对齐脚本才有意义。2.1 四种容器格式的边界与转换命令日常能碰到的证书文件其实就四类扩展名完全不靠谱.crt既可能是 PEM 也可能是 DER.cer在 Windows 上默认是 DER、在很多导出工具里又是 PEM。判断办法只有一个看头几个字节。容器典型扩展名内容能否直接进系统信任库PEM.pem .crt .cerBase64 文本-----BEGIN CERTIFICATE-----开头可以Linux 首选DER.der .cer二进制不行需转 PEMPKCS#7.p7b .p7c一条或多条证书无私钥不行需拆出单证书PKCS#12.pfx .p12私钥 证书 链带密码不行且通常不该进根信任库一个特别容易混淆的点PKCS#12 是给“服务端要出示证书”的场景用的把它整个导进根信任区等于信任了一把带私钥的身份方向就错了。信任库只放公钥证书。转换命令我一般直接背这几条# 1. 判断到底是 PEM 还是 DER看前几个字节是不是 ASCII 的 - head -c 27 server.crt; echo # 2. DER 转 PEM openssl x509 -in server.der -inform der -out server.pem -outform pem # 3. p7b 拆成 PEM可能输出多条证书正好是一条链 openssl pkcs7 -in chain.p7b -print_certs -out chain.pem # 4. pfx 只取证书部分不要私钥 openssl pkcs12 -in bundle.pfx -nokeys -clcerts -out leaf.pem openssl pkcs12 -in bundle.pfx -nokeys -cacerts -out ca.pem-inform der和-outform pem是显式指定输入输出编码不给的话 openssl 默认按 PEM 读喂 DER 进去只会得到一句unable to load certificate。-nokeys是硬性约束避免把私钥写进信任库目录-clcerts只要客户端/叶子证书-cacerts只要链上的 CA拆开之后你才知道自己到底该导哪一张。真正要导入系统的通常是最末端的那个自签根或者内网 CA 的根证书。判断方法很简单# subject 和 issuer 相同 自签根这张才该进信任库 openssl x509 -in ca.pem -noout -subject -issuer -dates -fingerprint -sha256-fingerprint -sha256这步别省。多人协作时最容易出的乱子是“同名不同证书”两个内网都叫internal-ca.crt指纹一比就知道拿错了。指纹也是脚本里做幂等判断的最好抓手目标目录已有同名文件时比指纹一样就跳过不一样就报错退出而不是默默覆盖。2.2 每个运行时读的信任库根本不是同一个这是导入脚本必须分层处理的根因。把证书塞进系统信任库只对“用系统信任库的程序”生效而常见的几类运行时各有各的家当。运行时/工具默认信任库位置覆盖方式curl / wget / openssl系统 bundle--cacert、CURL_CA_BUNDLEJava$JAVA_HOME/lib/security/cacerts-Djavax.net.ssl.trustStorePython requestscertifi 包内的cacerts.pemREQUESTS_CA_BUNDLE、SSL_CERT_FILENode.js内置根列表NODE_EXTRA_CA_CERTSGo系统信任库静态编译时可能没有SSL_CERT_FILE、SSL_CERT_DIR注意 Java 那一行cacerts是 JKS 格式跟 PEM 完全不是一回事直接把.pem拷进去只会得到一个损坏的密钥库。Python 的 certifi 更隐蔽系统信任库更新了certifi 还是旧的于是curl通、requests不通。提示脚本里别默认“导完系统信任库就完事”先确认业务到底跑在哪个运行时上。判断顺序我一般是报错栈里出现sun.security就是 Java出现certifi或urllib3就是 Python出现UNABLE_TO_VERIFY_LEAF_SIGNATURE基本是 Node。2.3 导入前的三行体检在动手写脚本之前先确认这张证书本身是能用的。三条命令分别回答“链能不能验通”“主机名对不对”“还有多久过期”。# 1. 用你准备导入的那张 CA 去验服务端证书链 openssl verify -CAfile ca.pem -untrusted chain.pem server.pem # 2. 看 SAN主机名必须落在这里面CN 字段现在已经不作数 openssl x509 -in server.pem -noout -ext subjectAltName # 3. 直接连服务端把真实下发的链抓下来对比 openssl s_client -connect api.internal:443 -servername api.internal -showcerts /dev/null 2/dev/null | openssl x509 -noout -subject -dates第 1 条返回server.pem: OK才说明链是完整的。如果返回unable to get local issuer certificate说明-untrusted里缺中间证书这时候补的是中间证书不是往系统里再塞一张根。第 2 条是很多人忽略的证书导入成功、链也完整但报hostname mismatch那是签发时 SAN 没写全跟导入脚本一点关系都没有改脚本没用得重签。第 3 条用来确认服务端到底下发了什么——有些网关只下发叶子证书不下发中间证书客户端补中间证书也补不回来只能让服务端改配置。3. Linux 侧脚本一条命令同时喂饱 Debian 系和 RHEL 系Linux 这块的坑集中在“目录约定”和“更新命令”两件事上。Debian 系认/usr/local/share/ca-certificates/下的.crtRHEL 系认/etc/pki/ca-trust/source/anchors/下的.pem两边更新命令还不一样。写脚本的第一要务是别硬编码先探测。3.1 目录约定与 update-ca-* 的真实行为update-ca-certificates并不是“把目录里的文件复制到 bundle 里”这么简单它会做几件事扫描指定目录、只接受后缀为.crt的文件、把内容合并进/etc/ssl/certs/ca-certificates.crt、在每个证书上建一个以哈希命名的软链、最后打印N added, M removed。这行输出是判断成败的关键看到0 added就说明文件根本没被认出来而不是“导进去了但没生效”。update-ca-trust extract是 RHEL 系的对应命令同样只扫anchors目录但它会把结果同时写进 PEM bundle 和它自己的哈希目录输出的安静程度比 Debian 系高出错也不吭声所以脚本里必须自己回读校验。一个常见误区是直接cat ca.crt /etc/ssl/certs/ca-certificates.crt。这么做当场有效但下次系统包升级重建 bundle追加的内容就没了而且这种写法不可撤销删除时只能整文件重来。血泪经验是永远只往 source 目录放让工具去生成 bundle。3.2 一份幂等的导入脚本脚本要满足三个条件重复执行结果一致、拿错证书能报错、失败能回滚。下面这份可以直接落地。#!/usr/bin/env bash # import-ca.sh 用法: ./import-ca.sh /path/to/ca.pem [别名] set -euo pipefail SRC${1:?用法: $0 证书文件 [别名]} ALIAS${2:-$(basename ${SRC%.*})} NAME${ALIAS}.crt # Debian 系强制要求 .crt 后缀 [[ -r $SRC ]] || { echo 读不到文件: $SRC 2; exit 1; } # 1. 归一化无论输入是 DER 还是 PEM统一转成 PEM 再往下走 TMP$(mktemp -d); trap rm -rf $TMP EXIT if head -c 1 $SRC | grep -q -- -; then cp $SRC $TMP/norm.pem else openssl x509 -in $SRC -inform der -out $TMP/norm.pem -outform pem fi # 2. 体检能解析、没过期、确实是自签根 openssl x509 -in $TMP/norm.pem -noout -checkend 0 /dev/null \ || { echo 证书已过期; exit 2; } SUBJ$(openssl x509 -in $TMP/norm.pem -noout -subject) ISSU$(openssl x509 -in $TMP/norm.pem -noout -issuer) NEW_FP$(openssl x509 -in $TMP/norm.pem -noout -fingerprint -sha256 | cut -d -f2) # 3. 按发行版选目录 if [[ -d /usr/local/share/ca-certificates ]]; then DEST_DIR/usr/local/share/ca-certificates UPDATE_CMD(update-ca-certificates) elif [[ -d /etc/pki/ca-trust/source/anchors ]]; then DEST_DIR/etc/pki/ca-trust/source/anchors UPDATE_CMD(update-ca-trust extract) else echo 未识别的信任库布局 2; exit 3 fi # 4. 幂等同名文件已存在就比较指纹 if [[ -f $DEST_DIR/$NAME ]]; then OLD_FP$(openssl x509 -in $DEST_DIR/$NAME -noout -fingerprint -sha256 | cut -d -f2) if [[ $OLD_FP $NEW_FP ]]; then echo 已存在且指纹一致跳过: $NAME; exit 0 fi echo 同名文件指纹不一致请先手工确认: $DEST_DIR/$NAME 2; exit 4 fi install -m 0644 $TMP/norm.pem $DEST_DIR/$NAME ${UPDATE_CMD[]} # 5. 回读校验确认 bundle 里真的含这张证书的指纹 if ! openssl crl2pkcs7 -nocrl -certfile /etc/ssl/certs/ca-certificates.crt 2/dev/null \ | openssl pkcs7 -print_certs -noout 2/dev/null | grep -qi ${SUBJ#subject}; then echo 警告更新后未在 bundle 中找到该证书请检查 update 命令输出 2 fi echo 导入完成: $NAME ($SUBJ / $ISSU)几个参数值得单独说。-checkend 0是“距今 0 秒后是否仍有效”等价于判断是否过期想提前预警可以改成-checkend 2592000表示 30 天内到期就报错。${SRC%.*}是去掉最后一个点之后的后缀避免把ca.pem拼成ca.pem.crt。trap ... EXIT保证临时目录一定被清掉脚本中断也不会留垃圾。install -m 0644比cp多做了权限收敛信任库文件不该带执行位。第 5 步的回读校验是我后来强加的因为update-ca-certificates在文件格式有问题时只打印一行警告就返回 0脚本会误以为成功。回读用的是把 bundle 拆成证书列表再 grep subject虽然土但比grep -q BEGIN CERT靠谱得多——后者永远为真。3.3 Java、Python、Node 的旁路信任库得单独补系统信任库搞定之后业务侧往往还有三个漏网之鱼。Java 的那份cacerts默认口令是changeit导入用的是 keytoolCA_PEM/usr/local/share/ca-certificates/internal-ca.crt KEYSTORE${JAVA_HOME:?未设置 JAVA_HOME}/lib/security/cacerts # -noprompt 避免交互确认重复导入同名 alias 会报错所以先删再导 keytool -delete -alias internal-ca -keystore $KEYSTORE -storepass changeit 2/dev/null || true keytool -importcert -noprompt -trustcacerts \ -alias internal-ca -file $CA_PEM \ -keystore $KEYSTORE -storepass changeit # 校验能看到条目才算成功 keytool -list -keystore $KEYSTORE -storepass changeit -alias internal-ca-storepass changeit是 JKS 的默认口令改过的话要通过-Djavax.net.ssl.trustStorePassword传。-delete前面挂|| true是因为首次导入时条目不存在keytool 会返回非零set -e下会直接中断脚本。另外注意$JAVA_HOME可能不止一个多 JDK 机器上要对每个都导一遍否则换个 JDK 跑服务又挂。Python 和 Node 更轻优先用环境变量而不是改包内文件因为改包内文件在重装依赖时会丢# Python指向系统 bundle 或单独的 CA 文件 export REQUESTS_CA_BUNDLE/etc/ssl/certs/ca-certificates.crt export SSL_CERT_FILE/etc/ssl/certs/ca-certificates.crt # Node只能追加不能替换正好符合补一张内网根的需求 export NODE_EXTRA_CA_CERTS/usr/local/share/ca-certificates/internal-ca.crtNODE_EXTRA_CA_CERTS的语义是“在内置根列表基础上追加”文件名必须能读、内容必须是 PEMNode 启动时会解析解析失败直接进程退出报错信息还算清楚。这几个变量建议写进服务的 systemd unit 或者容器启动脚本别只写在某个人的.bashrc里——那就是典型的“我这儿能跑”。4. Windows 侧脚本certutil 与 PowerShell 的分工Windows 的信任模型跟 Linux 完全不是一个思路没有全局 bundle 文件证书存在注册表支撑的“存储区”里按用户和机器分家。所以脚本的第一件事不是导而是问清楚“给谁导”。4.1 Root、CA、My 三个存储区怎么选存储区路径放什么典型误用Root受信任的根Cert:\LocalMachine\Root自签根、内网 CA 根把中间证书塞进来CA中间证书颁发机构Cert:\LocalMachine\CA中间证书不确定时全往 Root 扔My个人Cert:\LocalMachine\My带私钥的服务端证书放公钥 CA信任验证的逻辑是从叶子往上追追到 Root 里某张证书为止。所以中间证书放 Root 里虽然“能用”但会让信任边界变宽——任何由那张中间证书签出来的证书都被无条件信任了这在多部门共用一套 PKI 的环境里是个隐患。规范做法是根进 Root中间进 CA。另一个维度是LocalMachine和CurrentUser。前者对本机所有账户和服务生效需要管理员权限后者只对当前登录用户生效普通权限即可。这里埋着最经典的翻车点用CurrentUser导完手动跑测试脚本一切正常换成 Windows 服务以 LocalSystem 运行就报证书不受信。4.2 批量导入脚本与静默参数PowerShell 的Import-Certificate比certutil好用在返回值上能直接判断有没有重复。下面这份接受一个目录把.cer/.crt/.pem统一处理。# .SYNOPSIS 将目录下的 CA 证书导入指定存储区 .EXAMPLE .\Import-Ca.ps1 -Path .\certs -Scope LocalMachine -Store Root # param( [Parameter(Mandatory $true)][string]$Path, [ValidateSet(LocalMachine,CurrentUser)][string]$Scope LocalMachine, [ValidateSet(Root,CA,My)][string]$Store Root ) $target Cert:\$Scope\$Store # 1. 权限前置检查避免导到一半失败 if ($Scope -eq LocalMachine) { $isAdmin ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent() ).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator) if (-not $isAdmin) { throw 导入 LocalMachine 需要管理员权限 } } # 2. 逐个文件处理pem 需要先读成 X509Certificate2 Get-ChildItem -Path $Path -File | Where-Object { $_.Extension -in .cer, .crt, .pem } | ForEach-Object { try { $cert New-Object System.Security.Cryptography.X509Certificates.X509Certificate2 $_.FullName } catch { Write-Warning 解析失败跳过: $($_.Name); return } # 3. 幂等按指纹查重 $exists Get-ChildItem $target | Where-Object { $_.Thumbprint -eq $cert.Thumbprint } if ($exists) { Write-Host 已存在: $($_.Name); return } if ($cert.NotAfter -lt (Get-Date)) { Write-Warning 已过期: $($_.Name); return } # 4. -Confirm:$false 保证非交互会话下不弹确认框 Import-Certificate -FilePath $_.FullName -CertStoreLocation $target -Confirm:$false | Out-Null Write-Host 导入成功: $($_.Name) [$($cert.Thumbprint)] } # 5. 汇总当前存储区数量便于对比导入前后 Write-Host 当前 $target 证书数量: $((Get-ChildItem $target).Count)New-Object X509Certificate2这步是关键它同时兼任格式探测和有效性检查DER 和 PEM 都能吃。Thumbprint就是 SHA-1 指纹做去重足够用但和 openssl 那边给的 SHA-256 指纹不是一回事跨平台核对时要么统一算法要么两边都算一遍。-Confirm:$false看起来多余实际在无人值守的部署脚本里非常必要——某些组策略会把确认提示重新打开一弹框整条流水线就挂在那儿等超时。如果环境里只有certutil等价写法是# -f 强制覆盖-addstore 后面跟存储区名加 -user 表示 CurrentUser certutil -addstore -f -enterprise Root .\internal-ca.crt if ($LASTEXITCODE -ne 0) { throw certutil 失败退出码 $LASTEXITCODE }certutil的坑在于它出错时不一定返回非零某些“证书已存在”的情况返回 0 但实际没动作所以必须自己查$LASTEXITCODE再回读一次。-enterprise参数只在域环境有意义非域环境会报错pick 脚本里最好别默认带上。4.3 服务账号与非交互会话Windows 服务常见的运行身份是 LocalSystem、NetworkService 或者某个专用服务账户它们各自的CurrentUser存储区是独立的。判断方法是在服务上下文里查当前身份而不是在你自己登录的会话里查。排查思路是这样先确认报错进程的身份再决定导到哪儿。用LocalMachine能覆盖所有账户代价是要管理员权限用服务账户的CurrentUser则必须用该账户登录一次或者用PsExec -u之类的方式切过去执行比较麻烦。所以我的默认选择永远是LocalMachine除非明确知道那台机器上不允许动机器级配置。还有一个非交互会话的细节.pfx导入时如果需要私钥可导出必须显式传-Exportable否则后续某些组件读取私钥会失败口令用ConvertTo-SecureString转别把明文写在脚本里。$pwd ConvertTo-SecureString -String $env:PFX_PASS -AsPlainText -Force Import-PfxCertificate -FilePath .\server.pfx -CertStoreLocation Cert:\LocalMachine\My -Password $pwd -Exportable5. 避坑与排查五条真踩过的记录下面这些每一条都实际卡过我共同点是“脚本返回成功但业务不通”所以判断标准不能是脚本退出码而是回读和端到端验证。5.1 现象导完 curl 通了Java 服务还是 PKIX path building failed原因Java 根本不读系统信任库cacerts是独立的 JKS 文件而且机器上装了三套 JDK只导了默认那个。解决脚本里遍历所有$JAVA_HOME或者更稳妥地遍历/usr/lib/jvm/*/lib/security/cacerts对每个文件执行一次 keytool 导入并用-list -alias回读。服务启动参数里也可以显式指定-Djavax.net.ssl.trustStore把信任库和部署包绑在一起减少环境差异。5.2 现象update-ca-certificates输出0 added但文件明明在目录里原因三种可能。文件后缀不是.crt内容带 BOM 或者 CRLF 换行导致解析失败文件其实是 DER 二进制但被命名成了.crt。解决先head -c 3看有没有 BOMEF BB BF有就sed -i 1s/^\xEF\xBB\xBF//去掉再file看是不是 ASCII text最后确认后缀。脚本里我加了归一化步骤所有输入先转成标准 PEM 再落盘这三类问题一次性规避。5.3 现象Windows 上手动测试全通过服务一启动就报证书不可信原因证书导进了CurrentUser\Root而服务以 LocalSystem 运行看的是LocalMachine\Root。解决改导LocalMachine脚本里加管理员权限前置检查。另一个变体是导了LocalMachine但没刷新某些老组件会缓存重启服务或者certutil -pulse触发一次自动更新。5.4 现象导入了根证书链还是断的openssl verify报中间证书缺失原因只导了根服务端又没下发中间证书。信任库里有根不等于能自动补齐链路。解决要么让服务端在握手时带上完整链要么把中间证书也导进/etc/pki/ca-trust/source/anchorsRHEL 系或CA存储区Windows。用openssl s_client -showcerts抓一次服务端实际下发的链比对一下缺哪张这是最快的定位方式。5.5 现象脚本跑完没报错业务偶发握手失败重试又好原因证书有效期临近或者系统时间偏移。证书校验对时间很敏感容器里时钟没同步的情况下同一张证书会出现“时好时坏”。解决脚本里把-checkend告警阈值调出来30 天内到期就打印醒目的警告而不是静默通过容器镜像里确认date与宿主机一致。另外证书过期不会让脚本失败只会让业务失败所以校验必须在脚本这一层做完。6. 让脚本自己证明有效三层验证与一份自检脚本导入脚本返回 0 是最不可信的信号。我的习惯是让脚本自己在三个层次上证明一遍链能不能验通、运行时能不能握手、业务端口能不能通。缺任何一层都可能出现“导进去了但不生效”。6.1 三层验证的判据第一层是openssl verify用最终 bundle 文件去验服务端证书返回OK才算链完整。第二层是运行时自检Java 用一段最小SSLSocket代码Python 用requests.getNode 用https.get各自打一次目标地址报错信息比 curl 精确得多。第三层是业务探活也就是你真正要访问的那个接口它可能走的是双向认证或者自定义的 SNI前两层过了它未必过。层级命令通过判据失败指向链路openssl verify -CAfile bundle输出OK缺中间证书或根没进去运行时python/node/java 最小握手返回 200 或握手成功运行时信任库没覆盖业务curl -sv 真实接口HTTP 2xxSNI、双向认证、代理第三层特别容易被忽略。有些内网网关做双向认证客户端证书不在信任库讨论范围内有些服务只认特定 SNI用 IP 直连必然失败。这时候前面两层全绿也没用。6.2 一份可复用的自检脚本把三层验证串起来做成导入脚本的收尾步骤。#!/usr/bin/env bash # verify-trust.sh 用法: ./verify-trust.sh api.internal 443 /path/to/server.pem set -uo pipefail HOST${1:?主机}; PORT${2:-443}; SERVER_CERT${3:?服务端证书路径} FAIL0 echo [1/3] 校验证书链 if openssl verify -CAfile /etc/ssl/certs/ca-certificates.crt $SERVER_CERT 21 | grep -q : OK; then echo 通过 else echo 失败链不完整或根未导入; FAIL1 fi echo [2/3] 校验运行时握手 (Python) if python3 - $HOST $PORT PY import socket, ssl, sys host, port sys.argv[1], int(sys.argv[2]) ctx ssl.create_default_context() # 走默认信任库 with socket.create_connection((host, port), timeout5) as s: with ctx.wrap_socket(s, server_hostnamehost) as ss: print( 协议:, ss.version(), | 证书主体:, dict(x[0] for x in ss.getpeercert()[subject])) PY then echo 通过; else echo 失败运行时信任库未覆盖; FAIL1; fi echo [3/3] 校验业务端口 CODE$(curl -s -o /dev/null -w %{http_code} --max-time 5 https://${HOST}:${PORT}/ || echo 000) [[ $CODE ~ ^2|^3 ]] echo 通过 HTTP $CODE || { echo 失败 HTTP $CODE; FAIL1; } exit $FAILcreate_default_context()不加任何参数走的才是系统和SSL_CERT_FILE的默认查找逻辑如果这里失败而openssl verify通过基本可以断定是 certifi 覆盖了默认路径。server_hostnamehost这个参数不能省SNI 和主机名校验都靠它用 IP 直连时会因为 SAN 里没有 IP 而失败这是设计如此。最后的curl用-o /dev/null -w %{http_code}只取状态码--max-time 5防止卡死|| echo 000兜住连接失败的情况。注意自检脚本本身的退出码要能反映整体结果所以用FAIL累加而不是set -e。放到 CI 里时任何一层失败都应该阻断发布别让“证书问题”带着隐患上线。那次事故之后我给自己定了个规矩任何和环境信任相关的脚本收尾必须打一遍这三层任何一层红掉就不许标记完成。当时图省事只看了update-ca-certificates的输出结果 Java 侧的cacerts漏导灰度发布上去整批服务连不上配置中心回滚花了四十分钟。后来我把自检脚本挂到了部署流程的最后一个阶段跑一次不到十秒比事后翻日志便宜太多。希望帮到你。本文还有配套的精品资源点击获取