<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/">
  <channel>
    <title>OAuth on JiaLog</title>
    <link>https://jialog.top/tags/oauth/</link>
    <description>Recent content in OAuth on JiaLog</description>
    <image>
      <title>JiaLog</title>
      <url>https://jialog.top/images/papermod-cover.png</url>
      <link>https://jialog.top/images/papermod-cover.png</link>
    </image>
    <generator>Hugo -- 0.144.2</generator>
    <language>en</language>
    <copyright>JiaLog</copyright>
    <lastBuildDate>Tue, 12 Aug 2025 00:00:00 +0000</lastBuildDate>
    <atom:link href="https://jialog.top/tags/oauth/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>Project 6 - 令牌撤销与单点登出</title>
      <link>https://jialog.top/posts/oauth2.0/project-6-%E4%BB%A4%E7%89%8C%E6%92%A4%E9%94%80%E4%B8%8E%E5%8D%95%E7%82%B9%E7%99%BB%E5%87%BA/</link>
      <pubDate>Tue, 12 Aug 2025 00:00:00 +0000</pubDate>
      <guid>https://jialog.top/posts/oauth2.0/project-6-%E4%BB%A4%E7%89%8C%E6%92%A4%E9%94%80%E4%B8%8E%E5%8D%95%E7%82%B9%E7%99%BB%E5%87%BA/</guid>
      <description>&lt;h1 id=&#34;实验目标&#34;&gt;实验目标&lt;/h1&gt;
&lt;p&gt;恭喜你来到最后一个实验！本周我们将实现 OAuth 2.0 系统的&lt;strong&gt;收尾工作&lt;/strong&gt;：令牌撤销和单点登出。完成本实验后，你将能够：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;✅ 理解令牌撤销的重要性和应用场景&lt;/li&gt;
&lt;li&gt;✅ 实现符合 RFC 7009 的令牌撤销端点&lt;/li&gt;
&lt;li&gt;✅ 理解单点登出（Single Logout, SLO）的工作原理&lt;/li&gt;
&lt;li&gt;✅ 实现前端通道登出（Front-Channel Logout）&lt;/li&gt;
&lt;li&gt;✅ 理解后端通道登出（Back-Channel Logout）的概念&lt;/li&gt;
&lt;li&gt;✅ 完成整个 OAuth 2.0 授权服务器的实现&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h1 id=&#34;实验任务&#34;&gt;实验任务&lt;/h1&gt;
&lt;h2 id=&#34;任务-1完善令牌管理器的撤销逻辑&#34;&gt;任务 1：完善令牌管理器的撤销逻辑&lt;/h2&gt;
&lt;h3 id=&#34;文件位置&#34;&gt;文件位置&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;smart-sso-server/src/main/java/com/smart/sso/server/token/LocalTokenManager.java&lt;/code&gt;&lt;/p&gt;
&lt;h3 id=&#34;11-完善-remove-方法&#34;&gt;1.1 完善 &lt;code&gt;remove&lt;/code&gt; 方法&lt;/h3&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-java&#34; data-lang=&#34;java&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nd&#34;&gt;@Override&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;public&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;remove&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;String&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;refreshToken&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;// TODO 1: 获取令牌内容&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;// 提示：使用 refreshTokenCache.getIfPresent() 方法&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;// 思考：如果令牌不存在应该如何处理？（幂等性）&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;// TODO 2: 从刷新令牌缓存中移除&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;// 提示：使用 refreshTokenCache.invalidate() 方法&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;// TODO 3: 移除关联的访问令牌&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;// 提示：从 TokenContent 中获取 accessToken&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;// 注意：访问令牌可能为 null，需要判空&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;// TODO 4: 从 TGT 反向索引中移除&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;// 提示：&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;// - 获取 TGT 和对应的令牌集合&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;// - 从集合中移除当前刷新令牌&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;// - 如果集合为空，清理整个 TGT 缓存条目&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;// - 否则更新缓存&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;// TODO 5: 记录日志&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;// 提示：记录刷新令牌和用户 ID（用于审计）&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;设计提示&lt;/strong&gt;：&lt;/p&gt;</description>
    </item>
    <item>
      <title>Project 5 - 令牌颁发与刷新令牌轮换</title>
      <link>https://jialog.top/posts/oauth2.0/project-5-%E4%BB%A4%E7%89%8C%E9%A2%81%E5%8F%91%E4%B8%8E%E5%88%B7%E6%96%B0%E4%BB%A4%E7%89%8C%E8%BD%AE%E6%8D%A2/</link>
      <pubDate>Tue, 05 Aug 2025 00:00:00 +0000</pubDate>
      <guid>https://jialog.top/posts/oauth2.0/project-5-%E4%BB%A4%E7%89%8C%E9%A2%81%E5%8F%91%E4%B8%8E%E5%88%B7%E6%96%B0%E4%BB%A4%E7%89%8C%E8%BD%AE%E6%8D%A2/</guid>
      <description>&lt;h1 id=&#34;实验目标&#34;&gt;实验目标&lt;/h1&gt;
&lt;p&gt;本周是整个项目的&lt;strong&gt;核心部分&lt;/strong&gt;！我们将实现 OAuth 2.0 的引擎：令牌管理系统。完成本实验后，你将能够：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;✅ 理解访问令牌和刷新令牌的不同生命周期&lt;/li&gt;
&lt;li&gt;✅ 实现令牌的创建、验证和存储&lt;/li&gt;
&lt;li&gt;✅ 实现刷新令牌轮换（Refresh Token Rotation）&lt;/li&gt;
&lt;li&gt;✅ 实现刷新令牌盗窃检测机制&lt;/li&gt;
&lt;li&gt;✅ 理解令牌家族（Token Family）的概念&lt;/li&gt;
&lt;li&gt;✅ 掌握令牌与 TGT 的关联管理&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h1 id=&#34;理论背景&#34;&gt;理论背景&lt;/h1&gt;
&lt;h2 id=&#34;1-访问令牌-vs-刷新令牌&#34;&gt;1. 访问令牌 vs. 刷新令牌&lt;/h2&gt;
&lt;h3 id=&#34;11-核心区别&#34;&gt;1.1 核心区别&lt;/h3&gt;
&lt;table&gt;
  &lt;thead&gt;
      &lt;tr&gt;
          &lt;th&gt;特性&lt;/th&gt;
          &lt;th&gt;访问令牌 (Access Token)&lt;/th&gt;
          &lt;th&gt;刷新令牌 (Refresh Token)&lt;/th&gt;
      &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
      &lt;tr&gt;
          &lt;td&gt;&lt;strong&gt;用途&lt;/strong&gt;&lt;/td&gt;
          &lt;td&gt;访问受保护的 API 资源&lt;/td&gt;
          &lt;td&gt;获取新的访问令牌&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;&lt;strong&gt;生命周期&lt;/strong&gt;&lt;/td&gt;
          &lt;td&gt;短（15 分钟）&lt;/td&gt;
          &lt;td&gt;长（30 天）&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;&lt;strong&gt;传输频率&lt;/strong&gt;&lt;/td&gt;
          &lt;td&gt;每次 API 请求都携带&lt;/td&gt;
          &lt;td&gt;仅在刷新时使用&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;&lt;strong&gt;泄露风险&lt;/strong&gt;&lt;/td&gt;
          &lt;td&gt;高（频繁传输）&lt;/td&gt;
          &lt;td&gt;低（使用频率低）&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;&lt;strong&gt;存储位置&lt;/strong&gt;&lt;/td&gt;
          &lt;td&gt;客户端内存&lt;/td&gt;
          &lt;td&gt;安全存储（加密）&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;&lt;strong&gt;撤销成本&lt;/strong&gt;&lt;/td&gt;
          &lt;td&gt;低（自动过期）&lt;/td&gt;
          &lt;td&gt;高（需要主动撤销）&lt;/td&gt;
      &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id=&#34;12-为什么需要两种令牌&#34;&gt;1.2 为什么需要两种令牌？&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;安全与体验的平衡&lt;/strong&gt;：&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;场景 1：仅使用访问令牌
问题：访问令牌过期后，用户必须重新登录
结果：用户体验差 ❌

场景 2：访问令牌永不过期
问题：一旦泄露，攻击者可永久访问
结果：安全性差 ❌

场景 3：访问令牌 + 刷新令牌
优势：
- 访问令牌短期有效，降低泄露风险 ✅
- 刷新令牌长期有效，无需频繁登录 ✅
- 刷新令牌可追踪和撤销 ✅
&lt;/code&gt;&lt;/pre&gt;&lt;hr&gt;
&lt;h2 id=&#34;2-刷新令牌轮换-refresh-token-rotation&#34;&gt;2. 刷新令牌轮换 (Refresh Token Rotation)&lt;/h2&gt;
&lt;h3 id=&#34;21-传统方法-vs-轮换方法&#34;&gt;2.1 传统方法 vs. 轮换方法&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;传统方法&lt;/strong&gt;（不推荐）：&lt;/p&gt;</description>
    </item>
    <item>
      <title>Project 3 - 全局会话管理 (TGT)</title>
      <link>https://jialog.top/posts/oauth2.0/project-3-%E5%85%A8%E5%B1%80%E4%BC%9A%E8%AF%9D%E7%AE%A1%E7%90%86/</link>
      <pubDate>Tue, 22 Jul 2025 00:00:00 +0000</pubDate>
      <guid>https://jialog.top/posts/oauth2.0/project-3-%E5%85%A8%E5%B1%80%E4%BC%9A%E8%AF%9D%E7%AE%A1%E7%90%86/</guid>
      <description>&lt;h1 id=&#34;实验目标&#34;&gt;实验目标&lt;/h1&gt;
&lt;p&gt;本周我们将实现单点登录的核心机制：**票据授予票据（TGT）**管理。完成本实验后，你将能够：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;✅ 理解 TGT 在 SSO 系统中的关键作用&lt;/li&gt;
&lt;li&gt;✅ 使用缓存实现会话的创建、检索和移除&lt;/li&gt;
&lt;li&gt;✅ 实现基于 TTL 的会话自动过期机制&lt;/li&gt;
&lt;li&gt;✅ 实现滑动窗口过期策略（提升用户体验）&lt;/li&gt;
&lt;li&gt;✅ 限制每个用户的并发会话数量（安全增强）&lt;/li&gt;
&lt;li&gt;✅ 理解会话级联失效机制（为单点登出做准备）&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h1 id=&#34;理论背景&#34;&gt;理论背景&lt;/h1&gt;
&lt;h2 id=&#34;1-什么是票据授予票据-tgt&#34;&gt;1. 什么是票据授予票据 (TGT)？&lt;/h2&gt;
&lt;h3 id=&#34;11-tgt-的定义&#34;&gt;1.1 TGT 的定义&lt;/h3&gt;
&lt;p&gt;TGT（Ticket Granting Ticket）是 SSO 系统中的&lt;strong&gt;全局会话令牌&lt;/strong&gt;，类似于传统 Web 应用中的 Session ID，但具有更强的安全性和跨应用能力。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;关键特性&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;唯一性&lt;/strong&gt;：每个 TGT 对应一个用户会话&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;安全性&lt;/strong&gt;：通常使用 UUID 或密码学随机数生成&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;时效性&lt;/strong&gt;：具有明确的过期时间（如 30 分钟无操作）&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;跨域性&lt;/strong&gt;：可在多个子应用间共享&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;12-tgt-在-oauth-20-流程中的作用&#34;&gt;1.2 TGT 在 OAuth 2.0 流程中的作用&lt;/h3&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;用户首次登录流程：
1. 用户访问应用 A → 重定向到 SSO 登录页
2. 用户输入账号密码 → SSO 验证成功
3. SSO 创建 TGT → 将 TGT ID 写入浏览器 Cookie
4. SSO 颁发授权码 → 应用 A 获取访问令牌

用户再次访问应用 B（单点登录体现）：
1. 用户访问应用 B → 重定向到 SSO
2. 浏览器自动携带 TGT Cookie → SSO 验证 TGT 有效
3. SSO 直接颁发授权码 → 应用 B 获取访问令牌
   （无需用户再次输入密码！）
&lt;/code&gt;&lt;/pre&gt;&lt;hr&gt;
&lt;h2 id=&#34;2-会话管理策略&#34;&gt;2. 会话管理策略&lt;/h2&gt;
&lt;h3 id=&#34;21-生存时间-ttl-策略&#34;&gt;2.1 生存时间 (TTL) 策略&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;固定过期时间&lt;/strong&gt;：&lt;/p&gt;</description>
    </item>
    <item>
      <title>Project 1 - 环境搭建与系统设计</title>
      <link>https://jialog.top/posts/oauth2.0/project-1-%E7%8E%AF%E5%A2%83%E6%90%AD%E5%BB%BA%E4%B8%8E%E7%B3%BB%E7%BB%9F%E8%AE%BE%E8%AE%A1/</link>
      <pubDate>Tue, 08 Jul 2025 00:00:00 +0000</pubDate>
      <guid>https://jialog.top/posts/oauth2.0/project-1-%E7%8E%AF%E5%A2%83%E6%90%AD%E5%BB%BA%E4%B8%8E%E7%B3%BB%E7%BB%9F%E8%AE%BE%E8%AE%A1/</guid>
      <description>&lt;h1 id=&#34;实验目标&#34;&gt;实验目标&lt;/h1&gt;
&lt;p&gt;本节主要完成以下任务：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;✅ 搭建完整的 Java 开发环境&lt;/li&gt;
&lt;li&gt;✅ 配置 MySQL 数据库并初始化数据&lt;/li&gt;
&lt;li&gt;✅ 配置 Redis 缓存服务&lt;/li&gt;
&lt;li&gt;✅ 运行项目并验证环境正确性&lt;/li&gt;
&lt;li&gt;✅ 理解 OAuth 2.0 基本概念和系统整体设计&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h1 id=&#34;详细任务&#34;&gt;详细任务&lt;/h1&gt;
&lt;h2 id=&#34;一环境搭建&#34;&gt;一、环境搭建&lt;/h2&gt;
&lt;h3 id=&#34;1-安装-jdk-17&#34;&gt;1. 安装 JDK 17&lt;/h3&gt;
&lt;h4 id=&#34;步骤&#34;&gt;步骤&lt;/h4&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;下载 JDK&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;官方版本：&lt;a href=&#34;https://www.oracle.com/java/technologies/javase/jdk17-archive-downloads.html&#34;&gt;Oracle JDK 17&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;开源版本：&lt;a href=&#34;https://adoptium.net/&#34;&gt;OpenJDK 17&lt;/a&gt;（推荐）&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;安装 JDK&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Windows&lt;/strong&gt;：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-powershell&#34; data-lang=&#34;powershell&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c&#34;&gt;# 双击运行 .exe 文件，按提示完成安装&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c&#34;&gt;# 默认安装路径：C:\Program Files\Java\jdk-17&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;macOS&lt;/strong&gt;：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 使用 Homebrew 安装（推荐）&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;brew install openjdk@17
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 或下载 .dmg 文件手动安装&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;Linux (Ubuntu/Debian)&lt;/strong&gt;：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sudo apt update
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sudo apt install openjdk-17-jdk
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;Linux (CentOS/RHEL)&lt;/strong&gt;：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sudo yum install java-17-openjdk-devel
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;配置环境变量&lt;/strong&gt;&lt;/p&gt;</description>
    </item>
    <item>
      <title>Project #0 - 实验准备</title>
      <link>https://jialog.top/posts/oauth2.0/project-0-%E5%AE%9E%E9%AA%8C%E5%87%86%E5%A4%87/</link>
      <pubDate>Sun, 22 Jun 2025 00:00:00 +0000</pubDate>
      <guid>https://jialog.top/posts/oauth2.0/project-0-%E5%AE%9E%E9%AA%8C%E5%87%86%E5%A4%87/</guid>
      <description>&lt;h1 id=&#34;重要提醒&#34;&gt;重要提醒&lt;/h1&gt;
&lt;p&gt;⚠️ &lt;strong&gt;请勿公开你的代码&lt;/strong&gt; - 本课程遵循学术诚信原则，代码仅供个人学习使用。&lt;/p&gt;
&lt;hr&gt;
&lt;h1 id=&#34;概述&#34;&gt;概述&lt;/h1&gt;
&lt;p&gt;本课程所有代码基于 &lt;strong&gt;Java&lt;/strong&gt; 编写。在开始项目之前，请确保你具备以下基础：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Java SE 基础&lt;/strong&gt;：面向对象编程、集合框架、异常处理&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Java Web 基础&lt;/strong&gt;：Spring Boot、Spring MVC、RESTful API&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;数据库基础&lt;/strong&gt;：SQL 基本操作、MyBatis 使用&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;缓存基础&lt;/strong&gt;：Redis 基本概念和操作&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;本项目严格按照 &lt;strong&gt;OAuth 2.0&lt;/strong&gt; 协议标准实现，主要基于&lt;strong&gt;授权码模式&lt;/strong&gt;（Authorization Code Flow）。&lt;/p&gt;
&lt;h2 id=&#34;必读材料&#34;&gt;必读材料&lt;/h2&gt;
&lt;p&gt;在开始实验前，请仔细阅读以下材料：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;OAuth 2.0 核心概念&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://www.ruanyifeng.com/blog/2014/05/oauth_2_0.html&#34;&gt;理解OAuth 2.0 - 阮一峰的网络日志&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://datatracker.ietf.org/doc/html/rfc6749&#34;&gt;RFC 6749 - The OAuth 2.0 Authorization Framework&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;授权码模式深入理解&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;重点关注：授权请求、授权响应、令牌请求、令牌响应的完整流程&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;安全增强特性&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://datatracker.ietf.org/doc/html/rfc7009&#34;&gt;RFC 7009 - OAuth 2.0 Token Revocation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://datatracker.ietf.org/doc/html/rfc7636&#34;&gt;RFC 7636 - Proof Key for Code Exchange (PKCE)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h2 id=&#34;你的任务&#34;&gt;你的任务&lt;/h2&gt;
&lt;p&gt;本实验采用**&amp;ldquo;完形填空&amp;rdquo;**的形式：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;理解项目架构&lt;/strong&gt;：阅读提供的框架代码，理解各模块的职责&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;完成关键函数&lt;/strong&gt;：在标记了 &lt;code&gt;// TODO&lt;/code&gt; 的地方实现核心逻辑&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;通过单元测试&lt;/strong&gt;：每个实验都配有 JUnit 测试用例&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;手动测试验证&lt;/strong&gt;：使用 Postman 或其他工具进行端到端测试&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&#34;实验评估方式&#34;&gt;实验评估方式&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;自动化测试&lt;/strong&gt;：每个模块都有对应的单元测试（约 60%）&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;功能验证&lt;/strong&gt;：手动测试关键流程（约 30%）&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;代码质量&lt;/strong&gt;：代码规范性和安全性（约 10%）&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;重要提醒&lt;/strong&gt;：本项目没有完全自动化的评分系统，请以&lt;strong&gt;学到知识&lt;/strong&gt;为目标，诚信完成实验。&lt;/p&gt;</description>
    </item>
    <item>
      <title>Project 4 - 授权码管理与 PKCE</title>
      <link>https://jialog.top/posts/oauth2.0/project-4-%E6%8E%88%E6%9D%83%E7%A0%81%E7%AE%A1%E7%90%86%E4%B8%8Epkce/</link>
      <pubDate>Wed, 29 Jan 2025 00:00:00 +0000</pubDate>
      <guid>https://jialog.top/posts/oauth2.0/project-4-%E6%8E%88%E6%9D%83%E7%A0%81%E7%AE%A1%E7%90%86%E4%B8%8Epkce/</guid>
      <description>&lt;h1 id=&#34;实验目标&#34;&gt;实验目标&lt;/h1&gt;
&lt;p&gt;本周我们将实现 OAuth 2.0 的核心：&lt;strong&gt;授权码流程&lt;/strong&gt;。完成本实验后，你将能够：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;✅ 理解授权码在 OAuth 2.0 流程中的关键作用&lt;/li&gt;
&lt;li&gt;✅ 实现授权码的创建和一次性使用验证&lt;/li&gt;
&lt;li&gt;✅ 理解授权码被截获的安全威胁&lt;/li&gt;
&lt;li&gt;✅ 实现 PKCE（Proof Key for Code Exchange）安全增强&lt;/li&gt;
&lt;li&gt;✅ 掌握 SHA-256 哈希和 Base64 URL 编码&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h1 id=&#34;理论背景&#34;&gt;理论背景&lt;/h1&gt;
&lt;h2 id=&#34;1-授权码流程详解&#34;&gt;1. 授权码流程详解&lt;/h2&gt;
&lt;h3 id=&#34;11-为什么需要授权码&#34;&gt;1.1 为什么需要授权码？&lt;/h3&gt;
&lt;p&gt;OAuth 2.0 使用&lt;strong&gt;两步验证&lt;/strong&gt;而非直接颁发访问令牌，主要出于安全考虑：&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;❌ 不安全的单步流程（隐式授权）：
用户登录 → SSO 直接返回访问令牌到浏览器
问题：访问令牌暴露在浏览器历史记录、日志中

✅ 安全的两步流程（授权码）：
步骤 1：用户登录 → SSO 返回临时授权码到浏览器
步骤 2：客户端后端用授权码 + 密钥换取访问令牌
优势：访问令牌只在后端传输，永不经过浏览器
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id=&#34;12-授权码的生命周期&#34;&gt;1.2 授权码的生命周期&lt;/h3&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;时间轴：
T0: 用户完成登录，SSO 生成授权码
    授权码特性：
    - 随机生成（UUID 或密码学随机数）
    - 生命周期极短（5 分钟）
    - 一次性使用（use once and destroy）

T0+30s: 客户端用授权码换取令牌
        授权码被立即销毁

T0+5min: 授权码自动过期
         （即使未被使用）
&lt;/code&gt;&lt;/pre&gt;&lt;hr&gt;
&lt;h2 id=&#34;2-授权码截获攻击与-pkce&#34;&gt;2. 授权码截获攻击与 PKCE&lt;/h2&gt;
&lt;h3 id=&#34;21-授权码截获攻击场景&#34;&gt;2.1 授权码截获攻击场景&lt;/h3&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;攻击场景：恶意应用截获授权码

正常流程：
1. 合法 App 发起授权请求
2. 用户登录，SSO 重定向到 callback URL
3. 恶意 App 拦截重定向（通过注册相同的 URL Scheme）
4. 恶意 App 获取授权码
5. 恶意 App 用授权码 + 客户端密钥换取令牌 ← 问题！

在移动应用中，客户端密钥无法安全存储
（反编译即可获取）
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id=&#34;22-pkce-工作原理&#34;&gt;2.2 PKCE 工作原理&lt;/h3&gt;
&lt;p&gt;PKCE（读作 &amp;ldquo;pixy&amp;rdquo;）通过动态密钥解决此问题：&lt;/p&gt;</description>
    </item>
    <item>
      <title>Project 2 - 用户和客户端验证</title>
      <link>https://jialog.top/posts/oauth2.0/project-2-%E7%94%A8%E6%88%B7%E5%92%8C%E5%AE%A2%E6%88%B7%E7%AB%AF%E9%AA%8C%E8%AF%81/</link>
      <pubDate>Wed, 15 Jan 2025 00:00:00 +0000</pubDate>
      <guid>https://jialog.top/posts/oauth2.0/project-2-%E7%94%A8%E6%88%B7%E5%92%8C%E5%AE%A2%E6%88%B7%E7%AB%AF%E9%AA%8C%E8%AF%81/</guid>
      <description>&lt;h1 id=&#34;实验目标&#34;&gt;实验目标&lt;/h1&gt;
&lt;p&gt;本周我们将实现 OAuth 2.0 授权流程的第一道防线：&lt;strong&gt;身份验证&lt;/strong&gt;。完成本实验后，你将能够：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;✅ 实现安全的用户密码验证（使用 BCrypt）&lt;/li&gt;
&lt;li&gt;✅ 实现账户锁定机制（防止暴力破解）&lt;/li&gt;
&lt;li&gt;✅ 实现密码过期策略（强制定期更换密码）&lt;/li&gt;
&lt;li&gt;✅ 实现客户端应用验证（验证 &lt;code&gt;client_id&lt;/code&gt;、&lt;code&gt;client_secret&lt;/code&gt; 和 &lt;code&gt;redirect_uri&lt;/code&gt;）&lt;/li&gt;
&lt;li&gt;✅ 理解重定向 URI 验证的安全重要性&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h1 id=&#34;理论背景&#34;&gt;理论背景&lt;/h1&gt;
&lt;h2 id=&#34;1-用户认证的安全原则&#34;&gt;1. 用户认证的安全原则&lt;/h2&gt;
&lt;h3 id=&#34;11-密码存储&#34;&gt;1.1 密码存储&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;绝对禁止&lt;/strong&gt;：以明文或可逆加密方式存储密码&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;推荐方案&lt;/strong&gt;：使用强单向哈希算法（BCrypt、Argon2、PBKDF2）&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;BCrypt 优势&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;内置盐值（Salt），每次哈希结果不同&lt;/li&gt;
&lt;li&gt;计算成本可调（通过 &lt;code&gt;work factor&lt;/code&gt; 参数）&lt;/li&gt;
&lt;li&gt;抗彩虹表攻击&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;示例：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-java&#34; data-lang=&#34;java&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// 生成密码哈希&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;String&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;hashedPassword&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;passwordEncoder&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;encode&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;userPassword123&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;// 输出类似：$2a$10$N9qo8uLOickgx2ZMRZoMye1J9rY7AVMZ8tPNLPZdRs5u5xP5c5fUa&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;// 验证密码&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;boolean&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;matches&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;passwordEncoder&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;matches&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;userPassword123&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;hashedPassword&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;// 返回 true&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;12-账户锁定策略&#34;&gt;1.2 账户锁定策略&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;目的&lt;/strong&gt;：防止暴力破解攻击&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;实现方案&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;追踪每个用户的连续登录失败次数&lt;/li&gt;
&lt;li&gt;达到阈值（如 5 次）后锁定账户一段时间（如 10 分钟）&lt;/li&gt;
&lt;li&gt;成功登录后清除失败计数器&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;使用缓存的原因&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;高性能：失败计数不需要持久化到数据库&lt;/li&gt;
&lt;li&gt;自动过期：利用缓存的 TTL 特性实现锁定时间&lt;/li&gt;
&lt;li&gt;降低数据库压力&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;13-密码过期策略&#34;&gt;1.3 密码过期策略&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;目的&lt;/strong&gt;：降低长期使用同一密码的风险&lt;/p&gt;</description>
    </item>
  </channel>
</rss>
