架构图展示谁和谁通信,却无法展示通信的先后顺序,也看不出谁在等谁;而对很多问题来说,顺序就是全部。登录流程就是典型例子:同样的五条消息,顺序一变就是安全漏洞。
本文用一个大多数工程师迟早都会遇到的流程来讲解时序图表示法:一个单页应用通过带 PKCE 的 OAuth 2.0 授权码流程让用户登录,然后调用 API。
时序图展示什么
时间向下流动。每个参与方都有一条生命线,即一个下方带虚线的方框;每条消息是一支从发送方指向接收方的水平箭头。从上往下读,你得到事件的顺序;横向读,你能看出每一步有谁参与。
根据 UML 2.5.1 规范,表示法如下:
| 元素 | 画法 | 含义 |
|---|---|---|
| 生命线 | 下方带虚线的方框 | 参与其中的系统、服务或组件 |
| 参与者 | 火柴人 | 一个人或一个外部角色 |
| 同步消息 | 实线,实心箭头 | 一次调用;发送方等待回应 |
| 异步消息 | 实线,空心箭头 | 发送方不等待回应的消息 |
| 返回消息 | 虚线 | 对之前某条消息的回应 |
| 组合片段 | 带操作符的方框:alt、opt、loop、par |
按条件、重复或并行执行的部分 |
| 注释 | 折角的方框 | 一条说明,例如某个参与方所做的校验 |
流程:带 PKCE 的授权码模式
单页应用是公共客户端:它发给浏览器的一切都可以被读取,所以无法保管客户端密钥。RFC 7636 定义的 PKCE 依然能保护授权码:应用可以证明来兑换授权码的,正是发起这次流程的同一个实例。OAuth 2.0 安全最佳当前实践 RFC 9700 要求公共客户端使用 PKCE,建议其他所有客户端也使用,并指出客户端不应使用隐式授权(implicit grant)。
步骤如下:
- 用户点击“登录”。
- 应用生成 code verifier 和 challenge。
code_verifier是一个 43 到 128 个字符的随机字符串。code_challenge是BASE64URL(SHA256(code_verifier)),即 S256 方法。应用还会保存一个随机的state。 - 应用带着 challenge 把浏览器重定向到授权端点:
GET /authorize?response_type=code
&client_id=orders-spa
&redirect_uri=https%3A%2F%2Fapp.example.com%2Fcallback
&scope=orders.read
&state=af0ifjsldkj
&code_challenge=E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM
&code_challenge_method=S256 HTTP/1.1
Host: auth.example.com
- 授权服务器让用户登录并请求授权同意。
- 它重定向回来,跳转到
redirect_uri,附带一个短期有效的code和同一个state。 - 应用校验
state,与它保存的值进行比对。 - 应用在令牌端点兑换授权码,并发送它保存的 verifier:
POST /token HTTP/1.1
Host: auth.example.com
Content-Type: application/x-www-form-urlencoded
grant_type=authorization_code&code=SplxlOBeZQQYbYS6WxSbIA
&redirect_uri=https%3A%2F%2Fapp.example.com%2Fcallback
&client_id=orders-spa&code_verifier=dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk
- 授权服务器对 verifier 做哈希,并把结果与第 3 步的 challenge 比较。如果一致,就返回访问令牌;如果它会签发刷新令牌,也一并返回。
- 应用调用 API,携带
Authorization: Bearer <access token>。 - API 校验令牌,例如签名、签发者、受众、过期时间和权限范围,然后作出回应。
上面的 verifier 和 challenge 是 RFC 7636 中的示例值,你可以自己验证 S256 的计算结果。
图
图表内容
- User
- Browser app
- Authorization server
- API
- [access token expired]
- 1 Sign in
- 2 GET /authorize + code_challenge, state
- 3 Sign-in and consent page
- 4 Credentials, consent
- 5 Redirect: code, state
- 6 POST /token + code, code_verifier
- 7 access_token (+ refresh_token)
- 8 GET /orders, Bearer access_token
- 9 200 OK
- 10 GET /orders, expired token
- 11 401 invalid_token
- 12 POST /token + refresh_token
- 13 New access and refresh tokens
- 14 Retry with the new token
这张图的读法如下:
- 四个参与方排成一行:作为参与者的用户,然后是浏览器应用、授权服务器和 API 的生命线。每条消息都是位于自己高度的一支水平箭头,按顺序编号。
- 消息 2 到 7 是授权码流程。注释展示了保证其安全的两项校验:应用在重定向后比对
state,授权服务器把哈希后的 verifier 与 challenge 进行比较。 - 消息 8 和 9 是 API 调用。返回消息是虚线,箭头为空心。
opt片段涵盖访问令牌过期的情况:一个 401、一次刷新和一次重试。
不只画正常路径,也要画失败路径
大多数时序图都停在“200 OK”。有意思的 bug 都藏在分支里,所以至少要把每天都会发生的那个分支画出来。如果图中有多个互斥的分支、每个分支各带一个守卫条件,就用 alt;如果只有一个部分、仅在守卫条件为真时执行,就用 opt。这里的刷新是一个 opt:只有访问令牌过期时才会发生。
刷新分支有它自己的规则。在浏览器应用中,刷新令牌通常是 bearer 令牌,因此 RFC 9700 以及 2026 年 8 月发布的浏览器应用最佳当前实践 RFC 10017 要求授权服务器在每次使用时轮换刷新令牌或将其绑定到客户端,并限制其有效期。消息 13 返回一个新的刷新令牌,正是这个原因。
RFC 10017 还按安全性给浏览器应用的架构排了序。把令牌保存在服务器端的 backend-for-frontend 最安全;像本图这样由浏览器应用自己充当 OAuth 客户端的方式最简单,也最容易受到恶意 JavaScript 的攻击。如果你选择了浏览器模式,图就是写明原因的好地方。
时序图的常见错误
- 生命线太多。 超过六七条,标签就会挤在一起,读者也会跟丢。把流程拆开,或者把内部服务归到一条生命线后面。
- 返回消息没有标签。 “响应”什么也没说明。写出状态码或内容,例如“302 带 code”“401 invalid_token”。
- 细节层次混杂。 一支箭头上写 HTTP 参数,下一支写“用户很满意”,会让两类读者都困惑。选定一个层次并保持一致。
- 只有正常路径。 超时、重试和过期的凭据,恰恰是图最能体现价值的地方。
- 列出所有参数。 只展示读者必须核对的参数,例如
state、code_challenge和code_verifier,其余的交给 API 参考文档。
什么时候用,什么时候不用
协议、握手、重试、超时、竞态条件和事故时间线,凡是顺序和等待很重要的场景,都适合用时序图。静态结构用架构图或 C4 图;面向大量消费者的事件扇出,用事件驱动架构图;检查数据在哪里跨越信任边界的安全评审,用威胁建模中的数据流图。
在 Archboard 中构建
- 使用 Google 或 GitHub 登录,并新建一张图。
- 从时序节点包中添加一个 参与者 表示用户,再为浏览器应用、授权服务器和 API 添加三个 生命线 节点,排成一行、高度相同。
- 把每条消息画成两条生命线之间的一条直线连线,从上往下排列,并加上编号标签。返回消息设为虚线。
- 在消息 10 到 14 周围添加一个 组合片段,把操作符设为
opt,标签设为“[access token expired]”。 - 添加 UML 节点包中的 注释 节点,写明各项校验:
state、哈希后的 verifier 和令牌校验。
这个示例共用 5 个图表节点,在 Free 套餐的限制之内。Archboard 自己的 MCP 服务器也用这个流程让 AI 应用登录,即带 PKCE(S256)的授权码授权;请参阅如何用 AI 智能体绘制架构图。
常见问题
PKCE 只适用于移动应用吗? 不是。它最初为原生应用设计,但 RFC 9700 要求包括单页应用在内的所有公共客户端都使用 PKCE,并建议机密客户端也使用。
单页应用应该用隐式流程吗? 不应该。RFC 9700 指出客户端不应使用隐式授权。请使用带 PKCE 的授权码流程。
OAuth 2.1 已经是正式标准了吗? 截至撰写本文时(2026 年 10 月)还不是。它仍是一份 IETF Internet-Draft,整合了 OAuth 2.0 及其安全实践,其中包括 PKCE。
alt 和 opt 有什么区别?
alt 包含两个或更多互斥的分支,每个分支各带一个守卫条件。opt 只包含一个部分,仅在其守卫条件为真时执行。
一张时序图能放多少条消息? 以人们查看时的尺寸仍然清晰为限,通常是 15 到 20 条。更长的流程应按阶段拆成多张图。