欢迎光临
我们一直在努力

给 Web、CLI 和远程服务器做一套统一授权

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */
#content_views .toc,
/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */
#content_views.markdown_views > p:empty:has(+ .toc),
#content_views.markdown_views > .toc + p:empty,
/* 富文本旧版目录标记 */
#content_views.htmledit_views #main-toc,
#content_views.htmledit_views #hr-toc,
#content_views.htmledit_views p[id*=\”-toc\”] {
display: none !important;
}
/* 目录去掉后,紧跟的首个标题不再多出一块上边距 */
#content_views.markdown_views > .toc + h1,
#content_views.markdown_views > .toc + h2,
#content_views.markdown_views > .toc + h3,
#content_views.markdown_views > .toc + h4,
#content_views.markdown_views > .toc + p:empty + h1,
#content_views.markdown_views > .toc + p:empty + h2,
#content_views.markdown_views > .toc + p:empty + h3,
#content_views.markdown_views > .toc + p:empty + h4 {
margin-top: 0 !important;
}

很多系统的账号体系都是一点点长出来的。

网站先有邮箱密码登录,后来接入 Google;桌面工具嫌网页登录麻烦,增加一串 API Key;命令行工具再复制一套 Token;远程服务器无法打开浏览器,又写一个临时验证码。等到需要统一权限、统一计费和统一撤销时,才发现“同一个用户”在不同入口里已经变成了四种身份。

这篇文章用一个完整但不绑定具体业务的例子,做一套统一授权系统。目标不是实现另一个账号密码页面,而是让这些客户端共享同一个身份和授权中心:

  • Web 应用;
  • 浏览器中的单页应用;
  • Windows、macOS、Linux 桌面程序;
  • WSL 中运行的 CLI;
  • SSH 登录的远程 CLI;
  • 后台服务和自动化任务;
  • 多个独立部署的资源 API。

示例域名统一使用 example.com,代码只展示协议边界和关键逻辑,不依赖某个现成项目。

统一的究竟是什么

“统一登录”很容易被理解成所有页面共用一个登录框。真正要统一的至少有四层。

身份统一

无论用户从网站、CLI 还是桌面应用登录,最终都对应同一个稳定的用户 ID。邮箱、手机号、GitHub subject 和企业 SSO subject 都只是登录凭据,不应成为业务主键。

user_id = 7f…91
├── password identity
├── google subject
├── github subject
└── enterprise subject

授权统一

客户端不能因为“已经登录”就自动获得所有能力。授权需要明确回答:

  • 哪个客户端在请求?
  • 请求访问哪个资源?
  • 请求哪些 scope?
  • 用户是否批准?
  • 管理策略是否允许?

凭证统一

资源 API 不应该分别理解网站 Cookie、CLI 配置文件和第三方登录结果。它们只需要验证统一签发的 access token,并检查 issuer、audience、expiry 和 scope。

用量与计费统一

同一用户可能通过多个客户端消费同一种 API。计费不能只记录 token 里的 sub,还要保留客户端、资源、计费归属和授权方式,才能回答“谁通过什么应用消费了什么”。

总体架构:授权中心不等于资源服务器

先给虚构系统划分三个域名:

https://login.example.com 授权服务器与账号中心
https://account.example.com 账号资源 API
https://api.example.com 业务资源 API

授权服务器负责登录、同意页、客户端管理、授权码和 token。资源服务器负责业务数据。两者可以由同一个团队维护,但不能在概念上揉成一个服务。

图中可以按三层理解。

客户端层

浏览器、桌面程序、本地 CLI 和远程 CLI 都是 OAuth client。它们的运行环境不同,保密能力也不同,因此不能注册成同一种 client:

客户端
类型
推荐授权方式
能否保存 client secret
服务端 Web confidential Authorization Code + PKCE 可以,保存在服务端
浏览器 SPA public Authorization Code + PKCE 不可以
桌面/本地 CLI public Authorization Code + PKCE 不可以
远程/无头 CLI public Device Authorization 不可以
后台服务 confidential Client Credentials 或工作负载身份 可以,但优先非对称凭证

把一个 secret 写进公开发布的 Python 包、npm 包或桌面安装包,不会让 public client 变成 confidential client。用户总能把它取出来。

五类客户端分别怎么接入

统一授权不是强迫所有客户端走完全相同的页面和回调。统一的是 issuer、身份、grant、scope、resource、token 语义与撤销机制;客户端根据自身运行边界选择合适的 grant。

下面五张图都使用 1–5 编号,图后的清单与编号一一对应。

服务端 Web:Token 留在后端

服务端渲染网站或带 BFF(Backend for Frontend)的 Web 应用,适合使用 confidential client。浏览器只持有网站自己的 Session Cookie,OAuth access token 和 refresh token 留在后端。

服务端 Web 使用 Authorization Code 与 PKCE,Token 保存在后端

  • 浏览器访问 Web 应用,后端生成随机 state、nonce 和 PKCE verifier,把必要的事务状态保存在服务端 Session。
  • 后端把浏览器重定向到授权中心。client_secret 不出现在浏览器中。
  • 用户在授权中心登录并确认资源与权限范围。
  • 授权中心通过浏览器把一次性 code 返回 Web 后端的 HTTPS callback。
  • 后端使用 code、verifier 和自身 client 认证兑换 token,把 refresh token 加密存储,再向浏览器签发 HttpOnly + Secure + SameSite Session Cookie。
  • 资源 API 调用由后端发起:

    Browser –session cookie–> Web/BFF –access token–> Resource API

    这种模式的优点是 token 不进入 JavaScript 环境,适合高价值账号和复杂业务。代价是 Web 后端需要管理 Session、Token 刷新和 CSRF 防护。

    不要把后端拿到的 refresh token 再返回给浏览器,也不要为了“前后端统一”把 confidential client secret 编译进前端包。

    浏览器 SPA:Public Client + PKCE

    纯 SPA 没有可信后端,发布到浏览器的任何 secret 都可以被查看,因此必须注册为 public client。

  • SPA 在浏览器内存中生成 state、nonce、verifier 和 challenge,不生成 client secret。
  • 浏览器跳转到授权中心,redirect URI 必须是预注册的 HTTPS 地址。
  • 用户登录并批准 scope。
  • SPA callback 校验 state,使用 code + verifier 兑换 token;短期 access token 优先保存在内存,而不是 localStorage。
  • SPA 携带 access token 调用资源 API;需要长期会话时使用受控的 refresh token rotation,或者改成 BFF 架构。
  • 浏览器刷新会清空内存,这是安全性与体验之间的真实取舍。可以通过静默 Session 检查或 BFF 恢复登录,不要因为怕用户重新登录就把长期 token 永久塞进可被任意脚本读取的存储。

    SPA 还需要严格配置 CORS,并确保授权回调页不加载广告、统计脚本等无关第三方资源,避免 code 出现在 Referer 或前端日志里。

    本地桌面程序和 CLI:Loopback Callback

    桌面应用和本地 CLI 同样是 public client,但可以临时监听本机回环地址,让系统浏览器把授权码交回来。

    赞(0)
    未经允许不得转载:171主机测评 » 给 Web、CLI 和远程服务器做一套统一授权
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址