<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/">
  <channel>
    <title>Shine_ssr’Blog</title>
    <link>https://www.xuwx.top/</link>
    <language>zh-CN</language>
    <copyright>All rights reserved 2026, Shine_ssr</copyright>
    <lastBuildDate>Tue, 23 Jun 2026 03:17:53 GMT</lastBuildDate>
    <generator>Hexo</generator>
    <atom:link href="https://www.xuwx.top/rss.xml" rel="self" type="application/rss+xml"/>
    <item>
      <title>2025-2026 前沿开源语音克隆项目横评：从 MockingBird 到零样本时代</title>
      <link>https://www.xuwx.top/2026/06/23/2025-2026-%E5%89%8D%E6%B2%BF%E5%BC%80%E6%BA%90%E8%AF%AD%E9%9F%B3%E5%85%8B%E9%9A%86%E9%A1%B9%E7%9B%AE%E6%A8%AA%E8%AF%84/</link>
      <description>这两年语音克隆领域的进展超出了我的预期。用 3 秒音频就能克隆一个人的声音，150ms 延迟流式输出，这些在 MockingBird 时代是不可能的事情。整理了七个主流开源项目的技术路线、上手成本和适用边界，供参考。</description>
      <author>Shine_ssr</author>
      <category domain="https://www.xuwx.top/categories/AI/">AI</category>
      <category domain="https://www.xuwx.top/categories/AI/%E8%AF%AD%E9%9F%B3%E6%8A%80%E6%9C%AF/">语音技术</category>
      <category domain="https://www.xuwx.top/tags/TTS/">TTS</category>
      <category domain="https://www.xuwx.top/tags/%E8%AF%AD%E9%9F%B3%E5%90%88%E6%88%90/">语音合成</category>
      <category domain="https://www.xuwx.top/tags/%E6%B7%B1%E5%BA%A6%E5%AD%A6%E4%B9%A0/">深度学习</category>
      <category domain="https://www.xuwx.top/tags/%E5%BC%80%E6%BA%90%E9%A1%B9%E7%9B%AE/">开源项目</category>
      <pubDate>Mon, 22 Jun 2026 16:00:00 GMT</pubDate>
      <content:encoded>
        <![CDATA[<blockquote><p>基于深度调研整理，最后更新：2026-06-23<br>对标基准：MockingBird（SV2TTS 架构，2020年代初）</p></blockquote><hr><h2 id="技术背景：这两年发生了什么"><a href="#技术背景：这两年发生了什么" class="headerlink" title="技术背景：这两年发生了什么"></a>技术背景：这两年发生了什么</h2><p>如果你上次关注 TTS 还是 MockingBird 那个年代，现在再看这个领域，基本上是另一个世界了。</p><p>变化不是渐进式的，是范式级的——底层表示从连续 mel 频谱图换成了离散语音 Codec token，骨干网络从 LSTM+Tacotron 换成了 LLM&#x2F;Transformer&#x2F;Diffusion，更重要的是，克隆方式从”必须为每个说话人重新训练”变成了”给我 3 秒参考音频”。</p><table><thead><tr><th>维度</th><th>MockingBird（旧范式）</th><th>2025-2026 新范式</th></tr></thead><tbody><tr><td>中间表示</td><td>连续 mel 频谱图</td><td>离散语音 Codec token</td></tr><tr><td>骨干网络</td><td>LSTM + Tacotron</td><td>LLM &#x2F; Transformer &#x2F; Diffusion</td></tr><tr><td>模型结构</td><td>三段独立（Encoder→Synthesizer→Vocoder）</td><td>统一端到端模型</td></tr><tr><td>克隆方式</td><td>需针对每说话人训练 synthesizer</td><td>零样本（3-10 秒音频即克隆）</td></tr><tr><td>语言支持</td><td>仅中文普通话</td><td>多语言多方言</td></tr><tr><td>推理方式</td><td>逐帧自回归</td><td>流式 &#x2F; 非自回归并行</td></tr><tr><td>延迟</td><td>秒级</td><td>150ms 级流式输出</td></tr></tbody></table><hr><h2 id="项目速查"><a href="#项目速查" class="headerlink" title="项目速查"></a>项目速查</h2><table><thead><tr><th>项目</th><th>Stars</th><th>核心架构</th><th>零样本</th><th>语言</th><th>许可证</th><th>推荐场景</th></tr></thead><tbody><tr><td><a href="https://github.com/FunAudioLLM/CosyVoice">CosyVoice 3.0</a></td><td>高</td><td>LLM + Flow Matching</td><td>3秒</td><td>9语言+18方言</td><td>Apache 2.0</td><td>工业部署、多语言</td></tr><tr><td><a href="https://github.com/RVC-Boss/GPT-SoVITS">GPT-SoVITS</a></td><td>~59k</td><td>GPT + VITS</td><td>5秒</td><td>中英日韩粤</td><td>MIT</td><td>个人开发者、快速上手</td></tr><tr><td><a href="https://github.com/2noise/ChatTTS">ChatTTS</a></td><td>高</td><td>AR LM + DVAE + Vocos</td><td>支持</td><td>中英</td><td>AGPLv3</td><td>对话场景、客服</td></tr><tr><td><a href="https://github.com/amphion/MaskGCT">MaskGCT</a></td><td>~7k</td><td>非自回归掩码 Codec</td><td>支持</td><td>中英</td><td>MIT</td><td>学术研究、前沿架构</td></tr><tr><td><a href="https://github.com/SWivid/F5-TTS">F5-TTS</a></td><td>活跃</td><td>Flow Matching + DiT</td><td>支持</td><td>中英</td><td>CC BY-NC-SA</td><td>学术研究</td></tr><tr><td><a href="https://github.com/fishaudio/fish-speech">Fish Speech</a></td><td>高</td><td>VQGAN + Transformer LM</td><td>10秒</td><td>多语言</td><td>Apache 2.0</td><td>流式合成</td></tr><tr><td><a href="https://github.com/BytedanceSpeech/seed-tts-eval">Seed-TTS</a></td><td>—</td><td>AR LM + DiT 变体</td><td>支持</td><td>中英</td><td>评估代码开源</td><td>评估基准参考</td></tr></tbody></table><hr><h2 id="各项目详解"><a href="#各项目详解" class="headerlink" title="各项目详解"></a>各项目详解</h2><h3 id="1-CosyVoice-3-0（阿里通义）"><a href="#1-CosyVoice-3-0（阿里通义）" class="headerlink" title="1. CosyVoice 3.0（阿里通义）"></a>1. CosyVoice 3.0（阿里通义）</h3><p>目前综合能力最强的开源方案，没有之一。阿里把它当成内部 TTS 基础设施在维护，更新频率和文档质量都比较靠谱。</p><h4 id="架构思路"><a href="#架构思路" class="headerlink" title="架构思路"></a>架构思路</h4><figure class="highlight css"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs css">文本 → LLM（预测离散语音 token）→ <span class="hljs-attribute">Flow</span> Matching 生成声学特征 → HiFi-GAN 解码 → 波形<br></code></pre></td></tr></table></figure><p>用 LLM 理解文本语义，Flow Matching 替代 Tacotron 的自回归生成，离散 Codec 替代 mel 频谱图——基本上把新范式的几个核心改进都用上了。</p><h4 id="关键指标"><a href="#关键指标" class="headerlink" title="关键指标"></a>关键指标</h4><ul><li><strong>零样本克隆</strong>：约 3 秒参考音频</li><li><strong>多语言</strong>：9 种语言 + 18+ 种中文方言（粤语、四川话等）</li><li><strong>跨语言合成</strong>：用中文声音说英文，口音迁移效果不错</li><li><strong>流式输出</strong>：双流架构，首包延迟 150ms，可接实时应用</li><li><strong>可控性</strong>：支持指令控制情感、语速、音量（比如”用激动的语气说这句话”）</li><li><strong>发音修复</strong>：可针对特定词纠正读音，对专业名词友好</li></ul><h4 id="资源"><a href="#资源" class="headerlink" title="资源"></a>资源</h4><ul><li>GitHub：<a href="https://github.com/FunAudioLLM/CosyVoice">https://github.com/FunAudioLLM/CosyVoice</a></li><li>论文（v1）：<a href="https://arxiv.org/abs/2407.05407">https://arxiv.org/abs/2407.05407</a></li><li>论文（v2）：<a href="https://arxiv.org/abs/2412.10117">https://arxiv.org/abs/2412.10117</a></li><li>论文（v3）：<a href="https://arxiv.org/abs/2505.17589">https://arxiv.org/abs/2505.17589</a></li></ul><h4 id="快速上手"><a href="#快速上手" class="headerlink" title="快速上手"></a>快速上手</h4><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><code class="hljs bash">git <span class="hljs-built_in">clone</span> https://github.com/FunAudioLLM/CosyVoice.git<br><span class="hljs-built_in">cd</span> CosyVoice<br><br>conda create -n cosyvoice python=3.10<br>conda activate cosyvoice<br>pip install -r requirements.txt<br><br><span class="hljs-comment"># 下载预训练模型（也可以从 HuggingFace 拉）</span><br>python -c <span class="hljs-string">&quot;from modelscope import snapshot_download; snapshot_download(&#x27;iic/CosyVoice-300M&#x27;, local_dir=&#x27;pretrained_models/CosyVoice-300M&#x27;)&quot;</span><br><br><span class="hljs-comment"># 启动 Gradio Web UI</span><br>python webui.py<br></code></pre></td></tr></table></figure><p><strong>适合谁用</strong>：需要上生产的团队、有多语言或方言需求、对延迟有要求的实时应用。</p><hr><h3 id="2-GPT-SoVITS"><a href="#2-GPT-SoVITS" class="headerlink" title="2. GPT-SoVITS"></a>2. GPT-SoVITS</h3><p>社区里活跃度最高的项目，Star 数快到 60k，B 站上教程一搜一大把。如果你只是想快速跑通一个语音克隆 demo，从这里开始是最省事的选择。</p><h4 id="架构思路-1"><a href="#架构思路-1" class="headerlink" title="架构思路"></a>架构思路</h4><figure class="highlight gauss"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs gauss">文本 → GPT（自回归预测语义 <span class="hljs-built_in">token</span>）→ SoVITS（VITS 解码器）→ 波形<br></code></pre></td></tr></table></figure><p>GPT 负责文本到语义的映射，VITS-based 解码器生成波形。v2 Pro 的推理速度非常快，RTF 0.014（RTX 4090），基本上不是性能瓶颈。</p><h4 id="关键指标-1"><a href="#关键指标-1" class="headerlink" title="关键指标"></a>关键指标</h4><ul><li><strong>极低数据门槛</strong>：零样本只需 5 秒，微调最低 1 分钟音频就够</li><li><strong>多语言</strong>：中文、英文、日文、韩文、粤语</li><li><strong>版本迭代快</strong>：v1 → v2 → v3 → v4，问题修得挺快。v3 有 24k 的金属音问题，v4 升到原生 48k 解决了</li><li><strong>社区资源丰富</strong>：Windows 整合包开箱即用，适合非 Linux 背景的用户</li></ul><h4 id="资源-1"><a href="#资源-1" class="headerlink" title="资源"></a>资源</h4><ul><li>GitHub：<a href="https://github.com/RVC-Boss/GPT-SoVITS%EF%BC%88~59k">https://github.com/RVC-Boss/GPT-SoVITS（~59k</a> Stars，截至 2026-06）</li></ul><h4 id="快速上手-1"><a href="#快速上手-1" class="headerlink" title="快速上手"></a>快速上手</h4><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><code class="hljs bash"><span class="hljs-comment"># Windows 用户直接从 Releases 下整合包，解压即用</span><br><br><span class="hljs-comment"># 手动安装</span><br>git <span class="hljs-built_in">clone</span> https://github.com/RVC-Boss/GPT-SoVITS.git<br><span class="hljs-built_in">cd</span> GPT-SoVITS<br>conda create -n gptsovits python=3.10<br>conda activate gptsovits<br>pip install -r requirements.txt<br>python webui.py<br></code></pre></td></tr></table></figure><h4 id="微调流程（1-分钟数据）"><a href="#微调流程（1-分钟数据）" class="headerlink" title="微调流程（1 分钟数据）"></a>微调流程（1 分钟数据）</h4><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><code class="hljs bash"><span class="hljs-comment"># 1. 把音频文件放到 dataset/ 目录</span><br><span class="hljs-comment"># 2. WebUI 里走以下步骤：</span><br><span class="hljs-comment">#    - 音频标注（自动 ASR 转文字，支持手动校对）</span><br><span class="hljs-comment">#    - 训练 SoVITS 模型（约 5-15 分钟）</span><br><span class="hljs-comment">#    - 训练 GPT 模型（约 5-15 分钟）</span><br><span class="hljs-comment">#    - 推理合成</span><br></code></pre></td></tr></table></figure><p><strong>适合谁用</strong>：个人开发者、快速验证想法、预算有限但要效果、需要大量社区支持的场景。</p><hr><h3 id="3-ChatTTS"><a href="#3-ChatTTS" class="headerlink" title="3. ChatTTS"></a>3. ChatTTS</h3><p>专门为”让合成语音听起来像真人在聊天”这个目标设计的，韵律自然度在几个项目里比较突出。</p><h4 id="架构思路-2"><a href="#架构思路-2" class="headerlink" title="架构思路"></a>架构思路</h4><figure class="highlight"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs">文本 → 自回归 Transformer LM → DVAE 解码 → Vocos 声码器 → 波形<br></code></pre></td></tr></table></figure><p>受 VALL-E、Bark、XTTSv2 影响。DVAE（Discrete Variational Autoencoder）负责音频 token 化，Vocos 作为预训练声码器。</p><h4 id="关键特性"><a href="#关键特性" class="headerlink" title="关键特性"></a>关键特性</h4><ul><li><strong>对话韵律自然</strong>：内置停顿、笑声、语气词等非语言元素，这是其他项目不太关注的点</li><li><strong>词级控制</strong>：在文本里插入 <code>[laugh]</code>、<code>[uv_break]</code>、<code>[lbreak]</code> 等标记，精细控制语气</li><li><strong>中英混合</strong>：中英混合对话的效果比较好</li></ul><p>值得注意的是许可证是 AGPLv3，商用场景需要谨慎评估。</p><h4 id="资源-2"><a href="#资源-2" class="headerlink" title="资源"></a>资源</h4><ul><li>GitHub：<a href="https://github.com/2noise/ChatTTS">https://github.com/2noise/ChatTTS</a></li><li>许可证：AGPLv3</li></ul><h4 id="快速上手-2"><a href="#快速上手-2" class="headerlink" title="快速上手"></a>快速上手</h4><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs bash">git <span class="hljs-built_in">clone</span> https://github.com/2noise/ChatTTS.git<br><span class="hljs-built_in">cd</span> ChatTTS<br>pip install -r requirements.txt<br></code></pre></td></tr></table></figure><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><code class="hljs python"><span class="hljs-keyword">import</span> ChatTTS<br><span class="hljs-keyword">import</span> soundfile <span class="hljs-keyword">as</span> sf<br><br>chat = ChatTTS.Chat()<br>chat.load_models(<span class="hljs-built_in">compile</span>=<span class="hljs-literal">False</span>)  <span class="hljs-comment"># compile=True 可加速，但首次加载慢</span><br><br>texts = [<span class="hljs-string">&quot;你好，欢迎使用 ChatTTS，这是一个对话场景的语音合成系统。&quot;</span>]<br>wavs = chat.infer(texts, use_decoder=<span class="hljs-literal">True</span>)<br>sf.write(<span class="hljs-string">&quot;output.wav&quot;</span>, wavs[<span class="hljs-number">0</span>][<span class="hljs-number">0</span>], <span class="hljs-number">24000</span>)<br></code></pre></td></tr></table></figure><p><strong>适合谁用</strong>：对话机器人、客服系统，或者任何需要让 TTS 听起来”不那么像机器人”的场景。</p><hr><h3 id="4-MaskGCT（腾讯-香港中文大学（深圳））"><a href="#4-MaskGCT（腾讯-香港中文大学（深圳））" class="headerlink" title="4. MaskGCT（腾讯 &#x2F; 香港中文大学（深圳））"></a>4. MaskGCT（腾讯 &#x2F; 香港中文大学（深圳））</h3><p>ICLR 2025 论文项目，完全非自回归架构。如果你对 TTS 的技术路线感兴趣，这个值得深读。</p><h4 id="架构思路-3"><a href="#架构思路-3" class="headerlink" title="架构思路"></a>架构思路</h4><figure class="highlight livecodeserver"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs livecodeserver">文本 → Text-<span class="hljs-built_in">to</span>-Semantic 模型（掩码预测语义 <span class="hljs-keyword">token</span>）<br>          ↓<br>     Semantic-<span class="hljs-built_in">to</span>-Acoustic 模型（掩码预测声学 Codec <span class="hljs-keyword">token</span>）→ 波形<br></code></pre></td></tr></table></figure><p>两阶段掩码预测，类似 BERT 的掩码语言模型。完全并行生成，不需要显式的 duration predictor，也不需要强制对齐。整个流程跳过了 mel 频谱图，直接在离散 Codec 空间操作。</p><h4 id="关键指标-2"><a href="#关键指标-2" class="headerlink" title="关键指标"></a>关键指标</h4><ul><li><strong>零样本克隆</strong>：短参考音频即可</li><li><strong>中英双语</strong>：100K 小时数据训练（英文 50K + 中文 50K）</li><li><strong>推理快</strong>：非自回归并行生成，比自回归方案快不少</li><li><strong>学术质量</strong>：在标准 benchmark 上与 VALL-E、NaturalSpeech 相比有竞争力</li></ul><h4 id="资源-3"><a href="#资源-3" class="headerlink" title="资源"></a>资源</h4><ul><li>GitHub：<a href="https://github.com/amphion/MaskGCT%EF%BC%88%E5%9C%A8">https://github.com/amphion/MaskGCT（在</a> Amphion 工具包内）</li><li>论文：<a href="https://arxiv.org/abs/2409.00750">https://arxiv.org/abs/2409.00750</a></li><li>许可证：MIT</li></ul><h4 id="快速上手-3"><a href="#快速上手-3" class="headerlink" title="快速上手"></a>快速上手</h4><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><code class="hljs bash">git <span class="hljs-built_in">clone</span> https://github.com/open-mmlab/Amphion.git<br><span class="hljs-built_in">cd</span> Amphion<br>conda create -n amphion python=3.10<br>conda activate amphion<br>pip install -r requirements.txt<br><br><span class="hljs-comment"># 推理（参考 examples/maskgct/ 目录）</span><br>python inference.py --model maskgct --input <span class="hljs-string">&quot;你好世界&quot;</span> --reference ref.wav<br></code></pre></td></tr></table></figure><p><strong>适合谁用</strong>：做学术研究、想复现 ICLR 论文、关注非自回归 TTS 架构发展方向的开发者。</p><hr><h3 id="5-F5-TTS"><a href="#5-F5-TTS" class="headerlink" title="5. F5-TTS"></a>5. F5-TTS</h3><p>同为 ICLR 2025，架构比 MaskGCT 更简洁，Flow Matching + Diffusion Transformer 的组合。</p><h4 id="架构思路-4"><a href="#架构思路-4" class="headerlink" title="架构思路"></a>架构思路</h4><figure class="highlight css"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs css">文本 → ConvNeXt 文本编码器 → 上采样 → Diffusion Transformer（<span class="hljs-attribute">Flow</span> Matching）→ 波形<br></code></pre></td></tr></table></figure><p>全非自回归，不需要 duration predictor，ConvNeXt 替代传统文本编码器，Flow Matching 实现噪声到语音的转换。</p><h4 id="关键指标-3"><a href="#关键指标-3" class="headerlink" title="关键指标"></a>关键指标</h4><ul><li>中文 benchmark：SIM&#x3D;0.785, WER&#x3D;3.14</li><li>架构比 VALL-E、CosyVoice 更简洁，便于研究和改造</li></ul><h4 id="注意事项"><a href="#注意事项" class="headerlink" title="注意事项"></a>注意事项</h4><p>有用户反馈存在幻觉和跳字问题，在生产环境使用要做输出质量检测。另外，与 CosyVoice 2 的对比数据用的是原始 CosyVoice 版本，对比基准要注意。</p><h4 id="资源-4"><a href="#资源-4" class="headerlink" title="资源"></a>资源</h4><ul><li>GitHub：<a href="https://github.com/SWivid/F5-TTS">https://github.com/SWivid/F5-TTS</a></li><li>论文：<a href="https://arxiv.org/abs/2410.06885">https://arxiv.org/abs/2410.06885</a></li><li>许可证：CC BY-NC-SA（不可商用）</li></ul><h4 id="快速上手-4"><a href="#快速上手-4" class="headerlink" title="快速上手"></a>快速上手</h4><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><code class="hljs bash">git <span class="hljs-built_in">clone</span> https://github.com/SWivid/F5-TTS.git<br><span class="hljs-built_in">cd</span> F5-TTS<br>pip install -e .<br><br><span class="hljs-comment"># 命令行推理</span><br>f5-tts_infer --model F5TTS_v1_Base \<br>  --ref_audio reference.wav \<br>  --ref_text <span class="hljs-string">&quot;参考音频对应的文字&quot;</span> \<br>  --gen_text <span class="hljs-string">&quot;要合成的文字&quot;</span><br><br><span class="hljs-comment"># Web UI</span><br>python app.py<br></code></pre></td></tr></table></figure><p><strong>适合谁用</strong>：学术研究、架构探索，不适合直接上生产（有幻觉问题 + 许可证限制）。</p><hr><h3 id="6-Fish-Speech"><a href="#6-Fish-Speech" class="headerlink" title="6. Fish Speech"></a>6. Fish Speech</h3><p>Codec-based 方案，主打流式推理，Apache 2.0 许可证对商用友好。</p><h4 id="架构思路-5"><a href="#架构思路-5" class="headerlink" title="架构思路"></a>架构思路</h4><figure class="highlight fortran"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs fortran">文本 → Decoder-<span class="hljs-keyword">only</span> Transformer LM → VQGAN Codec 解码 → 波形<br></code></pre></td></tr></table></figure><p>VQGAN Codec + Transformer 语言模型，消除了独立的 vocoder 环节，支持流式推理。</p><h4 id="关键指标-4"><a href="#关键指标-4" class="headerlink" title="关键指标"></a>关键指标</h4><ul><li><strong>零样本克隆</strong>：约 10 秒参考音频（比 CosyVoice 多）</li><li><strong>多语言支持</strong></li><li><strong>流式推理</strong>：适合延迟敏感的实时应用</li></ul><h4 id="资源-5"><a href="#资源-5" class="headerlink" title="资源"></a>资源</h4><ul><li>GitHub：<a href="https://github.com/fishaudio/fish-speech">https://github.com/fishaudio/fish-speech</a></li><li>许可证：Apache 2.0</li></ul><h4 id="快速上手-5"><a href="#快速上手-5" class="headerlink" title="快速上手"></a>快速上手</h4><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><code class="hljs bash">git <span class="hljs-built_in">clone</span> https://github.com/fishaudio/fish-speech.git<br><span class="hljs-built_in">cd</span> fish-speech<br>pip install -e .<br><br><span class="hljs-comment"># Web UI</span><br>python -m fish_speech.webui<br><br><span class="hljs-comment"># API 服务</span><br>python -m fish_speech.api_server<br></code></pre></td></tr></table></figure><p><strong>适合谁用</strong>：需要流式输出、许可证偏好 Apache 2.0 的商业项目。克隆所需参考音频稍长是个小缺点。</p><hr><h3 id="7-Seed-TTS（字节跳动）"><a href="#7-Seed-TTS（字节跳动）" class="headerlink" title="7. Seed-TTS（字节跳动）"></a>7. Seed-TTS（字节跳动）</h3><p>字节的内部方案，模型权重未开放，只放出了评估代码。列在这里主要是因为它在学术圈常被当作评估基准，了解一下技术方向还是有价值的。</p><h4 id="架构"><a href="#架构" class="headerlink" title="架构"></a>架构</h4><p>提供两种变体：</p><ul><li><strong>Seed-TTS</strong>：大规模自回归语言模型</li><li><strong>Seed-TTS_DiT</strong>：非自回归扩散 Transformer 变体，端到端生成，不依赖预估音素时长</li></ul><h4 id="资源-6"><a href="#资源-6" class="headerlink" title="资源"></a>资源</h4><ul><li>评估代码：<a href="https://github.com/BytedanceSpeech/seed-tts-eval">https://github.com/BytedanceSpeech/seed-tts-eval</a></li><li>论文：<a href="https://arxiv.org/abs/2406.02430">https://arxiv.org/abs/2406.02430</a></li><li><strong>模型权重未公开</strong>，没法直接用</li></ul><hr><h2 id="MockingBird-还值得用吗？"><a href="#MockingBird-还值得用吗？" class="headerlink" title="MockingBird 还值得用吗？"></a>MockingBird 还值得用吗？</h2><p>先看看老架构的局限性：</p><figure class="highlight sqf"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs sqf">参考音频 → LSTM Encoder → <span class="hljs-number">256</span>维 <span class="hljs-built_in">speaker</span> embedding<br>                                ↓<br>文本 → Tacotron+GST → mel 频谱图 → HiFi-GAN → 波形<br></code></pre></td></tr></table></figure><p>核心问题：</p><ul><li>每个新说话人都要重新训练 synthesizer，没有真正意义上的零样本能力</li><li>三段模型需要独立训练和调优，部署复杂度高</li><li>只支持中文普通话</li><li>没有流式合成</li><li>自回归生成，速度慢</li></ul><p>和现在的方案对比：</p><table><thead><tr><th>能力</th><th>MockingBird</th><th>新一代项目</th></tr></thead><tbody><tr><td>克隆所需数据</td><td>数小时音频 + 数小时训练</td><td>3-10 秒（零样本）&#x2F; 1 分钟（微调）</td></tr><tr><td>语言</td><td>中文普通话</td><td>中英日韩粤 + 多方言</td></tr><tr><td>推理延迟</td><td>秒级</td><td>150ms 流式首包</td></tr><tr><td>模型结构</td><td>3 个独立模型</td><td>统一端到端</td></tr><tr><td>音质</td><td>中等</td><td>高（接近真人）</td></tr><tr><td>可控性</td><td>GST 全局风格</td><td>指令级情感&#x2F;语速&#x2F;韵律控制</td></tr></tbody></table><p>结论是：如果你现在还在用 MockingBird，是时候迁移了。</p><hr><h2 id="选型建议"><a href="#选型建议" class="headerlink" title="选型建议"></a>选型建议</h2><h3 id="按场景推荐"><a href="#按场景推荐" class="headerlink" title="按场景推荐"></a>按场景推荐</h3><table><thead><tr><th>需求</th><th>首选</th><th>备选</th><th>说明</th></tr></thead><tbody><tr><td>个人开发，快速跑通</td><td>GPT-SoVITS</td><td>ChatTTS</td><td>社区最大，教程多，1分钟数据微调</td></tr><tr><td>工业级部署，多语言</td><td>CosyVoice 3.0</td><td>Fish Speech</td><td>阿里长期维护，功能最全，流式延迟低</td></tr><tr><td>对话机器人&#x2F;客服</td><td>ChatTTS</td><td>CosyVoice</td><td>专门优化对话韵律</td></tr><tr><td>学术研究&#x2F;复现论文</td><td>MaskGCT</td><td>F5-TTS</td><td>均为 ICLR 2025，非自回归最新架构</td></tr><tr><td>商用（许可证干净）</td><td>CosyVoice &#x2F; Fish Speech</td><td>GPT-SoVITS</td><td>避开 AGPLv3 和 CC BY-NC-SA</td></tr><tr><td>GPU 资源有限</td><td>GPT-SoVITS</td><td>ChatTTS</td><td>较轻量，社区有 CPU 推理方案</td></tr><tr><td>粤语&#x2F;方言</td><td>CosyVoice 3.0</td><td>GPT-SoVITS</td><td>18+ 方言支持</td></tr></tbody></table><h3 id="从-MockingBird-迁移路线图"><a href="#从-MockingBird-迁移路线图" class="headerlink" title="从 MockingBird 迁移路线图"></a>从 MockingBird 迁移路线图</h3><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><code class="hljs yaml"><span class="hljs-string">当前：MockingBird</span> <span class="hljs-string">(SV2TTS)</span><br>  <span class="hljs-string">│</span><br>  <span class="hljs-string">├──</span> <span class="hljs-string">想要最顺滑的迁移？</span><br>  <span class="hljs-string">│</span>     <span class="hljs-string">└──</span> <span class="hljs-string">→</span> <span class="hljs-string">GPT-SoVITS（社区大、有整合包、WebUI</span> <span class="hljs-string">体验类似）</span><br>  <span class="hljs-string">│</span><br>  <span class="hljs-string">├──</span> <span class="hljs-string">需要多语言</span> <span class="hljs-string">/</span> <span class="hljs-string">工业部署？</span><br>  <span class="hljs-string">│</span>     <span class="hljs-string">└──</span> <span class="hljs-string">→</span> <span class="hljs-string">CosyVoice</span> <span class="hljs-number">3.0</span><span class="hljs-string">（功能最全、阿里长期维护）</span><br>  <span class="hljs-string">│</span><br>  <span class="hljs-string">├──</span> <span class="hljs-string">主要做对话场景？</span><br>  <span class="hljs-string">│</span>     <span class="hljs-string">└──</span> <span class="hljs-string">→</span> <span class="hljs-string">ChatTTS（韵律自然、专门对话优化）</span><br>  <span class="hljs-string">│</span><br>  <span class="hljs-string">└──</span> <span class="hljs-string">想跟进最新技术架构？</span><br>        <span class="hljs-string">└──</span> <span class="hljs-string">→</span> <span class="hljs-string">MaskGCT（非自回归前沿，ICLR</span> <span class="hljs-number">2025</span><span class="hljs-string">）</span><br></code></pre></td></tr></table></figure><hr><h2 id="硬件参考"><a href="#硬件参考" class="headerlink" title="硬件参考"></a>硬件参考</h2><table><thead><tr><th>项目</th><th>最低 GPU</th><th>推荐 GPU</th><th>模型大小</th><th>显存估算</th></tr></thead><tbody><tr><td>CosyVoice 3.0</td><td>RTX 3060</td><td>RTX 4090</td><td>~2GB</td><td>4-8 GB</td></tr><tr><td>GPT-SoVITS</td><td>GTX 1660</td><td>RTX 3080</td><td>~1GB</td><td>2-6 GB</td></tr><tr><td>ChatTTS</td><td>GTX 1060</td><td>RTX 3060</td><td>~1GB</td><td>2-4 GB</td></tr><tr><td>MaskGCT</td><td>RTX 3090</td><td>RTX 4090</td><td>~3GB</td><td>8-16 GB</td></tr><tr><td>F5-TTS</td><td>RTX 3060</td><td>RTX 4090</td><td>~2GB</td><td>4-8 GB</td></tr><tr><td>Fish Speech</td><td>RTX 3060</td><td>RTX 4090</td><td>~2GB</td><td>4-8 GB</td></tr></tbody></table><blockquote><p>以上为估算值，实际取决于推理配置、量化选项和输入长度。量化版本普遍能砍掉 30-50% 的显存占用。</p></blockquote><hr><h2 id="快速入门流程"><a href="#快速入门流程" class="headerlink" title="快速入门流程"></a>快速入门流程</h2><h3 id="环境准备"><a href="#环境准备" class="headerlink" title="环境准备"></a>环境准备</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><code class="hljs bash"><span class="hljs-comment"># 前置条件</span><br><span class="hljs-comment"># - CUDA Toolkit（推荐 11.8 或 12.x）</span><br><span class="hljs-comment"># - conda / miniconda</span><br><span class="hljs-comment"># - GPU 驱动正常</span><br><br><span class="hljs-comment"># 验证</span><br>nvidia-smi<br>python -c <span class="hljs-string">&quot;import torch; print(torch.cuda.is_available())&quot;</span><br></code></pre></td></tr></table></figure><h3 id="安装示例（以-GPT-SoVITS-为例）"><a href="#安装示例（以-GPT-SoVITS-为例）" class="headerlink" title="安装示例（以 GPT-SoVITS 为例）"></a>安装示例（以 GPT-SoVITS 为例）</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><code class="hljs bash">git <span class="hljs-built_in">clone</span> https://github.com/RVC-Boss/GPT-SoVITS.git<br><span class="hljs-built_in">cd</span> GPT-SoVITS<br>conda create -n gptsovits python=3.10<br>conda activate gptsovits<br>pip install -r requirements.txt<br>python webui.py<br></code></pre></td></tr></table></figure><h3 id="参考音频要求"><a href="#参考音频要求" class="headerlink" title="参考音频要求"></a>参考音频要求</h3><figure class="highlight asciidoc"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><code class="hljs asciidoc"><span class="hljs-bullet">- </span>格式：WAV（16kHz 或 48kHz，视项目要求）<br><span class="hljs-bullet">- </span>时长：零样本 3-10 秒，微调 1-5 分钟<br><span class="hljs-bullet">- </span>质量：清晰、无背景音乐、单人说话<br><span class="hljs-bullet">- </span>内容：尽量覆盖多种语调，避免只有单一情绪<br></code></pre></td></tr></table></figure><h3 id="合成流程"><a href="#合成流程" class="headerlink" title="合成流程"></a>合成流程</h3><figure class="highlight markdown"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><code class="hljs markdown">零样本模式（5 分钟跑通）：<br><span class="hljs-bullet">1.</span> 上传参考音频<br><span class="hljs-bullet">2.</span> 输入要合成的文本<br><span class="hljs-bullet">3.</span> 点击合成，试听效果<br><br>微调模式（效果更稳）：<br><span class="hljs-bullet">1.</span> 准备 1-5 分钟目标说话人音频<br><span class="hljs-bullet">2.</span> 自动标注（ASR 转文字 + 手动校对）<br><span class="hljs-bullet">3.</span> 训练（通常 5-30 分钟）<br><span class="hljs-bullet">4.</span> 用训练好的模型推理<br></code></pre></td></tr></table></figure><hr><h2 id="附录：资源汇总"><a href="#附录：资源汇总" class="headerlink" title="附录：资源汇总"></a>附录：资源汇总</h2><table><thead><tr><th>项目</th><th>GitHub</th><th>论文</th><th>文档&#x2F;社区</th></tr></thead><tbody><tr><td>CosyVoice 3.0</td><td><a href="https://github.com/FunAudioLLM/CosyVoice">FunAudioLLM&#x2F;CosyVoice</a></td><td><a href="https://arxiv.org/abs/2505.17589">arXiv</a></td><td>GitHub Wiki</td></tr><tr><td>GPT-SoVITS</td><td><a href="https://github.com/RVC-Boss/GPT-SoVITS">RVC-Boss&#x2F;GPT-SoVITS</a></td><td>—</td><td>B 站教程丰富</td></tr><tr><td>ChatTTS</td><td><a href="https://github.com/2noise/ChatTTS">2noise&#x2F;ChatTTS</a></td><td>—</td><td>GitHub Issues</td></tr><tr><td>MaskGCT</td><td><a href="https://github.com/amphion/MaskGCT">amphion&#x2F;MaskGCT</a></td><td><a href="https://arxiv.org/abs/2409.00750">arXiv</a></td><td><a href="https://github.com/open-mmlab/Amphion">Amphion 文档</a></td></tr><tr><td>F5-TTS</td><td><a href="https://github.com/SWivid/F5-TTS">SWivid&#x2F;F5-TTS</a></td><td><a href="https://arxiv.org/abs/2410.06885">arXiv</a></td><td>GitHub README</td></tr><tr><td>Fish Speech</td><td><a href="https://github.com/fishaudio/fish-speech">fishaudio&#x2F;fish-speech</a></td><td>—</td><td><a href="https://speech.fish.audio/">官方文档</a></td></tr><tr><td>Seed-TTS</td><td><a href="https://github.com/BytedanceSpeech/seed-tts-eval">BytedanceSpeech&#x2F;seed-tts-eval</a></td><td><a href="https://arxiv.org/abs/2406.02430">arXiv</a></td><td>仅评估代码</td></tr></tbody></table><hr><blockquote><p><strong>说明</strong>：文中的 benchmark 数据来自各项目官方论文和仓库，未经过独立标准化对比测试。语音质量这件事主观因素很强，建议自己拿实际数据跑一遍再做决策。这个领域发展很快，几个月内排名就可能变化。</p></blockquote>]]>
      </content:encoded>
    </item>
    <item>
      <title>
        <![CDATA[AI 编程指南：Claude Code & Codex 避坑与进阶实战]]>
      </title>
      <link>https://www.xuwx.top/2026/06/23/AI%E7%BC%96%E7%A8%8B%E6%8C%87%E5%8D%97-Claude-Code-%E5%92%8C-Codex-%E9%81%BF%E5%9D%91%E4%B8%8E%E8%BF%9B%E9%98%B6%E5%AE%9E%E6%88%98/</link>
      <description>用了 Claude Code 和 Codex 一段时间，踩了不少坑也摸出一些门道。一次一条主线、给验证别信嘴、清上下文别让它变笨——这三句话能挡掉大部分返工。</description>
      <author>Shine_ssr</author>
      <category domain="https://www.xuwx.top/categories/AI/">AI</category>
      <category domain="https://www.xuwx.top/categories/AI/%E5%BC%80%E5%8F%91%E5%B7%A5%E5%85%B7/">开发工具</category>
      <category domain="https://www.xuwx.top/tags/Claude-Code/">Claude Code</category>
      <category domain="https://www.xuwx.top/tags/Codex/">Codex</category>
      <category domain="https://www.xuwx.top/tags/AI%E7%BC%96%E7%A8%8B/">AI编程</category>
      <category domain="https://www.xuwx.top/tags/%E6%95%88%E7%8E%87%E5%B7%A5%E5%85%B7/">效率工具</category>
      <pubDate>Mon, 22 Jun 2026 16:00:00 GMT</pubDate>
      <content:encoded>
        <![CDATA[<h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>AI 编程工具这两年井喷式发展，光是「命令行 AI 编程助手」这个赛道，Anthropic 的 Claude Code 和 OpenAI 的 Codex 就让不少开发者选择困难。</p><p>更头疼的是：工具上手容易，用好不容易。</p><p>这篇文章是我长期使用 Claude Code 和 Codex 后，把官方文档、社区踩坑帖和自己的实战经验揉在一起，提炼出来的避坑要点和进阶干货，帮你少走弯路。</p><p><strong>适合谁读</strong>：</p><ul><li>还在观望、想选一个工具入门的新手</li><li>已经在用、但总觉得「AI 也就那样」的进阶用户</li><li>想从 Claude Code 迁移到 Codex（或反过来）的开发者</li></ul><hr><h2 id="一、先搞清楚：它俩到底是什么"><a href="#一、先搞清楚：它俩到底是什么" class="headerlink" title="一、先搞清楚：它俩到底是什么"></a>一、先搞清楚：它俩到底是什么</h2><h3 id="Claude-Code-——-Anthropic-出品的命令行编程搭档"><a href="#Claude-Code-——-Anthropic-出品的命令行编程搭档" class="headerlink" title="Claude Code —— Anthropic 出品的命令行编程搭档"></a>Claude Code —— Anthropic 出品的命令行编程搭档</h3><p>一句话：你在终端里给目标，它读整个项目、改文件、跑命令、跑测试，把活干完交给你验收。</p><p>核心形态是 CLI（命令行），也有 VS Code &#x2F; JetBrains 插件、桌面 App、网页版。</p><h3 id="Codex-——-OpenAI-出品的-AI-编程代理"><a href="#Codex-——-OpenAI-出品的-AI-编程代理" class="headerlink" title="Codex —— OpenAI 出品的 AI 编程代理"></a>Codex —— OpenAI 出品的 AI 编程代理</h3><p>同样是「代理模式」（想→做→看），但比 Claude Code 多了四种入口：桌面 App、CLI、IDE 扩展、云端 Web。</p><p>最大的差异：Codex 有个「云端」模式，任务可以丢到 OpenAI 服务器上跑，不占本机资源。</p><h3 id="核心相同点"><a href="#核心相同点" class="headerlink" title="核心相同点"></a>核心相同点</h3><p>两者底层逻辑一样：代理循环（想→做→看）——读代码、想方案、动手改、跑测试验证、不对就回头重来。</p><table><thead><tr><th>维度</th><th>Claude Code</th><th>Codex</th></tr></thead><tbody><tr><td>出品方</td><td>Anthropic</td><td>OpenAI</td></tr><tr><td>核心形态</td><td>命令行为主</td><td>多入口（App&#x2F;CLI&#x2F;IDE&#x2F;云端）</td></tr><tr><td>项目说明书</td><td><code>CLAUDE.md</code></td><td><code>AGENTS.md</code></td></tr><tr><td>配置文件</td><td><code>settings.json</code>（JSON）</td><td><code>config.toml</code>（TOML）</td></tr><tr><td>权限模型</td><td>权限模式 + allow&#x2F;deny 白名单</td><td>沙箱 + 审批（两个独立旋钮）</td></tr><tr><td>计费</td><td>订阅 &#x2F; API Key</td><td>ChatGPT 订阅 &#x2F; API Key</td></tr></tbody></table><hr><h2 id="二、避坑指南：七个最常踩的反模式"><a href="#二、避坑指南：七个最常踩的反模式" class="headerlink" title="二、避坑指南：七个最常踩的反模式"></a>二、避坑指南：七个最常踩的反模式</h2><p>用不顺 AI 编程工具，十有八九不是工具不行，是用法掉进了反模式。</p><h3 id="反模式-1：一句话塞一大堆需求"><a href="#反模式-1：一句话塞一大堆需求" class="headerlink" title="反模式 #1：一句话塞一大堆需求"></a>反模式 #1：一句话塞一大堆需求</h3><p><strong>症状</strong>：「帮我把登录模块重构了、加上单元测试、顺便优化下性能」</p><p><strong>为什么坑</strong>：它猜偏方向的概率极高，改一堆没用的，你还得擦屁股。</p><p><strong>正解</strong>：一次一条主线。大任务先用 Plan Mode（<code>/plan</code> 或 <code>Shift+Tab</code>）出方案，你确认了再动手。</p><h3 id="反模式-2：不写项目说明书-全塞进去"><a href="#反模式-2：不写项目说明书-全塞进去" class="headerlink" title="反模式 #2：不写项目说明书 &#x2F; 全塞进去"></a>反模式 #2：不写项目说明书 &#x2F; 全塞进去</h3><p><strong>症状</strong>：要么不写 <code>CLAUDE.md</code>（Claude Code）&#x2F; <code>AGENTS.md</code>（Codex），每天重复告诉它「这项目用 pnpm、测试跑 pytest」；要么把整本文档塞进去，规则被淹没。</p><p><strong>为什么坑</strong>：不写 &#x3D; 每次新会话都从零开始；写太多 &#x3D; 关键信息被稀释，还可能撑爆上下文。</p><p><strong>正解</strong>：一页纸精华须知。每写一行问自己：「删了它 Claude 会犯错吗？」不会就删。</p><table><thead><tr><th>✅ 该写</th><th>❌ 别写</th></tr></thead><tbody><tr><td>Claude 猜不到的 Bash 命令</td><td>它读代码就能搞懂的</td></tr><tr><td>跟默认不一样的代码风格</td><td>标准语言约定</td></tr><tr><td>测试指令、首选 runner</td><td>详细 API 文档（改成链接）</td></tr><tr><td>仓库礼仪、分支约定</td><td>经常变的信息</td></tr><tr><td>项目特有的架构决策</td><td>「写干净代码」这种废话</td></tr></tbody></table><h3 id="反模式-3：一个会话从早开到晚"><a href="#反模式-3：一个会话从早开到晚" class="headerlink" title="反模式 #3：一个会话从早开到晚"></a>反模式 #3：一个会话从早开到晚</h3><p><strong>症状</strong>：上下文窗口塞满，AI 越用越笨——变慢、变笼统、前后矛盾、反复问同样的问题。</p><p><strong>正解</strong>：</p><ul><li>切任务 → <code>/clear</code> 清屏重开</li><li>同任务太长 → <code>/compact</code> 压缩上下文</li><li>同一问题纠正两次以上 → 无条件 <code>/clear</code>，带着教训重开</li></ul><h3 id="反模式-4：把它当搜索引擎，说啥信啥"><a href="#反模式-4：把它当搜索引擎，说啥信啥" class="headerlink" title="反模式 #4：把它当搜索引擎，说啥信啥"></a>反模式 #4：把它当搜索引擎，说啥信啥</h3><p><strong>症状</strong>：「这个 API 怎么用？」它一本正经给你编个答案，参数名都是错的。</p><p><strong>正解</strong>：让它给证据，不是声称成功。它说「修好了」≠ 真修好了，要看测试输出、看命令返回。</p><h3 id="反模式-5：不给验证办法"><a href="#反模式-5：不给验证办法" class="headerlink" title="反模式 #5：不给验证办法"></a>反模式 #5：不给验证办法</h3><p><strong>症状</strong>：「改好了吗？」「改好了。」你信了，然后上线炸了。</p><p><strong>正解</strong>：prompt 结尾挂一句——「改完跑 pytest，全绿了再告诉我」。一句挡掉一大半返工。</p><h3 id="反模式-6：无脑开-bypassPermissions"><a href="#反模式-6：无脑开-bypassPermissions" class="headerlink" title="反模式 #6：无脑开 bypassPermissions"></a>反模式 #6：无脑开 bypassPermissions</h3><p><strong>症状</strong>：嫌确认弹窗烦，一把全放。</p><p><strong>为什么坑</strong>：裸奔，连提示注入都不防。AI 执行了不该执行的命令，你都不知道。</p><p><strong>正解</strong>：精细化 <code>allowed/denied tools</code>，默认从严，逐步放宽。</p><h3 id="反模式-7：让它「调查一下」不给范围"><a href="#反模式-7：让它「调查一下」不给范围" class="headerlink" title="反模式 #7：让它「调查一下」不给范围"></a>反模式 #7：让它「调查一下」不给范围</h3><p><strong>症状</strong>：「帮我看看这个项目有什么问题」→ 它读几百个文件，把上下文窗口烧爆。</p><p><strong>正解</strong>：给具体目录、文件清单。「看 <code>src/auth/</code> 下的认证逻辑」比「看看有啥问题」强十倍。</p><h3 id="恶性循环"><a href="#恶性循环" class="headerlink" title="恶性循环"></a>恶性循环</h3><figure class="highlight"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><code class="hljs">需求一堆 + 无范围调查 → 上下文塞满<br>        ↓<br>       它犯错<br>        ↓<br>你觉得它不行 → 不给验证 / 全自动裸奔<br>        ↓<br>更糟 → 「这 AI 也就那样」<br></code></pre></td></tr></table></figure><p>破局点：拆需求 + 清上下文 + 给验证 + 收窄范围。</p><hr><h2 id="三、进阶干货：从「能用」到「用好」"><a href="#三、进阶干货：从「能用」到「用好」" class="headerlink" title="三、进阶干货：从「能用」到「用好」"></a>三、进阶干货：从「能用」到「用好」</h2><h3 id="1-提示词的四件套配方"><a href="#1-提示词的四件套配方" class="headerlink" title="1. 提示词的四件套配方"></a>1. 提示词的四件套配方</h3><table><thead><tr><th>要素</th><th>它在回答什么</th><th>示例</th></tr></thead><tbody><tr><td><strong>目标（Goal）</strong></td><td>你到底要改什么、建什么</td><td>「给 login 函数加输入校验」</td></tr><tr><td><strong>上下文（Context）</strong></td><td>哪些文件、报错相关</td><td>用 <code>@</code> 引用文件，报错整段粘贴</td></tr><tr><td><strong>约束（Constraints）</strong></td><td>要守什么规范、不许碰什么</td><td>「别引新库、保持兼容」</td></tr><tr><td><strong>验收（Done when）</strong></td><td>什么条件算干完</td><td>「跑 pytest 全绿」</td></tr></tbody></table><p>最常漏的是验收——少这一格，它觉得「能跑」就交差，bug 留给你。</p><p><strong>烂提问 vs 好提问</strong>：</p><table><thead><tr><th>❌ 烂提问</th><th>✅ 好提问</th></tr></thead><tbody><tr><td>「修一下登录的错误」</td><td>「用户报告会话超时后登录失败。查 <code>src/auth/</code> 认证流程，重点看 token 刷新。先写失败测试再修」</td></tr><tr><td>「给 foo.py 加测试」</td><td>「覆盖用户已登出的边界情况，别用 mock」</td></tr><tr><td>「加个日历组件」</td><td>「照 <code>HotDogWidget.php</code> 模式实现日历组件，能选月份前后翻年，不引新库」</td></tr></tbody></table><h3 id="2-复杂任务先出计划"><a href="#2-复杂任务先出计划" class="headerlink" title="2. 复杂任务先出计划"></a>2. 复杂任务先出计划</h3><p>越复杂越模糊的活儿，越先出计划再动手。</p><ul><li>一句话说清 diff 的小活儿 → 直接干</li><li>说不清的 → <code>/plan</code> 或 <code>Shift+Tab</code> 切 Plan Mode</li><li>自己没想清楚 → 让它「先别写，先 challenge 我，把模糊想法问成具体方案」</li></ul><blockquote><p>反面教材：我让 Codex 从回调迁 async&#x2F;await，图省事没出计划，结果它顺手「优化」了全局错误处理，diff 糊成一团，最后 <code>git reset</code> 重来。</p></blockquote><h3 id="3-MCP：给-AI-接上外部世界"><a href="#3-MCP：给-AI-接上外部世界" class="headerlink" title="3. MCP：给 AI 接上外部世界"></a>3. MCP：给 AI 接上外部世界</h3><p>MCP（Model Context Protocol）是一套开源标准，给 AI 统一外接服务的「扩展坞」——接一次，GitHub&#x2F;Jira&#x2F;数据库&#x2F;Figma 的工具全摆到它面前。</p><p><strong>两种 Server 形态</strong>：</p><table><thead><tr><th>形态</th><th>场景</th><th>示例</th></tr></thead><tbody><tr><td><strong>stdio（本地）</strong></td><td>连本地数据库、浏览器</td><td><code>claude mcp add mydb -- npx @modelcontextprotocol/server-postgres</code></td></tr><tr><td><strong>HTTP（远程）</strong></td><td>云服务</td><td>GitHub、Sentry、Figma</td></tr></tbody></table><p><strong>信任红线</strong>：第三方 server 是第三方代码，官方不替你审计。优先用大厂官方 server，连数据库用只读凭据。</p><h3 id="4-子代理：上下文隔离的杀手锏"><a href="#4-子代理：上下文隔离的杀手锏" class="headerlink" title="4. 子代理：上下文隔离的杀手锏"></a>4. 子代理：上下文隔离的杀手锏</h3><p>子代理 &#x3D; 独立上下文 + 独立人设 + 独立工具的「外包小弟」。在自己房间干活，只把结论交回来。</p><p><strong>解决什么问题</strong>：</p><table><thead><tr><th>问题</th><th>说明</th></tr></thead><tbody><tr><td><strong>隔离上下文</strong></td><td>脏活（跑测试、翻日志）在自己房间扛，不污染主线</td></tr><tr><td><strong>专精任务</strong></td><td>给某类反复出现的活定一个专门干的人</td></tr><tr><td><strong>可并行</strong></td><td>互不相干的活同时甩给多个子代理</td></tr></tbody></table><p><strong>什么时候别用</strong>：频繁来回、共享上下文、快速小改 → 留在主对话。产出一堆中间垃圾、可自包含只回结论 → 才外包。</p><p><strong>怎么建</strong>：<code>/agents</code> 交互式填表，或手写 <code>.claude/agents/name.md</code>。</p><h3 id="5-Skills：把重复劳动变成可复用能力"><a href="#5-Skills：把重复劳动变成可复用能力" class="headerlink" title="5. Skills：把重复劳动变成可复用能力"></a>5. Skills：把重复劳动变成可复用能力</h3><p>Skill &#x3D; <code>SKILL.md</code>（frontmatter + 正文）+ 可选配套文件（模板&#x2F;脚本&#x2F;示例）。</p><p>核心机制是渐进式披露：平时只露一句 description、用到才展开全文。装再多 Skill 也不撑上下文。</p><p><strong>什么时候该写 Skill</strong>：当你发现自己反复粘同一套步骤时。</p><p><strong>三种来源</strong>：</p><ol><li>内置：<code>/code-review</code>、<code>/debug</code>、<code>/batch</code> 等，开箱即用</li><li>插件附带：带命名空间 <code>/插件名:skill名</code></li><li>自己写：反复粘同一套步骤时，就该写 Skill</li></ol><h3 id="6-记忆系统：让-AI-记住你的调教"><a href="#6-记忆系统：让-AI-记住你的调教" class="headerlink" title="6. 记忆系统：让 AI 记住你的调教"></a>6. 记忆系统：让 AI 记住你的调教</h3><table><thead><tr><th>工具</th><th>记忆机制</th></tr></thead><tbody><tr><td>Claude Code</td><td><code>CLAUDE.md</code>（手动）+ 自动记忆（默认开）</td></tr><tr><td>Codex</td><td><code>AGENTS.md</code>（手动）+ Memories（默认关，异步生成，有地区限制）</td></tr></tbody></table><p>铁律：当 AI 同一个错误犯了第二次，让它复盘把教训补进项目说明书——说明书被真实摩擦喂大，而非拍脑袋。</p><hr><h2 id="四、订阅与计费：别让钱包意外失血"><a href="#四、订阅与计费：别让钱包意外失血" class="headerlink" title="四、订阅与计费：别让钱包意外失血"></a>四、订阅与计费：别让钱包意外失血</h2><h3 id="Claude-Code-三条路"><a href="#Claude-Code-三条路" class="headerlink" title="Claude Code 三条路"></a>Claude Code 三条路</h3><table><thead><tr><th>方案</th><th>月成本参考</th><th>适合谁</th></tr></thead><tbody><tr><td>官方按量计费</td><td>浮动，重度 $150-250+</td><td>想用原生 Claude</td></tr><tr><td>官方订阅</td><td>固定月费</td><td>高频用，固定支出</td></tr><tr><td>国产 Coding Plan</td><td>低，常有促销</td><td>国内用户，能接受国产模型</td></tr></tbody></table><p><strong>国产套餐大坑</strong>：买了套餐 ≠ 走套餐。Base URL 必须带 <code>coding</code> 字样，Key 用套餐专属的！</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs bash"><span class="hljs-comment"># ✅ 走套餐</span><br>https://ark.cn-beijing.volces.com/api/coding<br><br><span class="hljs-comment"># ❌ 按量扣费（你以为走了套餐，其实在烧钱）</span><br>https://ark.cn-beijing.volces.com/api/v3<br></code></pre></td></tr></table></figure><h3 id="Codex-两套计费"><a href="#Codex-两套计费" class="headerlink" title="Codex 两套计费"></a>Codex 两套计费</h3><table><thead><tr><th>维度</th><th>ChatGPT 订阅</th><th>API Key</th></tr></thead><tbody><tr><td>收费</td><td>固定月费 + 用量限额</td><td>按 token 实时扣费</td></tr><tr><td>适合</td><td>个人日常高频</td><td>CI&#x2F;CD &#x2F; 自动化</td></tr></tbody></table><p>口诀：人用订阅，机器用 API Key。</p><h3 id="省钱四招"><a href="#省钱四招" class="headerlink" title="省钱四招"></a>省钱四招</h3><table><thead><tr><th>❌ 坏习惯</th><th>✅ 好习惯</th><th>省在哪</th></tr></thead><tbody><tr><td>一个会话从早用到晚</td><td>任务间 <code>/clear</code></td><td>不重发陈旧上下文</td></tr><tr><td>全程挂旗舰模型</td><td>默认中端，难题才切旗舰</td><td>单价更低</td></tr><tr><td>含糊需求直接开干</td><td>复杂任务先 Plan Mode</td><td>少返工</td></tr><tr><td>「改进下代码」</td><td>「给 login 函数加输入校验」</td><td>少读无关文件</td></tr></tbody></table><h3 id="三个管理命令"><a href="#三个管理命令" class="headerlink" title="三个管理命令"></a>三个管理命令</h3><table><thead><tr><th>命令</th><th>用途</th></tr></thead><tbody><tr><td><code>/usage</code></td><td>当前会话 token 用量和估算花销</td></tr><tr><td><code>/usage-credits</code></td><td>设月度使用上限（防超支）</td></tr><tr><td><code>/status</code></td><td>核对版本、模型、账户、连通性</td></tr></tbody></table><hr><h2 id="五、迁移指南：Claude-Code-↔-Codex"><a href="#五、迁移指南：Claude-Code-↔-Codex" class="headerlink" title="五、迁移指南：Claude Code ↔ Codex"></a>五、迁移指南：Claude Code ↔ Codex</h2><p>你 90% 的心智模型能直接搬——要重新认的只是「东西放哪、叫什么名」。</p><h3 id="概念对照表"><a href="#概念对照表" class="headerlink" title="概念对照表"></a>概念对照表</h3><table><thead><tr><th>Claude Code</th><th>Codex</th><th>关系</th></tr></thead><tbody><tr><td><code>CLAUDE.md</code></td><td><code>AGENTS.md</code></td><td>换名</td></tr><tr><td><code>~/.claude/settings.json</code>（JSON）</td><td><code>~/.codex/config.toml</code>（TOML）</td><td>换格式</td></tr><tr><td>权限模式 + allow&#x2F;deny 规则</td><td>沙箱 + 审批（两个独立旋钮）</td><td>换思路</td></tr><tr><td><code>claude -p</code></td><td><code>codex exec</code></td><td>换名</td></tr><tr><td><code>CLAUDE.local.md</code></td><td><code>AGENTS.override.md</code></td><td>机制不同</td></tr></tbody></table><h3 id="三个必栽的坑"><a href="#三个必栽的坑" class="headerlink" title="三个必栽的坑"></a>三个必栽的坑</h3><p><strong>坑 1：项目说明书的临时覆写机制不同</strong></p><ul><li>Claude Code：<code>CLAUDE.local.md</code> 是附加个人内容</li><li>Codex：<code>AGENTS.override.md</code> 是跳过同级 <code>AGENTS.md</code></li></ul><p><strong>坑 2：配置文件不是翻译，是重写</strong></p><p>JSON → TOML，最常犯的错：行尾加逗号（肌肉记忆），Codex 会报解析错。</p><p><strong>坑 3：权限模型根本性不同</strong></p><ul><li>Claude Code：按工具列白名单</li><li>Codex：沙箱（能动多大）+ 审批（问不问你）两个独立旋钮</li></ul><p>「不问」≠「放权」——<code>read-only + never</code> 是「随便读，但一句都别打扰我」。</p><h3 id="斜杠命令对照"><a href="#斜杠命令对照" class="headerlink" title="斜杠命令对照"></a>斜杠命令对照</h3><table><thead><tr><th>你想干的事</th><th>Claude Code</th><th>Codex</th></tr></thead><tbody><tr><td>切模型</td><td><code>/model</code></td><td><code>/model</code> ✅</td></tr><tr><td>压缩上下文</td><td><code>/compact</code></td><td><code>/compact</code> ✅</td></tr><tr><td>清屏重开</td><td><code>/clear</code></td><td><code>/clear</code> ✅</td></tr><tr><td>生成项目说明书</td><td><code>/init</code></td><td><code>/init</code> ✅</td></tr><tr><td>看改动 diff</td><td><code>/diff</code></td><td><code>/diff</code> ✅</td></tr><tr><td>审查改动</td><td><code>/review</code></td><td><code>/review</code> ✅</td></tr><tr><td>调权限</td><td><code>Shift+Tab</code></td><td><code>/permissions</code> ❌</td></tr></tbody></table><hr><h2 id="六、最佳实践速查表"><a href="#六、最佳实践速查表" class="headerlink" title="六、最佳实践速查表"></a>六、最佳实践速查表</h2><h3 id="Claude-Code-六大法则"><a href="#Claude-Code-六大法则" class="headerlink" title="Claude Code 六大法则"></a>Claude Code 六大法则</h3><ol><li><strong>给可自己验收的方式</strong> —— prompt 结尾挂「做完跑测试并验证」</li><li><strong>先探索再编程</strong> —— 不熟的代码先 Plan Mode 出方案</li><li><strong>把话说具体</strong> —— 点文件、限场景、给参照例子</li><li><strong>CLAUDE.md 写精不写多</strong> —— 每行问「删了它 Claude 会犯错吗？」</li><li><strong>一跑偏立刻拽回来</strong> —— 纠正满三次，无条件 <code>/clear</code> 重开</li><li><strong>用顺了再横向铺开</strong> —— Writer&#x2F;Reviewer 互审、<code>claude -p</code> 进脚本、subagent 干脏活</li></ol><h3 id="Codex-七条落地实践"><a href="#Codex-七条落地实践" class="headerlink" title="Codex 七条落地实践"></a>Codex 七条落地实践</h3><ol><li><strong>换心态</strong> —— 当队友而非工具，调教越久越顺手</li><li><strong>AGENTS.md 写到位</strong> —— <code>/init</code> 生成脚手架，改成项目真实的样子</li><li><strong>提示四件套填空</strong> —— 目标 + 上下文 + 约束 + 验收</li><li><strong>复杂活先出计划</strong> —— <code>/plan</code> 或 <code>Shift+Tab</code></li><li><strong>权限分档</strong> —— 默认从严，逐步放宽</li><li><strong>让它自验证</strong> —— 跑测试、跑 lint、确认行为对得上需求</li><li><strong>线程管理一条线一件事</strong> —— 一个任务一条线程，不要一个项目从早开到晚</li></ol><h3 id="踩坑-❌-正确-✅-对照表"><a href="#踩坑-❌-正确-✅-对照表" class="headerlink" title="踩坑 ❌ &#x2F; 正确 ✅ 对照表"></a>踩坑 ❌ &#x2F; 正确 ✅ 对照表</h3><table><thead><tr><th>❌ 踩坑</th><th>✅ 正确做法</th></tr></thead><tbody><tr><td>把持久规则塞进提示</td><td>持久的搬进项目说明书或 Skill</td></tr><tr><td>没告诉它构建&#x2F;测试命令</td><td>项目说明书写清，让它能看到成果</td></tr><tr><td>多步复杂活跳过规划</td><td>先 <code>/plan</code> 再动手</td></tr><tr><td>还没摸清工作流就给满权限</td><td>默认从严，逐步放宽</td></tr><tr><td>一个项目一条线程</td><td>一个任务一条线程</td></tr><tr><td>多条线程同时改同批文件</td><td>各开一个 git worktree</td></tr><tr><td>不靠谱的活急着做自动化</td><td>先手动跑稳再沉淀</td></tr></tbody></table><hr><h2 id="七、如何选择？"><a href="#七、如何选择？" class="headerlink" title="七、如何选择？"></a>七、如何选择？</h2><h3 id="选-Claude-Code-如果你："><a href="#选-Claude-Code-如果你：" class="headerlink" title="选 Claude Code 如果你："></a>选 Claude Code 如果你：</h3><ul><li>习惯命令行，喜欢终端工作流</li><li>主要用 Anthropic 的模型（Claude 系列）</li><li>想要更成熟的 Skills 生态和自动记忆</li><li>团队已经在用 Anthropic 的服务</li></ul><h3 id="选-Codex-如果你："><a href="#选-Codex-如果你：" class="headerlink" title="选 Codex 如果你："></a>选 Codex 如果你：</h3><ul><li>喜欢多入口（桌面 App、CLI、IDE、云端都想要）</li><li>想用云端模式，任务不占本机资源</li><li>已经有 ChatGPT Plus&#x2F;Pro 订阅</li><li>需要企业管理功能（SSO&#x2F;SCIM&#x2F;RBAC）</li></ul><h3 id="两个都试试"><a href="#两个都试试" class="headerlink" title="两个都试试"></a>两个都试试</h3><p>核心逻辑一样，斜杠命令大半同名。花半天跑一遍入门流程，哪个手感好用哪个。</p><hr><h2 id="结语"><a href="#结语" class="headerlink" title="结语"></a>结语</h2><p>AI 编程工具不是银弹，但用对了确实能提效。</p><p>关键就三句话：</p><ol><li><strong>拆需求，别让它猜</strong> —— 一次一条主线，复杂任务先出计划</li><li><strong>给验证，别信它说的</strong> —— 让它跑测试、给证据，「看起来对」不算数</li><li><strong>清上下文，别让它变笨</strong> —— 任务间 <code>/clear</code>，纠正三次无条件重开</li></ol><p>别光看，动手跑一遍才是你的。</p><hr><p><em>最后更新：2026-06-23</em></p>]]>
      </content:encoded>
    </item>
    <item>
      <title>企业级WMS策略体系设计思想：从规则到系统的架构实践</title>
      <link>https://www.xuwx.top/2026/06/10/%E4%BC%81%E4%B8%9A%E7%BA%A7WMS%E7%AD%96%E7%95%A5%E4%BD%93%E7%B3%BB%E8%AE%BE%E8%AE%A1%E6%80%9D%E6%83%B3/</link>
      <description>
        <![CDATA[<blockquote>
<p>本文基于实际项目经验，总结了一套适用于仓储管理系统（WMS）的策略体系设计方法论。这套方法论不仅适用于WMS，也可以推广到其他需要复杂规则引擎的企业系统中。</p>
</blockquote>
<h2 id="前言"><a href="#前言" c]]>
      </description>
      <author>Shine_ssr</author>
      <category domain="https://www.xuwx.top/categories/%E6%9E%B6%E6%9E%84%E8%AE%BE%E8%AE%A1/">架构设计</category>
      <category domain="https://www.xuwx.top/categories/%E6%9E%B6%E6%9E%84%E8%AE%BE%E8%AE%A1/%E5%90%8E%E7%AB%AF%E5%BC%80%E5%8F%91/">后端开发</category>
      <category domain="https://www.xuwx.top/tags/%E6%9E%B6%E6%9E%84%E8%AE%BE%E8%AE%A1/">架构设计</category>
      <category domain="https://www.xuwx.top/tags/WMS/">WMS</category>
      <category domain="https://www.xuwx.top/tags/%E7%AD%96%E7%95%A5%E6%A8%A1%E5%BC%8F/">策略模式</category>
      <category domain="https://www.xuwx.top/tags/%E8%A7%84%E5%88%99%E5%BC%95%E6%93%8E/">规则引擎</category>
      <category domain="https://www.xuwx.top/tags/%E4%BC%81%E4%B8%9A%E7%BA%A7%E5%BC%80%E5%8F%91/">企业级开发</category>
      <pubDate>Wed, 10 Jun 2026 07:30:00 GMT</pubDate>
      <content:encoded>
        <![CDATA[<blockquote><p>本文基于实际项目经验，总结了一套适用于仓储管理系统（WMS）的策略体系设计方法论。这套方法论不仅适用于WMS，也可以推广到其他需要复杂规则引擎的企业系统中。</p></blockquote><h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>在企业级软件开发中，我们经常面临这样的挑战：业务规则复杂、变化频繁、不同场景需要不同的处理方式。传统的硬编码方式已经无法满足业务快速变化的需求。</p><p>本文将分享一套完整的策略体系设计思想，帮助开发者构建灵活、可扩展、可维护的规则引擎系统。</p><h2 id="一、策略体系的核心价值"><a href="#一、策略体系的核心价值" class="headerlink" title="一、策略体系的核心价值"></a>一、策略体系的核心价值</h2><h3 id="1-1-将业务经验配置化"><a href="#1-1-将业务经验配置化" class="headerlink" title="1.1 将业务经验配置化"></a>1.1 将业务经验配置化</h3><p>企业运营中积累了大量业务经验，例如：</p><ul><li>什么时间段可以收货？</li><li>哪些物料需要优先处理？</li><li>什么情况下需要审批？</li></ul><p>这些经验如果写死在代码中，每次调整都需要开发人员介入。通过策略体系，我们可以将这些经验转化为可配置的规则。</p><h3 id="1-2-将人工判断系统化"><a href="#1-2-将人工判断系统化" class="headerlink" title="1.2 将人工判断系统化"></a>1.2 将人工判断系统化</h3><p>传统模式下，很多决策依赖人工判断：</p><ul><li>是否允许预约？</li><li>是否需要检验？</li><li>是否可以入库？</li></ul><p>策略体系可以将这些判断逻辑系统化，减少人为错误，提高处理效率。</p><h3 id="1-3-将业务差异策略化"><a href="#1-3-将业务差异策略化" class="headerlink" title="1.3 将业务差异策略化"></a>1.3 将业务差异策略化</h3><p>不同仓库、不同供应商、不同物料可能需要不同的处理规则：</p><ul><li>A仓库夏季收货时间是8:00-18:00，冬季是8:30-17:30</li><li>供应商A的物料需要全检，供应商B的物料可以免检</li><li>关键物料必须按批次隔离存放</li></ul><p>策略体系可以灵活处理这些业务差异。</p><h3 id="1-4-将异常场景流程化"><a href="#1-4-将异常场景流程化" class="headerlink" title="1.4 将异常场景流程化"></a>1.4 将异常场景流程化</h3><p>异常场景的处理往往最复杂：</p><ul><li>非窗口期预约怎么办？</li><li>逾期到货如何处理？</li><li>库容不足时是否允许入库？</li></ul><p>策略体系可以定义异常处理流程，包括审批、拦截、提醒等多种方式。</p><h3 id="1-5-为智能化决策打基础"><a href="#1-5-为智能化决策打基础" class="headerlink" title="1.5 为智能化决策打基础"></a>1.5 为智能化决策打基础</h3><p>当策略规则、命中记录、异常记录、审批结果逐渐沉淀后，可以进一步支持：</p><ul><li>供应商评价分析</li><li>仓库负荷优化</li><li>排队策略优化</li><li>缺料风险预测</li></ul><h2 id="二、策略分类设计"><a href="#二、策略分类设计" class="headerlink" title="二、策略分类设计"></a>二、策略分类设计</h2><h3 id="2-1-企业级策略"><a href="#2-1-企业级策略" class="headerlink" title="2.1 企业级策略"></a>2.1 企业级策略</h3><p><strong>定义</strong>：公司统一规则，影响整个系统或多个业务单元。</p><p><strong>特点</strong>：</p><ul><li>影响范围大</li><li>通常是公司级统一规则</li><li>不是针对某个单一对象</li><li>一旦启用，会影响多个业务流程</li></ul><p><strong>示例</strong>：</p><ul><li>是否启用供应商预约机制</li><li>是否启用批次管理</li><li>是否启用异常审批</li><li>是否启用供应商履约统计</li></ul><h3 id="2-2-资源策略"><a href="#2-2-资源策略" class="headerlink" title="2.2 资源策略"></a>2.2 资源策略</h3><p><strong>定义</strong>：针对具体资源（如仓库、库区、设备）的规则。</p><p><strong>特点</strong>：</p><ul><li>针对特定资源</li><li>影响该资源相关的所有业务</li><li>可以独立配置</li></ul><p><strong>示例</strong>：</p><ul><li>仓库收货时间策略</li><li>库区存储规则</li><li>设备使用规则</li></ul><h3 id="2-3-对象策略"><a href="#2-3-对象策略" class="headerlink" title="2.3 对象策略"></a>2.3 对象策略</h3><p><strong>定义</strong>：针对业务对象（如供应商、物料、客户）的规则。</p><p><strong>特点</strong>：</p><ul><li>针对特定业务对象</li><li>影响该对象的所有业务</li><li>可以按对象类型配置</li></ul><p><strong>示例</strong>：</p><ul><li>供应商供货策略</li><li>物料存储策略</li><li>客户信用策略</li></ul><h3 id="2-4-流程策略"><a href="#2-4-流程策略" class="headerlink" title="2.4 流程策略"></a>2.4 流程策略</h3><p><strong>定义</strong>：针对业务流程（如预约、到货、检验）的规则。</p><p><strong>特点</strong>：</p><ul><li>针对特定业务流程</li><li>影响流程的各个环节</li><li>可以按流程节点配置</li></ul><p><strong>示例</strong>：</p><ul><li>预约审批策略</li><li>到货检验策略</li><li>入库上架策略</li></ul><h2 id="三、策略模型设计"><a href="#三、策略模型设计" class="headerlink" title="三、策略模型设计"></a>三、策略模型设计</h2><h3 id="3-1-策略的基本要素"><a href="#3-1-策略的基本要素" class="headerlink" title="3.1 策略的基本要素"></a>3.1 策略的基本要素</h3><p>一条完整的策略通常包含以下要素：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><code class="hljs text">策略分类：属于哪类策略<br>策略对象：管理谁<br>适用范围：对哪些对象生效<br>生效环节：在哪个业务节点生效<br>触发条件：什么情况下命中规则<br>执行动作：命中后系统执行什么<br>异常处理：不满足规则时如何处理<br>审批要求：是否需要审批<br>优先级：多条策略冲突时谁优先<br>生效状态：是否启用<br>生效时间：何时生效<br>留痕要求：是否记录日志<br>统计分析：是否影响统计报表<br></code></pre></td></tr></table></figure><h3 id="3-2-策略的执行链路"><a href="#3-2-策略的执行链路" class="headerlink" title="3.2 策略的执行链路"></a>3.2 策略的执行链路</h3><p>策略在系统中的执行链路可以抽象为：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><code class="hljs text">业务操作发起<br>  → 获取业务上下文<br>  → 匹配策略对象<br>  → 判断触发条件<br>  → 计算策略结果<br>  → 执行业务动作<br>  → 处理异常/审批<br>  → 更新业务状态<br>  → 记录策略命中日志<br></code></pre></td></tr></table></figure><h3 id="3-3-策略的优先级设计"><a href="#3-3-策略的优先级设计" class="headerlink" title="3.3 策略的优先级设计"></a>3.3 策略的优先级设计</h3><p>当多个策略同时命中时，需要明确优先级：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs text">紧急业务 &gt; 特殊审批 &gt; 常规规则 &gt; 默认规则<br></code></pre></td></tr></table></figure><p>例如：</p><ul><li>生产紧急缺料 &gt; 普通库容限制</li><li>供应商整改审批 &gt; 常规预约规则</li><li>关键物料优先 &gt; 普通物料排队</li></ul><h2 id="四、策略设计原则"><a href="#四、策略设计原则" class="headerlink" title="四、策略设计原则"></a>四、策略设计原则</h2><h3 id="4-1-策略要可分类"><a href="#4-1-策略要可分类" class="headerlink" title="4.1 策略要可分类"></a>4.1 策略要可分类</h3><p>每条规则必须先知道属于哪类策略，否则后续无法落表、配置、维护和扩展。</p><h3 id="4-2-策略要有作用对象"><a href="#4-2-策略要有作用对象" class="headerlink" title="4.2 策略要有作用对象"></a>4.2 策略要有作用对象</h3><p>如果没有明确对象，系统无法判断规则作用在哪里。</p><h3 id="4-3-策略要有触发环节"><a href="#4-3-策略要有触发环节" class="headerlink" title="4.3 策略要有触发环节"></a>4.3 策略要有触发环节</h3><p>同一条规则，在不同环节的处理方式可能不同。例如”逾期”：</p><ul><li>创建预约时发现逾期</li><li>到货确认时发现逾期</li><li>点收时发现逾期</li><li>检验完成时发现逾期</li></ul><p>不同环节的处理方式可能完全不同。</p><h3 id="4-4-策略要有触发条件"><a href="#4-4-策略要有触发条件" class="headerlink" title="4.4 策略要有触发条件"></a>4.4 策略要有触发条件</h3><p>触发条件必须尽量明确。</p><p><strong>不推荐</strong>：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs text">严重逾期要审批。<br></code></pre></td></tr></table></figure><p><strong>推荐</strong>：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs text">预约到货时间超过采购订单要求交期 24 小时以上，视为严重逾期，需要采购主管审批。<br></code></pre></td></tr></table></figure><h3 id="4-5-策略要有执行结果"><a href="#4-5-策略要有执行结果" class="headerlink" title="4.5 策略要有执行结果"></a>4.5 策略要有执行结果</h3><p>命中策略后，系统必须知道做什么。常见执行结果包括：</p><ul><li>允许&#x2F;禁止</li><li>提醒&#x2F;预警</li><li>标记异常</li><li>自动分配&#x2F;排队</li><li>进入审批</li><li>变更状态</li><li>通知人员</li><li>记录日志</li><li>纳入统计</li></ul><h3 id="4-6-策略要有异常处理方式"><a href="#4-6-策略要有异常处理方式" class="headerlink" title="4.6 策略要有异常处理方式"></a>4.6 策略要有异常处理方式</h3><p>如果规则不满足，需要明确处理方式：</p><ul><li>不允许提交</li><li>允许暂存但不允许生效</li><li>允许提交但必须审批</li><li>允许继续流转但标记异常</li><li>转人工处理</li></ul><h3 id="4-7-策略要有优先级"><a href="#4-7-策略要有优先级" class="headerlink" title="4.7 策略要有优先级"></a>4.7 策略要有优先级</h3><p>多个策略可能同时命中，必须明确哪个策略优先。</p><h3 id="4-8-策略要可追溯"><a href="#4-8-策略要可追溯" class="headerlink" title="4.8 策略要可追溯"></a>4.8 策略要可追溯</h3><p>建议所有关键策略命中都要留痕：</p><ul><li>命中了哪条策略</li><li>为什么命中</li><li>系统做了什么动作</li><li>谁审批</li><li>审批结果是什么</li><li>是否影响统计</li></ul><h2 id="五、策略落地实践"><a href="#五、策略落地实践" class="headerlink" title="五、策略落地实践"></a>五、策略落地实践</h2><h3 id="5-1-第一阶段：收集业务规则"><a href="#5-1-第一阶段：收集业务规则" class="headerlink" title="5.1 第一阶段：收集业务规则"></a>5.1 第一阶段：收集业务规则</h3><p>先让业务侧按统一模板输出规则，不急于开发。优先收集：</p><ul><li>核心业务规则</li><li>异常处理规则</li><li>审批流程规则</li><li>统计报表规则</li></ul><h3 id="5-2-第二阶段：进行策略归类"><a href="#5-2-第二阶段：进行策略归类" class="headerlink" title="5.2 第二阶段：进行策略归类"></a>5.2 第二阶段：进行策略归类</h3><p>技术侧拿到业务规则后，先判断归属：</p><ul><li>已有模型可配置</li><li>已有模型需扩展字段</li><li>需要新增策略表</li><li>需要新增流程节点</li><li>需要接入审批流程</li><li>需要新增统计报表</li></ul><h3 id="5-3-第三阶段：形成策略矩阵"><a href="#5-3-第三阶段：形成策略矩阵" class="headerlink" title="5.3 第三阶段：形成策略矩阵"></a>5.3 第三阶段：形成策略矩阵</h3><p>建议整理成策略矩阵：</p><table><thead><tr><th>策略名称</th><th>分类</th><th>对象</th><th>环节</th><th>条件</th><th>动作</th><th>审批</th><th>优先级</th></tr></thead><tbody><tr><td>非窗口期预约审批</td><td>预约</td><td>预约单</td><td>提交预约</td><td>不在收货时间</td><td>进入审批</td><td>是</td><td>中</td></tr><tr><td>优先排队策略</td><td>排队</td><td>车辆</td><td>排队</td><td>紧急物料</td><td>优先排队</td><td>否</td><td>高</td></tr><tr><td>批次隔离存储</td><td>存储</td><td>物料</td><td>上架</td><td>批次管理</td><td>隔离分配</td><td>否</td><td>高</td></tr></tbody></table><h3 id="5-4-第四阶段：沉淀为系统配置和流程"><a href="#5-4-第四阶段：沉淀为系统配置和流程" class="headerlink" title="5.4 第四阶段：沉淀为系统配置和流程"></a>5.4 第四阶段：沉淀为系统配置和流程</h3><p>最终把策略分成几种落地方式：</p><ol><li><p><strong>配置类</strong></p><ul><li>例如：收货时间、二维码有效期、是否启用排队</li></ul></li><li><p><strong>规则类</strong></p><ul><li>例如：优先排队、逾期判断、库容不足判断</li></ul></li><li><p><strong>流程类</strong></p><ul><li>例如：异常审批、检验流程、差异处理</li></ul></li><li><p><strong>状态类</strong></p><ul><li>例如：预约单状态、排队任务状态、收货状态</li></ul></li><li><p><strong>统计类</strong></p><ul><li>例如：逾期率、排队时长、异常次数</li></ul></li></ol><h2 id="六、策略输出规范"><a href="#六、策略输出规范" class="headerlink" title="六、策略输出规范"></a>六、策略输出规范</h2><h3 id="6-1-业务侧输出规范"><a href="#6-1-业务侧输出规范" class="headerlink" title="6.1 业务侧输出规范"></a>6.1 业务侧输出规范</h3><p>业务侧输出策略时，建议按以下模板：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br></pre></td><td class="code"><pre><code class="hljs text">【策略名称】<br>例如：非窗口期预约审批策略<br><br>【策略分类】<br>企业策略 / 资源策略 / 对象策略 / 流程策略<br><br>【适用对象】<br>供应商 / 物料 / 仓库 / 预约单 / 车辆等<br><br>【适用范围】<br>全部 / 指定范围<br><br>【生效环节】<br>创建预约 / 提交预约 / 到货确认 / 排队 / 点收 / 检验 / 上架<br><br>【触发条件】<br>什么情况下命中该策略<br><br>【系统处理结果】<br>允许 / 禁止 / 提醒 / 预警 / 标记异常 / 自动分配 / 进入审批<br><br>【异常处理方式】<br>不满足条件时怎么处理<br><br>【审批要求】<br>是否需要审批；审批通过/驳回后怎么走<br><br>【优先级】<br>与其他策略冲突时谁优先<br><br>【记录与统计】<br>是否记录日志；是否进入报表<br></code></pre></td></tr></table></figure><h3 id="6-2-技术侧承接规范"><a href="#6-2-技术侧承接规范" class="headerlink" title="6.2 技术侧承接规范"></a>6.2 技术侧承接规范</h3><p>技术侧拿到业务规则后，需要判断：</p><ul><li>能不能配置？</li><li>要不要开发？</li><li>影响哪个流程？</li><li>是否需要审批？</li><li>状态怎么变化？</li><li>是否需要统计？</li></ul><h2 id="七、总结"><a href="#七、总结" class="headerlink" title="七、总结"></a>七、总结</h2><p>策略体系设计的核心思想是：</p><ol><li><strong>抽象</strong>：将业务规则抽象为可配置的策略</li><li><strong>分类</strong>：按业务领域和影响范围对策略分类</li><li><strong>标准化</strong>：统一策略的输入输出格式</li><li><strong>流程化</strong>：将策略执行嵌入业务流程</li><li><strong>可追溯</strong>：记录策略执行过程和结果</li></ol><p>这套设计思想不仅适用于WMS系统，也可以推广到其他需要复杂规则引擎的企业系统中，如：</p><ul><li>订单管理系统</li><li>供应链管理系统</li><li>金融风控系统</li><li>人力资源管理系统</li></ul><p>关键是要理解业务规则的本质，将其转化为可配置、可执行、可追溯的策略体系。</p>]]>
      </content:encoded>
    </item>
    <item>
      <title>使用onelogin开源工具库java-saml-toolkit实现基于SAML2的SSO</title>
      <link>https://www.xuwx.top/2020/09/03/%E4%BD%BF%E7%94%A8onelogin%E5%BC%80%E6%BA%90%E5%B7%A5%E5%85%B7%E5%BA%93java-saml-toolkit%E5%AE%9E%E7%8E%B0%E5%9F%BA%E4%BA%8ESAML2%E7%9A%84SSO/</link>
      <description>
        <![CDATA[<h3 id="1-为什么使用SAML2"><a href="#1-为什么使用SAML2" class="headerlink" title="1.为什么使用SAML2"></a>1.为什么使用SAML2</h3><p>saml是用于web浏览器单点登录的基于XML的标准,由OA]]>
      </description>
      <author>Shine_ssr</author>
      <category domain="https://www.xuwx.top/categories/SSO/">SSO</category>
      <category domain="https://www.xuwx.top/categories/SSO/SAML2/">SAML2</category>
      <category domain="https://www.xuwx.top/tags/SAML2/">SAML2</category>
      <category domain="https://www.xuwx.top/tags/onelogin/">onelogin</category>
      <category domain="https://www.xuwx.top/tags/SSO/">SSO</category>
      <pubDate>Thu, 03 Sep 2020 07:52:37 GMT</pubDate>
      <content:encoded>
        <![CDATA[<h3 id="1-为什么使用SAML2"><a href="#1-为什么使用SAML2" class="headerlink" title="1.为什么使用SAML2"></a>1.为什么使用SAML2</h3><p>saml是用于web浏览器单点登录的基于XML的标准,由OASIS安全服务技术委员会定义。该标准自2002年以来一直存在，但近来由于其以下优点而变得流行：</p><ul><li>可用性-从门户或Intranet的一键式访问，深层链接，消除密码和自动续订会话，使用户的生活更轻松。</li><li>安全性-SAML基于强大的数字签名进行身份验证和完整性，是世界上最大，最具安全意识的企业所依赖的安全单点登录协议。</li><li>速度-SAML速度很快。<br>一种浏览器重定向就是将用户安全登录到应用程序所需的全部操作。</li><li>网页仿冒防范-如果您没有应用程序密码，就不会被欺骗在虚假的登录页面上输入密码。</li><li>IT友好-SAML简化了IT的使用寿命，因为它集中了身份验证，提供了更大的可见性并使目录集成更加容易。</li></ul><h3 id="2-SAML-Java工具包使我们可以将Java应用程序转换为可以连接到IdP（身份提供者）的SP（服务提供者）。"><a href="#2-SAML-Java工具包使我们可以将Java应用程序转换为可以连接到IdP（身份提供者）的SP（服务提供者）。" class="headerlink" title="2.SAML Java工具包使我们可以将Java应用程序转换为可以连接到IdP（身份提供者）的SP（服务提供者）。"></a>2.SAML Java工具包使我们可以将Java应用程序转换为可以连接到IdP（身份提供者）的SP（服务提供者）。</h3><p>支持:</p><ul><li>SSO和SLO(由SP启动和由IdP启动).</li><li>断言和nameId加密.</li><li>断言签名.</li><li>消息签名: AuthNRequest, LogoutRequest, LogoutResponses.</li><li>启用 断言消费者服务(ACS) 端点.</li><li>启用 单一注销服务(SLS) 端点.</li><li>发布SP metadata(可以签名).</li></ul><h3 id="3-开始使用"><a href="#3-开始使用" class="headerlink" title="3.开始使用"></a>3.开始使用</h3><h5 id="1-工具包引入"><a href="#1-工具包引入" class="headerlink" title="1.工具包引入"></a>1.工具包引入</h5><blockquote><p>Maven引入方式</p></blockquote><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs xml"><span class="hljs-tag">&lt;<span class="hljs-name">dependency</span>&gt;</span><br>    <span class="hljs-tag">&lt;<span class="hljs-name">groupId</span>&gt;</span>com.onelogin<span class="hljs-tag">&lt;/<span class="hljs-name">groupId</span>&gt;</span><br>    <span class="hljs-tag">&lt;<span class="hljs-name">artifactId</span>&gt;</span>java-saml<span class="hljs-tag">&lt;/<span class="hljs-name">artifactId</span>&gt;</span><br>    <span class="hljs-tag">&lt;<span class="hljs-name">version</span>&gt;</span>2.5.0<span class="hljs-tag">&lt;/<span class="hljs-name">version</span>&gt;</span><br><span class="hljs-tag">&lt;/<span class="hljs-name">dependency</span>&gt;</span><br></code></pre></td></tr></table></figure><blockquote><p>lib方式引入</p></blockquote><p>只需要 把 java-saml.jar 与java-saml-core.jar 添加即可(可以通过拉下来的项目直接打包 支持源码修改以适应具体使用情况)</p><h5 id="2-使用java-saml-toolkit所提供的简单SP-demo工程"><a href="#2-使用java-saml-toolkit所提供的简单SP-demo工程" class="headerlink" title="2.使用java-saml-toolkit所提供的简单SP demo工程"></a>2.使用java-saml-toolkit所提供的简单SP demo工程</h5><blockquote><p>从<a href="https://github.com/onelogin/java-saml">git</a>上面获取demo项目</p></blockquote><p>项目包含三哥模块,其中core是核心包, toolkit是对外工具集合,samples是项目提供的SP demo 工程.</p><blockquote><p>samples项目介绍</p></blockquote><ul><li>resources 目录下的onelogin.saml.properties 是该工具包的核心配置文件(所有配置都在这边文件里,正式项目中需要放入class 根目录即可读取到)</li><li>index.jsp Web应用程序的首页。</li><li>dologin.jsp SP启动的SSO端点。</li><li>dologout.jsp SP启动的SLO端点。</li><li>acs.jsp服务提供商声明消费者服务端点。</li><li>attrs.jsp显示从SAMLResponse收集的属性。</li><li>sls.jsp服务提供者单一注销服务端点。</li><li>metadata.jsp发布SP元数据。</li></ul><h3 id="4-IDP-服务搭建"><a href="#4-IDP-服务搭建" class="headerlink" title="4.IDP 服务搭建"></a>4.IDP 服务搭建</h3><p>这里我们使用oneLogin 提供的IDP服务来进行开发测试SAML协议,该服务需要注册开发者账户后获取.下面将介绍如何搭建</p><h5 id="1-在OneLogin中创建应用连接器"><a href="#1-在OneLogin中创建应用连接器" class="headerlink" title="1.在OneLogin中创建应用连接器"></a>1.在OneLogin中创建应用连接器</h5><ul><li>登录 OneLogin.</li><li>Go to Apps &gt; Add Apps.</li><li>搜索 SAML Test Connector.</li><li>选择 SAML Test Connector (IdP w&#x2F; attr) app.</li><li>如果需要，请编辑显示名称。如果使用demo1应用程序，请输入demo1</li><li>接受默认值并单击保存。</li></ul><h5 id="2-在onelogin-saml-properties中配置-IDP-身份提供者参数"><a href="#2-在onelogin-saml-properties中配置-IDP-身份提供者参数" class="headerlink" title="2.在onelogin.saml.properties中配置(IDP)身份提供者参数"></a>2.在onelogin.saml.properties中配置(IDP)身份提供者参数</h5><ul><li>从SSO选项卡中复制值，并将其粘贴到onelogin.saml.properties的“ idp”（身份提供者，以onelogin.saml2.idp开头的参数）部分，如下所示</li></ul><table><thead><tr><th>复制SSO标签字段值</th><th>settings.json位置</th></tr></thead><tbody><tr><td>Issuer URL</td><td>onelogin.saml2.idp.entityid</td></tr><tr><td>SAML 2.0 Endpoint (HTTP)</td><td>onelogin.saml2.idp.single_sign_on_service.url</td></tr><tr><td>SLO Endpoint (HTTP)</td><td>onelogin.saml2.idp.single_logout_service.url</td></tr><tr><td>X.509 Certificate &gt; View Details</td><td>onelogin.saml2.idp.x509cert</td></tr></tbody></table><p>保存配置后进入下一步</p><h5 id="3-在onelogin-saml-properties中定义-SP-服务提供商的参数"><a href="#3-在onelogin-saml-properties中定义-SP-服务提供商的参数" class="headerlink" title="3.在onelogin.saml.properties中定义(SP)服务提供商的参数"></a>3.在onelogin.saml.properties中定义(SP)服务提供商的参数</h5><p>以我们demo项目根路径(<a href="http://localhost:8080/)%E4%B8%BA%E4%BE%8B">http://localhost:8080/)为例</a> 我们的具体参数为以下</p><ul><li>onelogin.saml2.sp.entityid &#x3D; <a href="http://localhost:8080/metadata.jsp">http://localhost:8080/metadata.jsp</a></li><li>onelogin.saml2.sp.assertion_consumer_service.url &#x3D; <a href="http://localhost:8080/acs.jsp">http://localhost:8080/acs.jsp</a></li><li>onelogin.saml2.sp.single_logout_service.url &#x3D; <a href="http://localhost:8000/sls.jsp">http://localhost:8000/sls.jsp</a></li></ul><p> 对于onelogin.saml2.sp.nameid格式，将unspecified更改为emailAddress.在正式项目中根据实际情况来进行配置,这里是目前是OneLogin使用的值。</p><p>保存配置后进入onelogin连接器的配置选项卡,然后将值从onelogin.saml.properties复制到&#96;&#96;配置’’选项卡字段中，如下所示。</p><table><thead><tr><th>复制settings.json值</th><th>配置选项卡字段</th></tr></thead><tbody><tr><td>onelogin.saml2.sp.assertion_consumer_service.url</td><td>ACS (Consumer) URL</td></tr><tr><td>onelogin.saml2.sp.assertion_consumer_service.url</td><td>Recipient</td></tr><tr><td>onelogin.saml2.sp.single_logout_service.url</td><td>Single Logout URL</td></tr><tr><td>onelogin.saml2.sp.entityid</td><td>Audience</td></tr></tbody></table><ul><li>RelayState 参数可以不用填写 在发起sso代码处可以指定</li><li>现在，将ACS（消费者）URL验证器设置为。*。<br>最后进行保存.</li></ul><blockquote><p>IDP端的配置可以实时修改保存(在 正式项目环境中我们通常 用metadata文件来进行配置交互)</p></blockquote><h5 id="4-部署运行samples项目"><a href="#4-部署运行samples项目" class="headerlink" title="4.部署运行samples项目"></a>4.部署运行samples项目</h5><h3 id="5-onelogin-saml-properties配置文件解读"><a href="#5-onelogin-saml-properties配置文件解读" class="headerlink" title="5.onelogin.saml.properties配置文件解读"></a>5.onelogin.saml.properties配置文件解读</h3><blockquote><p>以下是我的demo环境 的配置文件</p></blockquote><figure class="highlight apache"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br><span class="line">152</span><br><span class="line">153</span><br><span class="line">154</span><br><span class="line">155</span><br><span class="line">156</span><br><span class="line">157</span><br><span class="line">158</span><br><span class="line">159</span><br><span class="line">160</span><br><span class="line">161</span><br><span class="line">162</span><br><span class="line">163</span><br><span class="line">164</span><br><span class="line">165</span><br><span class="line">166</span><br><span class="line">167</span><br><span class="line">168</span><br><span class="line">169</span><br><span class="line">170</span><br><span class="line">171</span><br></pre></td><td class="code"><pre><code class="hljs apache"><span class="hljs-comment"># 该配置表示加密环境 正式中为true(只要是报文加密都为true)</span><br><span class="hljs-attribute">onelogin</span>.saml2.strict = true<br><span class="hljs-comment"># debug 模式 调试日志打印</span><br><span class="hljs-attribute">onelogin</span>.saml2.debug = true<br><br><span class="hljs-comment">#  SP端配置</span><br><span class="hljs-comment"># 协议配置 基本上默认 没有特殊修改</span><br><br><span class="hljs-comment"># SP entityId</span><br><span class="hljs-comment">#  Identifier of the SP entity  (must be a URI)</span><br><span class="hljs-attribute">onelogin</span>.saml2.sp.entityid = http://localhost:<span class="hljs-number">8080</span>/metadata.jsp<br><br><span class="hljs-comment"># SP 断言解析服务地址</span><br><span class="hljs-comment"># Specifies info about where and how the &lt;AuthnResponse&gt; message MUST be</span><br><span class="hljs-comment">#  returned to the requester, in this case our SP.</span><br><span class="hljs-comment"># URL Location where the &lt;Response&gt; from the IdP will be returned</span><br><span class="hljs-attribute">onelogin</span>.saml2.sp.assertion_consumer_service.url = http://localhost:<span class="hljs-number">8080</span>/acs.jsp<br><br><span class="hljs-comment"># SAML protocol binding to be used when returning the &lt;Response&gt;</span><br><span class="hljs-comment"># message.  Onelogin Toolkit supports for this endpoint the</span><br><span class="hljs-comment"># HTTP-POST binding only</span><br><span class="hljs-attribute">onelogin</span>.saml2.sp.assertion_consumer_service.binding = urn:oasis:names:tc:SAML:<span class="hljs-number">2</span>.<span class="hljs-number">0</span>:bindings:HTTP-POST<br><br><span class="hljs-comment">#单点登出 服务地址 主要是提供给 IDP端用于接收登出响应的</span><br><span class="hljs-comment"># Specifies info about where and how the &lt;Logout Response&gt; message MUST be</span><br><span class="hljs-comment"># returned to the requester, in this case our SP.</span><br><span class="hljs-attribute">onelogin</span>.saml2.sp.single_logout_service.url = http://localhost:<span class="hljs-number">8080</span>/sls.jsp<br><br><span class="hljs-comment"># SAML protocol binding to be used when returning the &lt;LogoutResponse&gt; or sending the &lt;LogoutRequest&gt;</span><br><span class="hljs-comment"># message.  Onelogin Toolkit supports for this endpoint the</span><br><span class="hljs-comment"># HTTP-Redirect binding only</span><br><span class="hljs-attribute">onelogin</span>.saml2.sp.single_logout_service.binding = urn:oasis:names:tc:SAML:<span class="hljs-number">2</span>.<span class="hljs-number">0</span>:bindings:HTTP-Redirect<br><br><span class="hljs-comment"># nameID 格式 一般使用 unspecified 默认参数 </span><br><span class="hljs-comment"># Specifies constraints on the name identifier to be used to</span><br><span class="hljs-comment"># represent the requested subject.</span><br><span class="hljs-comment"># Take a look on lib/Saml2/Constants.php to see the NameIdFormat supported</span><br><span class="hljs-attribute">onelogin</span>.saml2.sp.nameidformat = urn:oasis:names:tc:SAML:<span class="hljs-number">1</span>.<span class="hljs-number">1</span>:nameid-format:emailAddress<br><br><br><span class="hljs-comment"># sp端用于报文加密签名使用到的证书</span><br><span class="hljs-comment"># 这里我们可以自签的x509格式证书 也可以使用pem格式通过 以下方式转换(我使用的是https证书 other下载而来的)</span><br><span class="hljs-comment"># 公钥转换 openssl x509 -in x509cert.pem -text -out Cert.pem</span><br><span class="hljs-comment"># 私钥转换 openssl pkcs8 -topk8 -inform pem -nocrypt -in sp.rsa_key -outform pem -out sp.pem</span><br><span class="hljs-comment"># 证书粘贴注意不要前后注释 并保持在一行上 证书解析对空格换行符敏感</span><br><br><span class="hljs-comment"># Usually x509cert and privateKey of the SP are provided by files placed at</span><br><span class="hljs-comment"># the certs folder. But we can also provide them with the following parameters</span><br><span class="hljs-attribute">onelogin</span>.saml2.sp.x509cert =MIICRDCCAa2gAwIBAgIBADANBgkqhkiG9w0BAQ0FADA/MQswCQYDVQQGEwJjbjEOMAwGA1UECAwFaHViZWkxDDAKBgNVBAoMA1RTTDESMBAGA1UEAwwJMTI3LjAuMC4xMB4XDTIwMDMwOTE0MzAxN1oXDTIxMDMwOTE0MzAxN1owPzELMAkGA1UEBhMCY24xDjAMBgNVBAgMBWh1YmVpMQwwCgYDVQQKDANUU0wxEjAQBgNVBAMMCTEyNy4wLjAuMTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAsi27eDSbpKh4J3snBktGjLxSDVotU3BEwuz9AZs8rXzC6aZmfLa7E6TOgq8+aaaOgjMGkSCotsLv91REvD/lnc4wsO4AXRePzL4duPFyJZK/XHxpKS+kSLredQwgt5yN0KeJotXOor5HKuGV8lS9yIYRkxpFA8Rglr1NS2WQSy0CAwEAAaNQME4wHQYDVR0OBBYEFJaUIDCkzQqhVE08KDn8WiSrPvvIMB8GA1UdIwQYMBaAFJaUIDCkzQqhVE08KDn8WiSrPvvIMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQENBQADgYEAJhLq5AQerT29AdoYUIvyxo8ce8BS2qqp+WU6dnWB8pUEQ163nC1goPZfE7gKRjpR3MdGU4FWz1PAe4ZAolcvGbqAcOdd4IxEFcXQam1QWEvGaRCflA5KlvhdPyQglhaM5EnuwJ4eFry9VgFFgmaFtBuq+aPo6o2W+E6DQfsXIXY=<br><br><span class="hljs-comment"># Requires Format PKCS#8   BEGIN PRIVATE KEY     </span><br><span class="hljs-comment"># If you have     PKCS#1   BEGIN RSA PRIVATE KEY  convert it by   openssl pkcs8 -topk8 -inform pem -nocrypt -in sp.rsa_key -outform pem -out sp.pem</span><br><span class="hljs-attribute">onelogin</span>.saml2.sp.privatekey =MIICdQIBADANBgkqhkiG9w0BAQEFAASCAl8wggJbAgEAAoGBALItu3g0m6SoeCd7JwZLRoy8Ug1aLVNwRMLs/QGbPK18wummZny2uxOkzoKvPmmmjoIzBpEgqLbC7/dURLw/<span class="hljs-number">5</span>Z3OMLDuAF0Xj8y+HbjxciWSv1x8aSkvpEi63nUMILecjdCniaLVzqK+RyrhlfJUvciGEZMaRQPEYJa9TUtlkEstAgMBAAECgYA4zCE1aTewHk/m7ff7pqU/mYxiWzuVnHUe2eKwz2ZhOyL6ziNfX/R0h5WYzPoNPZ2x3UgbnRiK4csSwVcD1y5/PZzMoXaedxEl7ertKUkomvQQVPz8SAEyzlufqxmQVAdNJIW3EZlZq/YDtEv5ZYEXHxDi5cE65Kf7rgxHlFWBoQJBAOj1jfTfCbkyhefnTp7TDrMHAaORWUi1h0BUAiIvfE7oHXE4O0x+R6dTn8w80bP0ZdD7GssAqLxv13MRDq6j8rUCQQDDzSc7/<span class="hljs-number">2</span>UdykO5MliTg9/W5riHYuFgY6M0S4+Eq/diSaUyF5H36gGkr5YVcBhpdz0iayiKO2oXuaYVPKVn6GmZAkA8vOERKiG/<span class="hljs-number">3</span>nNZPk6aTE710GrV+ax8r4+e7whLX3Qaopwii9WyIO6PqtbsCiNmtt7g+MdIjFhyIPrcbmMUl3xNAkBXHqkeYPy0zJJljKksubiW/gGM+<span class="hljs-number">8</span>ocATlUw2oQNhPUf8ApEaO5Ez238QhucXnrM0rYTaW0G8uQ0uG7AUj3esnBAkB7jjXJloxpWaz/cT8Ystqe/oL6JKgyXsbHj9nVYnKwXCZY1TM9X9tE3nP9pyTO7w0Np4WadkH1goyVKRpkdMbL<br><br><span class="hljs-comment"># IDP端配置</span><br><br><span class="hljs-comment"># IDP  entityId</span><br><span class="hljs-comment"># Identifier of the IdP entity  (must be a URI)</span><br><span class="hljs-attribute">onelogin</span>.saml2.idp.entityid = https://app.onelogin.com/saml/metadata/<span class="hljs-number">2</span>edb5038-be70-<span class="hljs-number">40</span>f5-ad3b-<span class="hljs-number">2</span>de9d00ab1a3<br><br><span class="hljs-comment"># SSO endpoint info of the IdP. (Authentication Request protocol)</span><br><span class="hljs-comment"># URL Target of the IdP where the SP will send the Authentication Request Message</span><br><span class="hljs-attribute">onelogin</span>.saml2.idp.single_sign_on_service.url = https://westinfosoft-dev.onelogin.com/trust/saml2/http-post/sso/<span class="hljs-number">2</span>edb5038-be70-<span class="hljs-number">40</span>f5-ad3b-<span class="hljs-number">2</span>de9d00ab1a3<br><span class="hljs-comment"># SAML protocol binding to be used when returning the &lt;Response&gt;</span><br><span class="hljs-comment"># message.  Onelogin Toolkit supports for this endpoint the</span><br><span class="hljs-comment"># HTTP-Redirect binding only</span><br><span class="hljs-attribute">onelogin</span>.saml2.idp.single_sign_on_service.binding = urn:oasis:names:tc:SAML:<span class="hljs-number">2</span>.<span class="hljs-number">0</span>:bindings:HTTP-Redirect<br><br><span class="hljs-comment"># SLO endpoint info of the IdP.</span><br><span class="hljs-comment"># URL Location of the IdP where the SP will send the SLO Request</span><br><span class="hljs-attribute">onelogin</span>.saml2.idp.single_logout_service.url =https://westinfosoft-dev.onelogin.com/trust/saml2/http-redirect/slo/<span class="hljs-number">1095020</span><br><br><span class="hljs-comment"># Optional SLO Response endpoint info of the IdP.</span><br><span class="hljs-comment"># URL Location of the IdP where the SP will send the SLO Response. If left blank, same URL as onelogin.saml2.idp.single_logout_service.url will be used.</span><br><span class="hljs-comment"># Some IdPs use a separate URL for sending a logout request and response, use this property to set the separate response url</span><br><span class="hljs-attribute">onelogin</span>.saml2.idp.single_logout_service.response.url =<br><br><span class="hljs-comment"># SAML protocol binding to be used when returning the &lt;Response&gt;</span><br><span class="hljs-comment"># message.  Onelogin Toolkit supports for this endpoint the</span><br><span class="hljs-comment"># HTTP-Redirect binding only</span><br><span class="hljs-attribute">onelogin</span>.saml2.idp.single_logout_service.binding = urn:oasis:names:tc:SAML:<span class="hljs-number">2</span>.<span class="hljs-number">0</span>:bindings:HTTP-Redirect<br><br><span class="hljs-comment"># 正式项目中 sp端都是从SP_metadata 中获取 的</span><br><span class="hljs-comment"># Public x509 certificate of the IdP</span><br><span class="hljs-attribute">onelogin</span>.saml2.idp.x509cert =MIID6zCCAtOgAwIBAgIUFzBUjmLFkpZwWqy5YSpvAcWFUOswDQYJKoZIhvcNAQEFBQAwSjEVMBMGA1UECgwMd2VzdGluZm9zb2Z0MRUwEwYDVQQLDAxPbmVMb2dpbiBJZFAxGjAYBgNVBAMMEU9uZUxvZ2luIEFjY291bnQgMB4XDTIwMDMwOTEyMjIwMVoXDTI1MDMwOTEyMjIwMVowSjEVMBMGA1UECgwMd2VzdGluZm9zb2Z0MRUwEwYDVQQLDAxPbmVMb2dpbiBJZFAxGjAYBgNVBAMMEU9uZUxvZ2luIEFjY291bnQgMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAp2ooweqHRLI/L7SPe1DrlvdyeRQGUnmXbBPZ/lj6pWEBhu6mgwHDLcC+<span class="hljs-number">4</span>wknZKQqq4EGCtoE9Tk1/bMRDxU7kPdzyZfhh/W3M8TDIB7gR/nbkMKTPBfcC2xNQ7n2gK6q9801B6zwcAoPhdfkV0M6O9mIMlqwscLG3Xyrn/+<span class="hljs-number">82</span>wrpMSVl85tlkS9BprzYVdre6B599QAcTSh3WSE9cbIKyVxb27XlfuRcmbSYD5EW67PtlutOE03WlQKagX6HHrnpdIUXZWSt8xjXnVxBINZeq8GPU4kow6glLZG6jrd2kh7qTa1meCHxdtjjvJXpsJASKlizlmUCPjpKtncdju6aTQIDAQABo4HIMIHFMAwGA1UdEwEB/wQCMAAwHQYDVR0OBBYEFOsEW3B51SXIjuFbKqN3d5eaMTJsMIGFBgNVHSMEfjB8gBTrBFtwedUlyI7hWyqjd3eXmjEybKFOpEwwSjEVMBMGA1UECgwMd2VzdGluZm9zb2Z0MRUwEwYDVQQLDAxPbmVMb2dpbiBJZFAxGjAYBgNVBAMMEU9uZUxvZ2luIEFjY291bnQgghQXMFSOYsWSlnBarLlhKm8BxYVQ6zAOBgNVHQ8BAf8EBAMCB4AwDQYJKoZIhvcNAQEFBQADggEBABaMiNIq/SYKeYezFvKJx7RB9eECSzbyBZYjpYt55XtJV0UWKaHvjvtRiT7XTgqQ/S9uUFPBqOLk09jBAOt1UiLELyuzX6KFC0ghwL6y7BLWL/W7AsxUQCiOAimyGUMTxAcjvFsqWQX3n6eSUr9y0LWqVz9esTj0joxApbejRNiW7Y4Mz/b1OEHT40UN9HxIC2IvQ5/tiU/c84wopOOeHtxb/hYKk1HQnPY2tfkJvKqJJZY8SIG8GlEPlVfwcm1mRrts8VMvbysTAsOJdLhLGLh8SsFkZ6jTTRed3XU+fwKARgg25+MsAQbsaVuXvMOUFlpYwcBhmmU5VseEPpJt3HU=<br><br><span class="hljs-comment"># 以下是 指纹模式的配置 不过官方不建议使用 应为hash碰撞的问题 </span><br><span class="hljs-comment"># Instead of use the whole x509cert you can use a fingerprint</span><br><span class="hljs-comment"># (openssl x509 -noout -fingerprint -in &quot;idp.crt&quot; to generate it,</span><br><span class="hljs-comment"># or add for example the -sha256 , -sha384 or -sha512 parameter)</span><br><span class="hljs-comment">#</span><br><span class="hljs-comment"># If a fingerprint is provided, then the certFingerprintAlgorithm is required in order to</span><br><span class="hljs-comment"># let the toolkit know which Algorithm was used. Possible values: sha1, sha256, sha384 or sha512</span><br><span class="hljs-comment"># &#x27;sha1&#x27; is the default value.</span><br><span class="hljs-comment"># onelogin.saml2.idp.certfingerprint = 3E:3B:0D:FA:F2:80:B2:0E:95:46:36:07:9A:78:BD:04:CC:76:CE:A8</span><br><span class="hljs-comment"># onelogin.saml2.idp.certfingerprint_algorithm = sha1</span><br><br><br><span class="hljs-comment"># Security settings</span><br><br><span class="hljs-comment">#安全配置 在演示项目中使用不多 不过在正式环境中 需要注意开启对应的加密项</span><br><br><span class="hljs-comment"># Indicates that the nameID of the &lt;samlp:logoutRequest&gt; sent by this SP</span><br><span class="hljs-comment"># will be encrypted.</span><br><span class="hljs-attribute">onelogin</span>.saml2.security.nameid_encrypted = false<br><br><span class="hljs-comment">#认证请求的加密</span><br><span class="hljs-comment"># Indicates whether the &lt;samlp:AuthnRequest&gt; messages sent by this SP</span><br><span class="hljs-comment"># will be signed.              [The Metadata of the SP will offer this info]</span><br><span class="hljs-attribute">onelogin</span>.saml2.security.authnrequest_signed = false<br><br><span class="hljs-comment">#登出请求的加密</span><br><span class="hljs-comment"># Indicates whether the &lt;samlp:logoutRequest&gt; messages sent by this SP</span><br><span class="hljs-comment"># will be signed.</span><br><span class="hljs-attribute">onelogin</span>.saml2.security.logoutrequest_signed = false<br><br><span class="hljs-comment">#登出响应的加密</span><br><span class="hljs-comment"># Indicates whether the &lt;samlp:logoutResponse&gt; messages sent by this SP</span><br><span class="hljs-comment"># will be signed.</span><br><span class="hljs-attribute">onelogin</span>.saml2.security.logoutresponse_signed = false<br><br><span class="hljs-comment"># Indicates a requirement for the &lt;samlp:Response&gt;, &lt;samlp:LogoutRequest&gt; and</span><br><span class="hljs-comment"># &lt;samlp:LogoutResponse&gt; elements received by this SP to be signed.</span><br><span class="hljs-attribute">onelogin</span>.saml2.security.want_messages_signed = false<br><br><span class="hljs-comment"># Indicates a requirement for the &lt;saml:Assertion&gt; elements received by this SP to be signed.</span><br><span class="hljs-attribute">onelogin</span>.saml2.security.want_assertions_signed = false<br><br><span class="hljs-comment"># Indicates a requirement for the Metadata of this SP to be signed.</span><br><span class="hljs-comment"># Right now supported null (in order to not sign) or true (sign using SP private key) </span><br><span class="hljs-attribute">onelogin</span>.saml2.security.sign_metadata =<br><br><span class="hljs-comment"># Indicates a requirement for the Assertions received by this SP to be encrypted</span><br><span class="hljs-attribute">onelogin</span>.saml2.security.want_assertions_encrypted = false<br><br><span class="hljs-comment"># Indicates a requirement for the NameID received by this SP to be encrypted</span><br><span class="hljs-attribute">onelogin</span>.saml2.security.want_nameid_encrypted = false<br><br><span class="hljs-comment"># Authentication context.</span><br><span class="hljs-comment"># Set Empty and no AuthContext will be sent in the AuthNRequest</span><br><span class="hljs-comment"># You can set multiple values (comma separated them)</span><br><span class="hljs-attribute">onelogin</span>.saml2.security.requested_authncontext = urn:oasis:names:tc:SAML:<span class="hljs-number">2</span>.<span class="hljs-number">0</span>:ac:classes:Password<br><br><span class="hljs-comment"># Allows the authn comparison parameter to be set, defaults to &#x27;exact&#x27;</span><br><span class="hljs-attribute">onelogin</span>.saml2.security.onelogin.saml2.security.requested_authncontextcomparison = exact<br><br><br><span class="hljs-comment"># Indicates if the SP will validate all received xmls.</span><br><span class="hljs-comment"># (In order to validate the xml, &#x27;strict&#x27; and &#x27;wantXMLValidation&#x27; must be true).</span><br><span class="hljs-attribute">onelogin</span>.saml2.security.want_xml_validation = true<br><br><span class="hljs-comment"># Algorithm that the toolkit will use on signing process. Options:</span><br><span class="hljs-comment">#  &#x27;http://www.w3.org/2000/09/xmldsig#rsa-sha1&#x27;</span><br><span class="hljs-comment">#  &#x27;http://www.w3.org/2000/09/xmldsig#dsa-sha1&#x27;</span><br><span class="hljs-comment">#  &#x27;http://www.w3.org/2001/04/xmldsig-more#rsa-sha256&#x27;</span><br><span class="hljs-comment">#  &#x27;http://www.w3.org/2001/04/xmldsig-more#rsa-sha384&#x27;</span><br><span class="hljs-comment">#  &#x27;http://www.w3.org/2001/04/xmldsig-more#rsa-sha512&#x27;</span><br><span class="hljs-attribute">onelogin</span>.saml2.security.signature_algorithm = http://www.w3.org/<span class="hljs-number">2000</span>/<span class="hljs-number">09</span>/xmldsig#rsa-sha1<br><br><span class="hljs-comment"># Organization</span><br><span class="hljs-attribute">onelogin</span>.saml2.organization.name = SP Java <br><span class="hljs-attribute">onelogin</span>.saml2.organization.displayname = SP Java OA<br><span class="hljs-attribute">onelogin</span>.saml2.organization.url = http://<br><span class="hljs-attribute">onelogin</span>.saml2.organization.lang = <br><br><span class="hljs-comment"># Contacts</span><br><span class="hljs-attribute">onelogin</span>.saml2.contacts.technical.given_name = Technical Guy<br><span class="hljs-attribute">onelogin</span>.saml2.contacts.technical.email_address = technical@example.com<br><span class="hljs-attribute">onelogin</span>.saml2.contacts.support.given_name = Support Guy<br><span class="hljs-attribute">onelogin</span>.saml2.contacts.support.email_address = support@@example.com<br><br></code></pre></td></tr></table></figure><h3 id="6-SP端-代码解读"><a href="#6-SP端-代码解读" class="headerlink" title="6.SP端 代码解读"></a>6.SP端 代码解读</h3><blockquote><p>keyStores</p></blockquote><p>Auth构造函数支持从KeyStore读取SP公共证书&#x2F;私钥的功能。<br>必须为KeyStoreSettings对象提供KeyStore，别名和KeyEntry密码。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">import</span> java.io.FileInputStream;<br><span class="hljs-keyword">import</span> java.security.KeyStore;<br><span class="hljs-keyword">import</span> com.onelogin.saml2.Auth<br><span class="hljs-keyword">import</span> com.onelogin.saml2.model.KeyStoreSettings<br><br><span class="hljs-type">String</span> <span class="hljs-variable">keyStoreFile</span> <span class="hljs-operator">=</span> <span class="hljs-string">&quot;oneloginTestKeystore.jks&quot;</span>;<br><span class="hljs-type">String</span> <span class="hljs-variable">alias</span> <span class="hljs-operator">=</span> <span class="hljs-string">&quot;keywithpassword&quot;</span>;<br><span class="hljs-type">String</span> <span class="hljs-variable">storePass</span> <span class="hljs-operator">=</span> <span class="hljs-string">&quot;changeit&quot;</span>;<br><span class="hljs-type">String</span> <span class="hljs-variable">keyPassword</span> <span class="hljs-operator">=</span> <span class="hljs-string">&quot;keypassword&quot;</span>;<br><br><span class="hljs-type">KeyStore</span> <span class="hljs-variable">ks</span> <span class="hljs-operator">=</span> KeyStore.getInstance(<span class="hljs-string">&quot;JKS&quot;</span>);<br>ks.load(<span class="hljs-keyword">new</span> <span class="hljs-title class_">FileInputStream</span>(keyStoreFile), storePass.toCharArray());<br><br><span class="hljs-type">KeyStoreSettings</span> <span class="hljs-variable">keyStoreSettings</span> <span class="hljs-operator">=</span>  <span class="hljs-keyword">new</span> <span class="hljs-title class_">keyStoreSettings</span>(ks, alias, keyPassword);<br><span class="hljs-type">Auth</span> <span class="hljs-variable">auth</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">Auth</span>(KeyStoreSettings keyStoreSetting);<br></code></pre></td></tr></table></figure><blockquote><p>动态配置</p></blockquote><p>可以以编程方式建立设置。您可以从其他来源（例如文件，数据库或生成的值）加载值。SettingsBuilder类公开了fromValues（Map &lt;String，Object&gt; samlData）方法，该方法可让您动态构建设置。密钥字符串与属性文件中的相同</p><figure class="highlight haxe"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><code class="hljs haxe">Map&lt;<span class="hljs-keyword">String</span>, Object&gt; samlData = <span class="hljs-keyword">new</span> <span class="hljs-type">HashMap</span>&lt;&gt;();<br>samlData.put(<span class="hljs-string">&quot;onelogin.saml2.sp.entityid&quot;</span>, <span class="hljs-string">&quot;http://localhost:8080/java-saml-tookit-jspsample/metadata.jsp&quot;</span>);<br>samlData.put(<span class="hljs-string">&quot;onelogin.saml2.sp.assertion_consumer_service.url&quot;</span>, <span class="hljs-keyword">new</span> <span class="hljs-type">URL</span>(<span class="hljs-string">&quot;http://localhost:8080/java-saml-tookit-jspsample/acs.jsp&quot;</span>));<br>samlData.put(<span class="hljs-string">&quot;onelogin.saml2.security.want_xml_validation&quot;</span>,<span class="hljs-literal">true</span>);<br>samlData.put(<span class="hljs-string">&quot;onelogin.saml2.sp.x509cert&quot;</span>, myX509CertInstance);<br><br>SettingsBuilder builder = <span class="hljs-keyword">new</span> <span class="hljs-type">SettingsBuilder</span>();<br>Saml2Settings settings = builder.fromValues(samlData).build();<br><span class="hljs-comment">//实例化您编写的Auth类</span><br>Auth auth = <span class="hljs-keyword">new</span> <span class="hljs-type">Auth</span>(settings, request, response);<br><br></code></pre></td></tr></table></figure><blockquote><p>关于 HttpRequest</p></blockquote><p>java-saml依赖于javax.servlet：servlet-api，而Auth和ServletUtils类使用HttpServletRequest和HttpServletResponse对象。如果要使用与javax.servlet.http不同的任何内容，则需要基于HTTP请求&#x2F;响应的新表示重新实现Auth和ServletUtils。</p><h5 id="1-发起单点登录"><a href="#1-发起单点登录" class="headerlink" title="1.发起单点登录"></a>1.发起单点登录</h5><p>用于向IDP发送AuthNRequest</p><figure class="highlight vbscript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs vbscript">Auth auth = <span class="hljs-keyword">new</span> Auth(<span class="hljs-built_in">request</span>, <span class="hljs-built_in">response</span>);<br>auth.login();<br></code></pre></td></tr></table></figure><p>AuthNRequest将根据安全设置“ onelogin.saml2.security.authnrequest_signed”以签名或未签名的形式发送。然后，IdP将把SAML响应返回给用户的客户端。然后，使用此信息将客户端转发到SP的属性消费者服务。我们可以为登录函数设置一个“ returnTo” URL参数，并将其转换为“ RelayState”参数：</p><figure class="highlight abnf"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs abnf">//指定登录成功后跳转的地址<br>String targetUrl <span class="hljs-operator">=</span> <span class="hljs-string">&quot;https://github.com/onelogin/java-saml&quot;</span><span class="hljs-comment">;</span><br>auth.login(targetUrl)<span class="hljs-comment">;</span><br></code></pre></td></tr></table></figure><blockquote><p>SP 端点</p></blockquote><p>三个重要的端点,sp元数据(SP_metadata),ACS(断言解析服务),SLS(单点登出响应解析服务) </p><blockquote><p>SP Metadata</p></blockquote><p>这段代码将根据设置文件中提供的信息提供SP的XML元数据文件。</p><figure class="highlight maxima"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><code class="hljs maxima">Auth auth = <span class="hljs-built_in">new</span> Auth();<br>Saml2Settings settings = auth.getSettings();<br>String metadata = settings.getSPMetadata();<br>List&lt;String&gt; <span class="hljs-built_in">errors</span> = Saml2Settings.validateMetadata(metadata);<br><span class="hljs-keyword">if</span> (<span class="hljs-built_in">errors</span>.isEmpty()) &#123;<br>   out.println(metadata);<br>&#125; <span class="hljs-keyword">else</span> &#123;<br>   response.setContentType(<span class="hljs-string">&quot;text/html; charset=UTF-8&quot;</span>);<br>   <span class="hljs-keyword">for</span> (String <span class="hljs-built_in">error</span> : <span class="hljs-built_in">errors</span>) &#123;<br>       out.println(<span class="hljs-string">&quot;&lt;p&gt;&quot;</span>+<span class="hljs-built_in">error</span>+<span class="hljs-string">&quot;&lt;/p&gt;&quot;</span>);<br>   &#125;<br>&#125;<br></code></pre></td></tr></table></figure><blockquote><p>Attribute Consumer Service(ACS)</p></blockquote><p>此代码处理IdP通过用户客户端转发到SP的SAML响应。</p><figure class="highlight pgsql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br></pre></td><td class="code"><pre><code class="hljs pgsql">Auth auth = <span class="hljs-built_in">new</span> Auth(request, response);<br>//具体的 响应断言解析<br>auth.processResponse();<br><span class="hljs-keyword">if</span> (!auth.isAuthenticated()) &#123;<br>   <span class="hljs-keyword">out</span>.println(&quot;Not authenticated&quot;);<br>&#125;<br><br>List&lt;String&gt; errors = auth.getErrors();<br><span class="hljs-keyword">if</span> (!errors.isEmpty()) &#123;<br>    <span class="hljs-keyword">out</span>.println(StringUtils.<span class="hljs-keyword">join</span>(errors, &quot;, &quot;));<br>    <span class="hljs-keyword">if</span> (auth.isDebugActive()) &#123;<br>        String errorReason = auth.getLastErrorReason();<br>        <span class="hljs-keyword">if</span> (errorReason != <span class="hljs-keyword">null</span> &amp;&amp; !errorReason.isEmpty()) &#123;<br>            <span class="hljs-keyword">out</span>.println(auth.getLastErrorReason());<br>        &#125;<br>    &#125;<br>&#125; <span class="hljs-keyword">else</span> &#123;<br>    Map&lt;String, List&lt;String&gt;&gt; attributes = auth.getAttributes();<br>    String nameId = auth.getNameId();<br>    String nameIdFormat = auth.getNameIdFormat();<br>    String sessionIndex = auth.getSessionIndex();<br>    String nameidNameQualifier = auth.getNameIdNameQualifier();<br>    String nameidSPNameQualifier = auth.getNameIdSPNameQualifier();<br><br>    //关键参数<br>    <span class="hljs-keyword">session</span>.setAttribute(&quot;attributes&quot;, attributes);<br>    <span class="hljs-keyword">session</span>.setAttribute(&quot;nameId&quot;, nameId);<br>    <span class="hljs-keyword">session</span>.setAttribute(&quot;nameIdFormat&quot;, nameIdFormat);<br>    <span class="hljs-keyword">session</span>.setAttribute(&quot;sessionIndex&quot;, sessionIndex);<br>    <br>    <span class="hljs-keyword">session</span>.setAttribute(&quot;nameidNameQualifier&quot;, nameidNameQualifier);<br>    <span class="hljs-keyword">session</span>.setAttribute(&quot;nameidSPNameQualifier&quot;, nameidSPNameQualifier);<br><br>    String relayState = request.getParameter(&quot;RelayState&quot;);<br><br>    <span class="hljs-keyword">if</span> (relayState != <span class="hljs-keyword">null</span> &amp;&amp; relayState != ServletUtils.getSelfRoutedURLNoQuery(request)) &#123;<br>        response.sendRedirect(request.getParameter(&quot;RelayState&quot;));<br>    &#125; <span class="hljs-keyword">else</span> &#123;<br>        <span class="hljs-keyword">if</span> (attributes.isEmpty()) &#123;<br>            <span class="hljs-keyword">out</span>.println(&quot;You don&#x27;t have any attributes&quot;);<br>        &#125;<br>       <span class="hljs-keyword">else</span> &#123;<br>            Collection&lt;String&gt; keys = attributes.keySet();<br>            <span class="hljs-keyword">for</span>(String <span class="hljs-type">name</span> :keys)&#123;<br>                <span class="hljs-keyword">out</span>.println(<span class="hljs-type">name</span>);<br>                List&lt;String&gt; <span class="hljs-keyword">values</span> = attributes.<span class="hljs-keyword">get</span>(<span class="hljs-type">name</span>);<br>                <span class="hljs-keyword">for</span>(String <span class="hljs-keyword">value</span> :<span class="hljs-keyword">values</span>) &#123;<br>                    <span class="hljs-keyword">out</span>.println(&quot; - &quot; + <span class="hljs-keyword">value</span>);<br>                &#125;<br>            &#125;<br>        &#125;<br>    &#125;<br>&#125;<br></code></pre></td></tr></table></figure><p>在尝试获取属性之前，请检查用户是否已通过身份验证。如果用户未通过身份验证，则将返回一个空的Map。例如，如果我们在auth.processResponse之前调用getAttributes，则getAttributes（）将返回一个空Map。</p><blockquote><p>Single Logout Service (SLS)</p></blockquote><p>以下代码处理注销请求和注销响应。</p><figure class="highlight maxima"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><code class="hljs maxima">Auth auth = <span class="hljs-built_in">new</span> Auth(request, response);<br>auth.processSLO();<br>List&lt;String&gt; <span class="hljs-built_in">errors</span> = auth.getErrors();<br><span class="hljs-keyword">if</span> (<span class="hljs-built_in">errors</span>.isEmpty()) &#123;<br>   out.println(<span class="hljs-string">&quot;Sucessfully logged out&quot;</span>);<br>&#125; <span class="hljs-keyword">else</span> &#123;<br>   <span class="hljs-keyword">for</span>(String <span class="hljs-built_in">error</span> : <span class="hljs-built_in">errors</span>) &#123;<br>      out.println(<span class="hljs-built_in">error</span>);<br>   &#125;<br>&#125;<br></code></pre></td></tr></table></figure><p>如果SLS端点收到注销响应，则该响应将得到验证，并且HttpRequest的会话可能会关闭。如果SLS端点接收到注销请求，则该请求将得到验证，会话将关闭，并且注销响应将发送到IdP的SLS端点。如果我们不希望该processSLO破坏会话，则将keepLo​​calSession参数作为true传递给processSLO方法。</p><h5 id="2-发起单点登出"><a href="#2-发起单点登出" class="headerlink" title="2.发起单点登出"></a>2.发起单点登出</h5><p>用于发送Logout Request到IdP</p><blockquote><p>注意 此方式 是通过SP端发起的单点登出</p></blockquote><figure class="highlight vbscript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><code class="hljs vbscript">Auth auth = <span class="hljs-keyword">new</span> Auth(<span class="hljs-built_in">request</span>, <span class="hljs-built_in">response</span>);<br><br>String nameId = <span class="hljs-literal">null</span>;<br><span class="hljs-keyword">if</span> (session.getAttribute(<span class="hljs-string">&quot;nameId&quot;</span>) != <span class="hljs-literal">null</span>) &#123;<br>    nameId = session.getAttribute(<span class="hljs-string">&quot;nameId&quot;</span>).<span class="hljs-keyword">to</span><span class="hljs-built_in">String</span>();<br>&#125;<br>String nameIdFormat = <span class="hljs-literal">null</span>;<br><span class="hljs-keyword">if</span> (session.getAttribute(<span class="hljs-string">&quot;nameIdFormat&quot;</span>) != <span class="hljs-literal">null</span>) &#123;<br>    nameIdFormat = session.getAttribute(<span class="hljs-string">&quot;nameIdFormat&quot;</span>).<span class="hljs-keyword">to</span><span class="hljs-built_in">String</span>();<br>&#125;<br>String nameidNameQualifier = <span class="hljs-literal">null</span>;<br><span class="hljs-keyword">if</span> (session.getAttribute(<span class="hljs-string">&quot;nameidNameQualifier&quot;</span>) != <span class="hljs-literal">null</span>) &#123;<br>    nameIdFormat = session.getAttribute(<span class="hljs-string">&quot;nameidNameQualifier&quot;</span>).<span class="hljs-keyword">to</span><span class="hljs-built_in">String</span>();<br>&#125;<br>String nameidSPNameQualifier = <span class="hljs-literal">null</span>;<br><span class="hljs-keyword">if</span> (session.getAttribute(<span class="hljs-string">&quot;nameidSPNameQualifier&quot;</span>) != <span class="hljs-literal">null</span>) &#123;<br>    nameidSPNameQualifier = session.getAttribute(<span class="hljs-string">&quot;nameidSPNameQualifier&quot;</span>).<span class="hljs-keyword">to</span><span class="hljs-built_in">String</span>();<br>&#125;<br>String sessionIndex = <span class="hljs-literal">null</span>;<br><span class="hljs-keyword">if</span> (session.getAttribute(<span class="hljs-string">&quot;sessionIndex&quot;</span>) != <span class="hljs-literal">null</span>) &#123;<br>    sessionIndex = session.getAttribute(<span class="hljs-string">&quot;sessionIndex&quot;</span>).<span class="hljs-keyword">to</span><span class="hljs-built_in">String</span>();<br>&#125;<br>auth.logout(<span class="hljs-literal">null</span>, nameId, sessionIndex, nameIdFormat);<br></code></pre></td></tr></table></figure><p>将根据安全设置“ onelogin.saml2.security.logoutrequest_signed”以签名或未签名的形式发送注销请求。IdP将通过用户客户端将注销响应返回到SP的单一注销服务。最终在sp的sls中获取登出结果</p><h3 id="7-工作流"><a href="#7-工作流" class="headerlink" title="7 工作流"></a>7 工作流</h3><ul><li>运行我们示例项目。首次访问主视图http&#x2F;&#x2F;localhost：8080&#x2F;index.jsp时，您可以选择登录并返回同一视图，或登录并重定向到属性视图（属性）。</li><li>当您单击链接时，：</li><li>1。在第一个链接中，我们被重定向到&#x2F;dologin.jsp视图。AuthNRequest发送到IdP，我们在IdP上进行身份验证，然后将响应发送到SP，特别是Assertion Consumer Service视图：&#x2F;acs.jsp。在此验证SAMLResponse，提取NameID和用户属性并将其存储在会话中。注意，将RelayState参数设置为启动该过程的URL，即dologin.jsp url，但是我们没有将用户重定向到该视图，而是在&#x2F;acs.jsp视图上显示了用户数据。</li><li>2。在第二个链接中，我们使用“ attrs” GET参数重定向到&#x2F;dologin.jsp视图。AuthNRequest通过&#x2F;attrs.jsp视图作为RelayState参数发送到IdP，我们在IdP上进行身份验证，然后将响应发送到SP，特别是到断言消费者服务视图：&#x2F;acs.jsp。在那里验证了SAMLResponse，提取了NameID和用户属性并将其存储在会话中，然后将我们重定向到RelayState视图，attrs.jsp视图，在该视图中从会话中读取用户数据并进行提示。单一注销功能可以通过两种方法进行测试。</li><li>3。SLO由SP发起。单击SP上的“注销”链接，之后，我们将重定向到&#x2F;dologout.jsp视图，在该视图中，将注销请求发送到IdP，IdP上的会话关闭，并向SP答复注销响应（已发送）到“单一注销服务”端点）。SP的SLS端点&#x2F;sls.jsp处理注销响应，如果有效，则关闭本地应用程序的用户会话。请注意，SLO工作流在SP处开始和结束。</li><li>4。SLO由IdP发起。在这种情况下，该操作在IdP端发生，注销过程在IdP上启动，它向SP（SLS端点，&#x2F; sls.jsp）发送注销请求。SP的SLS端点处理注销请求，如果有效，则关闭本地应用程序上的用户会话，然后将注销响应发送到IdP（发送到IdP的SLS端点）。IdP接收注销响应，对其进行处理并在IdP上关闭会话。请注意，SLO工作流程在IdP处开始和结束。</li></ul><h3 id="8-相关文档和工具地址"><a href="#8-相关文档和工具地址" class="headerlink" title="8.相关文档和工具地址"></a>8.相关文档和工具地址</h3><blockquote><p><a href="https://developers.onelogin.com/saml/java">IDP 连接器搭建文档</a></p></blockquote><blockquote><p><a href="https://github.com/onelogin/java-saml">java-saml工具库代码地址</a></p></blockquote><blockquote><p><a href="https://www.samltool.com/base64.php">saml助手网站</a></p></blockquote><blockquote><p><a href="https://chrome.google.com/webstore/detail/saml-tracer/mpdajninpobndbfcldcmbpnnbhibjmch?utm_source=chrome-ntp-icon">saml调试谷歌插件 SAML-tracer</a></p></blockquote>]]>
      </content:encoded>
    </item>
    <item>
      <title>将hexo部署至阿里云服务器</title>
      <link>https://www.xuwx.top/2020/08/30/%E5%B0%86hexo%E9%83%A8%E7%BD%B2%E8%87%B3%E9%98%BF%E9%87%8C%E4%BA%91%E6%9C%8D%E5%8A%A1%E5%99%A8/</link>
      <description>
        <![CDATA[<h4 id="将hexo部署至阿里云服务器"><a href="#将hexo部署至阿里云服务器" class="headerlink" title="将hexo部署至阿里云服务器"></a>将hexo部署至阿里云服务器</h4><h6 id="使用Hexo搭建博客目前有以下三种]]>
      </description>
      <author>Shine_ssr</author>
      <category domain="https://www.xuwx.top/categories/%E5%8D%9A%E5%AE%A2/">博客</category>
      <category domain="https://www.xuwx.top/tags/Hexo/">Hexo</category>
      <category domain="https://www.xuwx.top/tags/ECS/">ECS</category>
      <category domain="https://www.xuwx.top/tags/Git/">Git</category>
      <category domain="https://www.xuwx.top/tags/Blog/">Blog</category>
      <pubDate>Sun, 30 Aug 2020 09:31:06 GMT</pubDate>
      <content:encoded>
        <![CDATA[<h4 id="将hexo部署至阿里云服务器"><a href="#将hexo部署至阿里云服务器" class="headerlink" title="将hexo部署至阿里云服务器"></a>将hexo部署至阿里云服务器</h4><h6 id="使用Hexo搭建博客目前有以下三种方案"><a href="#使用Hexo搭建博客目前有以下三种方案" class="headerlink" title="使用Hexo搭建博客目前有以下三种方案:"></a>使用Hexo搭建博客目前有以下三种方案:</h6><ul><li>Hexo 框架 + github</li><li>Hexo 框架 + github + 自定义域名</li><li>Hexo 框架 + 云服务器 + 自定义域名</li></ul><p>我主要讲述 第三种方式,使用自己已经有的服务器,以及可用的域名来搭建自己的博客.</p><h6 id="「Hexo-阿里云-域名」搭建步骤"><a href="#「Hexo-阿里云-域名」搭建步骤" class="headerlink" title="「Hexo + 阿里云 + 域名」搭建步骤"></a>「Hexo + 阿里云 + 域名」搭建步骤</h6><h5 id="整体框架结构图"><a href="#整体框架结构图" class="headerlink" title="整体框架结构图"></a>整体框架结构图</h5><p><img src="https://qiniu.xuwx.top/blog/article/Hexo%E5%8D%9A%E5%AE%A2%E8%87%AA%E5%8A%A8%E5%8F%91%E5%B8%83%E6%9E%B6%E6%9E%84%E5%9B%BE.webp" alt="整体框架结构图"></p><h5 id="整体流程"><a href="#整体流程" class="headerlink" title="整体流程"></a>整体流程</h5><ol><li>本地PC 安装node.js 以及Hexo 的初始化</li><li>服务器环境搭建 安装 git node nginx配置 创建git用户</li><li>使用Git 自动化部署发布博客</li></ol><h5 id="1-本地Hexo环境"><a href="#1-本地Hexo环境" class="headerlink" title="1.本地Hexo环境"></a>1.本地Hexo环境</h5><p>1.1 安装git  </p><p>1.2 安装 node </p><p>1.3 安装 npm 强烈建议 使用 cnpm (只要网络问题很多时候 npm 命令会报错 导致 步骤失败 )</p><p>1.4 node -v npm -v 检查 版本 验证是否安装成功</p><p>1.5 安装Hexo</p><figure class="highlight avrasm"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs avrasm">npm install -g hexo-<span class="hljs-keyword">cli</span><br></code></pre></td></tr></table></figure><p>在本地自己喜欢的地方新建一个博客文件夹 blog 比将其初始化</p><figure class="highlight mipsasm"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs mipsasm">hexo init <span class="hljs-keyword">blog</span><br><span class="hljs-keyword"></span>cd <span class="hljs-keyword">blog</span><br><span class="hljs-keyword"></span>npm <span class="hljs-keyword">install</span><br></code></pre></td></tr></table></figure><p>初始化之后在项目根目录 里面进行Hexo 插件 hexo-deployer-git(自动化部署的时候使用),hexo-server 的安装 </p><figure class="highlight cmake"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs cmake">npm <span class="hljs-keyword">install</span> hexo-deployer-git --save<br>npm <span class="hljs-keyword">install</span> hexo-server<br></code></pre></td></tr></table></figure><p>配置GIT全局变量 </p><p>项目根目录中打开 GitBash here</p><figure class="highlight stylus"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs stylus">git config <span class="hljs-attr">--global</span> user<span class="hljs-selector-class">.email</span> <span class="hljs-string">&quot;youremail@mail.com&quot;</span><br>git config <span class="hljs-attr">--global</span> user<span class="hljs-selector-class">.name</span> <span class="hljs-string">&quot;yourname&quot;</span><br></code></pre></td></tr></table></figure><h5 id="2-服务器端"><a href="#2-服务器端" class="headerlink" title="2.服务器端"></a>2.服务器端</h5><p>1.会用到 80端口 所以需要配置安全组 开放80端口(阿里云默认不开启)</p><p>2.最好使用 https 在 配置nginx时可以一起配置</p><p>3.在配置 密钥时 .ssh文件夹(隐藏文件夹) 在ftp中 默认不可见 需要调出来 ls -a 查看</p><p>4.在 服务器端操作时 需要新建 git 用户 需要注意 当前用户 和可操作 权限问题 </p><h4 id="1-安装nginx"><a href="#1-安装nginx" class="headerlink" title="1.安装nginx"></a>1.安装nginx</h4><p>安装有多种方式 我主要说下 配置<br>首先 服务器上需要新建 一个hexo 部署目录 我使用的是 &#x2F;home&#x2F;git&#x2F;blog</p><figure class="highlight arduino"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs arduino">mkdir -p /home/git/blog<br></code></pre></td></tr></table></figure><p>进入nginx配置文件</p><figure class="highlight stata"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs stata"><span class="hljs-keyword">cd</span> /usr/<span class="hljs-keyword">local</span>/nginx/<span class="hljs-keyword">conf</span><br>vim nginx.<span class="hljs-keyword">conf</span><br></code></pre></td></tr></table></figure><p>将顶端的user(用户)改为 root<br>以下为我的配置</p><figure class="highlight nginx"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br></pre></td><td class="code"><pre><code class="hljs nginx"><br><span class="hljs-attribute">user</span>  root;<br><span class="hljs-attribute">worker_processes</span>  <span class="hljs-number">1</span>;<br><br><span class="hljs-comment">#error_log  logs/error.log;</span><br><span class="hljs-comment">#error_log  logs/error.log  notice;</span><br><span class="hljs-comment">#error_log  logs/error.log  info;</span><br><br><span class="hljs-comment">#pid        logs/nginx.pid;</span><br><br><br><span class="hljs-section">events</span> &#123;<br>    <span class="hljs-attribute">worker_connections</span>  <span class="hljs-number">1024</span>;<br>&#125;<br><br><br><span class="hljs-section">http</span> &#123;<br>    <span class="hljs-attribute">include</span>       mime.types;<br>    <span class="hljs-attribute">default_type</span>  application/octet-stream;<br><br>    <span class="hljs-comment">#log_format  main  &#x27;$remote_addr - $remote_user [$time_local] &quot;$request&quot; &#x27;</span><br>    <span class="hljs-comment">#                  &#x27;$status $body_bytes_sent &quot;$http_referer&quot; &#x27;</span><br>    <span class="hljs-comment">#                  &#x27;&quot;$http_user_agent&quot; &quot;$http_x_forwarded_for&quot;&#x27;;</span><br><br>    <span class="hljs-comment">#access_log  logs/access.log  main;</span><br><br>    <span class="hljs-attribute">sendfile</span>        <span class="hljs-literal">on</span>;<br>    <span class="hljs-comment">#tcp_nopush     on;</span><br><br>    <span class="hljs-comment">#keepalive_timeout  0;</span><br>    <span class="hljs-attribute">keepalive_timeout</span>  <span class="hljs-number">65</span>;<br><br>    <span class="hljs-comment">#gzip  on;</span><br><br>    <span class="hljs-section">server</span> &#123;<br>        <span class="hljs-attribute">listen</span>       <span class="hljs-number">80</span>;<br>        <span class="hljs-attribute">server_name</span> www.xuwx.top;<br><span class="hljs-attribute">rewrite</span><span class="hljs-regexp"> ^(.*)$</span> https://<span class="hljs-variable">$host</span><span class="hljs-variable">$1</span> <span class="hljs-literal">permanent</span>;<br>    &#125;<br><br><span class="hljs-comment"># HTTPS server</span><br>    <span class="hljs-comment">#</span><br>    <span class="hljs-section">server</span> &#123;<br>        <span class="hljs-attribute">listen</span>       <span class="hljs-number">443</span> ssl;<br>        <span class="hljs-attribute">listen</span>       [::]:<span class="hljs-number">443</span> ssl;<br>        <span class="hljs-attribute">server_name</span>  www.xuwx.top;<br><br><span class="hljs-comment">#正式配置</span><br>        <span class="hljs-attribute">ssl_certificate</span>      cert/www.xuwx.top.pem;<br>        <span class="hljs-attribute">ssl_certificate_key</span>  cert/www.xuwx.top.key;<br><br>        <span class="hljs-attribute">ssl_session_cache</span>    shared:SSL:<span class="hljs-number">1m</span>;<br>        <span class="hljs-attribute">ssl_session_timeout</span> <span class="hljs-number">5m</span>;<br><span class="hljs-attribute">ssl_ciphers</span> ECDHE-RSA-AES128-GCM-SHA256:ECDHE:ECDH:AES:HIGH:!NULL:!aNULL:!MD5:!ADH:!RC4;<br><span class="hljs-attribute">ssl_protocols</span> TLSv1 TLSv1.<span class="hljs-number">1</span> TLSv1.<span class="hljs-number">2</span>;<br><span class="hljs-attribute">ssl_prefer_server_ciphers</span> <span class="hljs-literal">on</span>;<br><br>        <span class="hljs-section">location</span> / &#123;<br>    <span class="hljs-attribute">root</span> /home/git/blog;  <br>    <span class="hljs-attribute">proxy_read_timeout</span> <span class="hljs-number">300</span>;<br> <span class="hljs-attribute">proxy_connect_timeout</span> <span class="hljs-number">300</span>;<br> <span class="hljs-attribute">proxy_redirect</span>     <span class="hljs-literal">off</span>;<br> <span class="hljs-attribute">proxy_set_header</span>   X-Forwarded-Proto <span class="hljs-variable">$scheme</span>;<br> <span class="hljs-attribute">proxy_set_header</span>   Host              <span class="hljs-variable">$http_host</span>;<br> <span class="hljs-attribute">proxy_set_header</span>   X-Real-IP         <span class="hljs-variable">$remote_addr</span>;  <br>         &#125;<br> <br><span class="hljs-attribute">error_page</span> <span class="hljs-number">404</span> /<span class="hljs-number">404</span>.html;<br><span class="hljs-section">location</span> = /40x.html &#123;<br>&#125;<br><br><span class="hljs-attribute">error_page</span> <span class="hljs-number">500</span> <span class="hljs-number">502</span> <span class="hljs-number">503</span> <span class="hljs-number">504</span> /50x.html;<br><span class="hljs-section">location</span> = /50x.html &#123;<br>&#125;<br>    &#125;<br><br>&#125;<br><br></code></pre></td></tr></table></figure><p>主要是两个地方 location 下 配置 root 为直接的 &#x2F;home&#x2F;git&#x2F;blog 博客要部署的位置 以及 80端口的配置</p><p>注意 修改完毕 记得重启 nginx</p><h4 id="2-node的安装"><a href="#2-node的安装" class="headerlink" title="2. node的安装"></a>2. node的安装</h4><p>这里不再复述 当然npm 最好使用 国内仓库</p><h4 id="3-服务器端git的安装以及配置"><a href="#3-服务器端git的安装以及配置" class="headerlink" title="3. 服务器端git的安装以及配置"></a>3. 服务器端git的安装以及配置</h4><p>1.git 安装 不再 复述</p><ol start="2"><li>新建git 用户 (目的用于后面blog代码的自动化部署)</li></ol><figure class="highlight ebnf"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs ebnf"><span class="hljs-attribute">adduser git</span><br></code></pre></td></tr></table></figure><p>3.修改git用户的权限 740</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash"><span class="hljs-built_in">chmod</span> 740 /etc/sudoers<br></code></pre></td></tr></table></figure><p>4.配置文件中增加 Git 用户</p><figure class="highlight awk"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs awk">vim <span class="hljs-regexp">/etc/</span>sudoers<br></code></pre></td></tr></table></figure><p>5.在 root  ALL&#x3D;(ALL)   ALL<br>下 添加 git ALL&#x3D;(ALL)  ALL</p><p> 6.Git 用户的权限改回去。</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash"><span class="hljs-built_in">chmod</span> 400 /etc/sudoers<br></code></pre></td></tr></table></figure><p>7.设置 Git 用户密码</p><figure class="highlight ebnf"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs ebnf"><span class="hljs-attribute">sudo passwd git</span><br></code></pre></td></tr></table></figure><p>完成了 Git 用户的创建后，接下来我们向 Git 用户添加公钥，就像配置 Github 那样</p><ol start="8"><li>Git 用户配置 ssh 公钥<br>将 本地pc user\用户.ssh 文件夹下的 id_rsa.pub(没有的话网上参考如何创建该文件) 文件 拷贝至 服务器 &#x2F;home&#x2F;git&#x2F;.ssh文件夹下</li></ol><p>8.1在服务器端 切换到 git 用户，在根目录下创建 .ssh文件夹。</p><figure class="highlight stata"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs stata"><span class="hljs-keyword">su</span> git<br><span class="hljs-keyword">cd</span> ~<br><span class="hljs-keyword">mkdir</span> .ssh<br></code></pre></td></tr></table></figure><p>8.2 用GIT用户身份 .ssh 文件夹内新建 authorized_keys 文件，并将公钥内容拷贝到该文件中。</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><code class="hljs bash"><span class="hljs-built_in">cd</span> ~/.ssh<br><span class="hljs-built_in">cp</span> id_rsa.pub authorized_keys<br><span class="hljs-built_in">cat</span> id_rsa.pub &gt;&gt; ~/.ssh/authorized_keys<br><br></code></pre></td></tr></table></figure><p>8.3 修改文件权限</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs bash"><span class="hljs-built_in">chmod</span> 600 ~/.ssh/authorized_keys<br><span class="hljs-built_in">chmod</span> 700 ~/.ssh<br></code></pre></td></tr></table></figure><p>8.4 确保设置了正确的SELinux上下文</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">restorecon -Rv ~/.ssh<br></code></pre></td></tr></table></figure><p>8.5 测试一下是否设置成功。在本地任意位置右键打开 GitBash Here，输入公网 IP</p><figure class="highlight nginx"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs nginx"><span class="hljs-attribute">ssh</span> -v git<span class="hljs-variable">@xxx</span>.xxx.xxx.xxx（你的公网 IP）<br></code></pre></td></tr></table></figure><p>成功就会和 xshell 连进去一样了.</p><p>9.设置Git仓库</p><p>9.1 继续使用Git 用户 新建 post-receive 钩子文件</p><figure class="highlight jboss-cli"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs jboss-cli"><span class="hljs-keyword">cd</span> ~<br>git init <span class="hljs-params">--bare</span> hexo.git<br>vi ~<span class="hljs-string">/hexo.git/hooks/post-receive</span><br></code></pre></td></tr></table></figure><p>新增如下命令 注意跟换为自己的博客部署目录</p><figure class="highlight routeros"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs routeros">git <span class="hljs-attribute">--work-tree</span>=/home/git/blog <span class="hljs-attribute">--git-dir</span>=/home/git/hexo.git checkout -f<br></code></pre></td></tr></table></figure><p>9.2 授予钩子文件可执行权限</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs bash"><span class="hljs-built_in">chmod</span> +x ~/hexo.git/hooks/post-receive<br><span class="hljs-built_in">cd</span> ~<br>sudo <span class="hljs-built_in">chmod</span> -R 777 /home/git/blog<br></code></pre></td></tr></table></figure><p>10.完成上述配置之后就可以 重启服务器了.</p><h5 id="3-自动化部署"><a href="#3-自动化部署" class="headerlink" title="3.自动化部署"></a>3.自动化部署</h5><p>1.现在 回到本地pc blog 文件根目录 找到 _config.yml文件 打开 并修改</p><p>***为自己的服务器公网ip</p><figure class="highlight dts"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><code class="hljs dts"><span class="hljs-meta"># Deployment</span><br><span class="hljs-meta">## Docs: https:<span class="hljs-comment">//hexo.io/docs/one-command-deployment</span></span><br><span class="hljs-symbol">deploy:</span><br><span class="hljs-symbol">  type:</span> git<br><span class="hljs-symbol">  repo:</span> git@***.***.***.***:<span class="hljs-keyword">/home/</span>git/hexo.git<br><span class="hljs-symbol">  branch:</span> master<br></code></pre></td></tr></table></figure><p>除此之外，URL 项改为自己的域名，没有备案的化可以先填写公网 IP</p><figure class="highlight routeros"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><code class="hljs routeros"><span class="hljs-comment"># URL</span><br><span class="hljs-comment">## If your site is put in a subdirectory, set url as &#x27;http://yoursite.com/child&#x27; and root as &#x27;/child/&#x27;</span><br>url: www.xuwx.top<br>root: /<br>permalink: :year/:month/:day/:title/<br>permalink_defaults:<br>pretty_urls:<br>  trailing_index: <span class="hljs-literal">true</span> # <span class="hljs-built_in">Set</span> <span class="hljs-keyword">to</span> <span class="hljs-literal">false</span> <span class="hljs-keyword">to</span> <span class="hljs-built_in">remove</span> trailing <span class="hljs-string">&#x27;index.html&#x27;</span> <span class="hljs-keyword">from</span> permalinks<br>  trailing_html: <span class="hljs-literal">true</span> # <span class="hljs-built_in">Set</span> <span class="hljs-keyword">to</span> <span class="hljs-literal">false</span> <span class="hljs-keyword">to</span> <span class="hljs-built_in">remove</span> trailing <span class="hljs-string">&#x27;.html&#x27;</span> <span class="hljs-keyword">from</span> permalinks<br></code></pre></td></tr></table></figure><p>2.Hexo 自动部署<br>执行以下三个命令(每次文章发布的时候都需要用到)</p><figure class="highlight verilog"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><code class="hljs verilog">hexo clean<br>hexo <span class="hljs-keyword">generate</span><br>hexo deploy<br>或<br>hexo cl<br>hexo g<br>hexo d<br>或<br>hexo cl &amp;&amp; hexo g &amp;&amp; hexo d<br></code></pre></td></tr></table></figure><p>最后 hexo s 启动博客 </p><h5 id="4-参考的文章"><a href="#4-参考的文章" class="headerlink" title="4.参考的文章"></a>4.参考的文章</h5><p><a href="https://zhuanlan.zhihu.com/p/144774977">https://zhuanlan.zhihu.com/p/144774977</a></p><p><a href="https://www.jianshu.com/p/e1ccd49b4e5d">https://www.jianshu.com/p/e1ccd49b4e5d</a></p>]]>
      </content:encoded>
    </item>
  </channel>
</rss>
