外观
单点登录(OAuth2)
字数
2717 字
预计阅读
11 分钟
栖梧可以当作一个 OAuth2 授权服务器,让别的系统接入:
- 第三方网站或应用可以在登录页放一个"用栖梧账号登录"的按钮。用户点击后来到栖梧的授权页,同意以后,第三方就能读取这个用户的基本资料(用户名、姓名、头像、界面语言),用来登录或关联自己的账号;
- 机器客户端(比如另一个系统的后台服务)可以用客户端标识和密钥直接申请令牌,不代表任何用户。
用户在栖梧已经登录时,授权只需要点一次"同意授权";记住授权以后,连这一步也会跳过,第三方的登录几乎是无感的。
给第三方开发者
完整的接入步骤、请求和响应示例见 OAuth2 接入指南。这一页只讲管理员和用户能看到的部分。
客户端管理
每个要接入的系统都要先登记成一个客户端。在 系统管理 → 客户端管理 中新增,主要填写:
- 客户端标识:第三方接入时用的 ID,比如
crm。由管理员填写,只能用小写字母、数字和._-,3 到 64 个字符,创建后不能修改。console和mobile是本系统自己保留的,不能使用; - 客户端名称和图标:显示在授权页上,让用户知道是谁在申请;
- 授权方式:授权码(让用户授权)、刷新令牌(访问令牌到期后换一对新令牌,用户不用再授权)、客户端凭证(机器客户端),至少选一项;选刷新令牌必须同时选授权码;
- 回调地址:用户同意或拒绝后回到第三方的地址,最多 10 个。必须是
https://,本机调试可以用http://localhost或http://127.0.0.1; - 授权范围和自动授权范围:目前只有一个范围
user.read(读取基本资料)。放进"自动授权范围"的不用问用户,授权页上也不出现; - 访问令牌有效期和刷新令牌有效期:每个客户端单独设置,见会话和令牌。
字段的详细规则和列表上的操作见系统管理 · 客户端管理。这里只说几条要特别注意的:
- 密钥只显示一次。保存新客户端后,弹窗"保存客户端密钥"里显示密钥,复制保存后点"我已保存"。之后在页面、接口和日志里都看不到它。丢了只能点"重置密钥",重置后旧密钥立即失效,已经签发的令牌不受影响;
- 内置客户端
console(管理后台自己)在列表里只读,不能编辑、停用、重置密钥或删除; - 停用或删除客户端(包括批量删除),它的所有会话立即结束,用户授权的和客户端凭证的都算在内。删除以后,同一个客户端标识可以重新登记,但它是一个新的客户端:旧会话、没兑换的授权码和用户记住的授权都不会转到新客户端上。
权限:oauth.client.browse(浏览)、oauth.client.view(查看)、oauth.client.create(新增)、oauth.client.modify(修改)、oauth.client.remove(删除)、oauth.client.reset-secret(重置密钥)。
授权同意页
第三方把用户的浏览器带到本站的 /sso 页面(地址里带着授权请求的参数),用户在这里决定是否授权:
- 标题是"授权登录",下面一行"以 (你的显示名) 的身份继续",显示的是当前登录用户的显示名;
- 显示客户端的图标(没有图标时显示客户端名称的首字)、名称和标识,然后是"(客户端名称) 请求访问你的账号:"和申请的权限列表,比如"读取你的基本资料(用户名、姓名、头像)";
- 下面一行"授权后将跳转到 ……",显示回调地址的主机名。用户可以借此认出冒充的应用;
- 三个按钮:同意授权、拒绝、切换账号。同意后回到第三方并带上授权码;拒绝后也回到第三方,但只带一个"用户拒绝"的结果;切换账号会先退出当前账号,换一个账号登录后回到这个授权页。
几种特殊情况:
- 还没登录:先跳到登录页,登录后自动回到同一个授权页。授权过程中登录过期了也一样;
- 请求无效:客户端不存在或已停用、回调地址没有登记、缺少 PKCE 参数、申请的范围超出登记的范围等。页面停在
/sso,显示服务端给出的原因和"返回首页",不会跳转到任何地址,以免把用户送到没有核实过的网站; - 不需要询问:申请的范围都是自动授权的,或者都已经记住授权,页面显示"正在跳转…"后直接回到第三方。
同意和拒绝都会记入操作日志,日志里不会出现授权码。
记住授权
用户同意一次以后,在一段时间内再授权同一个客户端,就不会再看到同意页。
- 天数由参数
oauth.consent_ttl_days("记住授权天数(0 = 每次询问)")决定,默认 30 天,在 系统管理 → 参数设置 中修改,可以填 0 到 3650,填错时按 30 天处理; - 设为 0 时不再记住,已经记住的也不再生效,每次都要用户点同意;
- 同意页上没有"记住授权"的勾选框,同意时这次申请的所有范围一起记住。每次同意都把有效期重新算满;每个客户端分开记;
- 自动授权范围本来就不需要用户同意,所以不用记,授权页也不会出现。
目前还没有让用户自己撤回授权的页面。需要撤回时,管理员可以停用或删除这个客户端,或者把参数设为 0。删除用户或删除客户端时,相关的授权记录一起作废。
会话和令牌
第三方拿到的令牌,在本系统里是一个普通的会话,只是用途受限:
- 在线用户里能看到:在 系统监控 → 在线用户 中,第三方会话的"客户端"一列显示它的客户端标识,筛选时也可以直接输入客户端标识。强退后令牌立即失效。客户端凭证申请的会话不属于任何用户,只有超级管理员能看到和强退;
- 不能调用后台接口:第三方令牌只能读取用户信息,调用后台的其他接口一律返回 401,也不能替用户在同意页上点同意;
- 账号变化时一起失效:用户修改密码、更换手机号、被管理员重置密码或用短信找回密码、被停用、被删除、被管理员"强退该用户",或者在锁屏、改密码、换手机号时连续输错当前密码,他在所有第三方的会话,以及还没兑换的授权码,都会一起失效。只强退某一个会话时,只结束那一个;
- 有效期按客户端设置:访问令牌有效期 300 到 86400 秒,默认 1800 秒(30 分钟),到期不会自动续期,要用刷新令牌换新的;刷新令牌有效期 3600 到 2592000 秒,默认 604800 秒(7 天),同时也是这次授权的最长时间,从第三方应用用授权码换到令牌时算起,刷新不会延长。到期后用户要重新授权(记住授权期间只是一次跳转)。
限制
- 只支持机密客户端:申请、校验、撤销令牌时都必须带客户端密钥,所以接入方必须有自己的后端。纯前端的网页应用和手机 App 要通过自己的后端接入,不能把密钥放进浏览器或安装包;
- 授权码方式必须用 PKCE,而且只接受
S256。授权码 300 秒内有效,只能用一次; - 不支持 OIDC:没有
id_token,没有自动发现地址(discovery)和公钥地址(JWKS)。用户信息从专门的用户信息接口读取; - 不支持密码授权(
password)和隐式授权(implicit); - 授权范围只有
user.read,用户信息只有用户 ID、用户名、姓名、头像和界面语言,不含手机号、邮箱、部门和角色; - 按 IP 限流:授权页每个 IP 每分钟 120 次,令牌接口 600 次,校验和撤销接口各 1200 次,超出返回 429。计数存在服务进程的内存里,目前按单实例设计,多实例部署时每个实例各算各的,见部署 · 单实例说明。
开发指南
- OAuth2 接入指南:给第三方开发者的完整流程(curl 示例)、接口、错误码和接入清单
- 部署 · OAuth2 与单点登录:反向代理和
TRUST_PROXY的要求
相关功能:系统管理 · 客户端管理、登录与账号 · 在线用户与强制下线、安全基线。