Skip to content

OPC · SSO

统一登录接入(SSO)

让访客用沉思波账号登录你的网站——授权码模式,你的服务端拿授权码换令牌、再取身份,浏览器全程拿不到 secret。

SSO计费

前提

已在控制台「开放能力」建好应用,手里有 site_key(公开)与 secret(仅服务端);建应用时登记了回跳地址(allowed_origins 或站点地址,写进应用的允许来源)。

有后端才能走完整链路(code 换令牌必须在服务端做)。纯前端站见下文「没有后端怎么办」。

授权码时序 ​

text
用户浏览器          你的前端            你的服务端           沉思波
    |  点「用沉思波登录」 |                    |                  |
    |------------------>|  跳转授权地址        |                  |
    |----------------------------------------|----------------->|
    |            (未登录则先到沉思波登录页,登录后回跳授权地址)      |
    |<----------------------------------------------------------|
    |   302 回你的 redirect_uri?code=...&state=...                |
    |------------------>|  把 code 交给服务端  |                  |
    |                   |------------------->|  POST /sso/token   |
    |                   |                    |------------------>|
    |                   |                    |<------------------|
    |                   |                    |  access_token+user |
    |                   |<-------------------|  (写入会话/下发)   |
    |<------------------|  进入已登录态        |                  |
三个关键点

授权码(code)一次性、约 90 秒过期,只能在你的服务端用一次,用完作废。

state 原样回显:你发起授权时带一串随机 state,回跳时校验一致,防跨站请求伪造(CSRF)。

redirect_uri 登记制:回跳地址必须与应用登记的允许来源精确同源(登记了完整回调地址则精确相等,登记了域名则该域下任意路径均可),没登记的地址直接拒。

接口契约 ​

开放接口前缀以你部署的 community-api 为准(官网同源 /api/open/...)。路径与参数与服务端实现对齐,变更以控制台公告为准。

1. 发起授权(浏览器跳转) ​

http
GET /api/open/sso/authorize
    ?client_id={site_key}
    &redirect_uri={你登记的回跳地址}
    &state={随机串}
    &scope={可选}
  • 未登录:302 到沉思波登录页(带回跳);已登录:302 到 redirect_uri,带上 code 与 state;
  • 浏览器 fetch 场景可加 ?json=1(或 Accept: application/json),未登录回 401 + loginUrl,成功回 JSON:
json
{
  "redirect": "https://your.example/callback?code=opc_code_...&state=abc",
  "code": "opc_code_...",
  "state": "abc",
  "expires_in": 90
}

2. 授权码换令牌(仅服务端) ​

http
POST /api/open/sso/token
Content-Type: application/json

{
  "code": "opc_code_...",
  "client_id": "{site_key}",
  "client_secret": "{secret,只在这里出现}",
  "redirect_uri": "https://your.example/callback"
}

成功响应:

json
{
  "access_token": "opc_at_...",
  "token_type": "Bearer",
  "expires_in": 7200,
  "scope": "",
  "user": { "id": 12, "nickname": "示例用户", "avatar": null }
}

失败回 401,文案统一为「授权码无效或已过期」(不区分是密钥错还是码错——不给试探留缝)。

3. 取用户信息 ​

http
GET /api/open/sso/userinfo
Authorization: Bearer {access_token}
json
{
  "id": 12,
  "nickname": "示例用户",
  "avatar": null,
  "email_masked": "ab***@cd.com"
}

邮箱一律脱敏,不回明文。

4. 吊销令牌(可选) ​

http
POST /api/open/sso/revoke
Content-Type: application/json

{ "access_token": "opc_at_..." }

回 { "ok": true }——成功失败都只回这一句,不区分令牌是否存在。

服务端示例(Node) ​

js
// 仅示意:secret 从环境变量读,不写进代码仓库
async function exchangeCode(code, redirectUri) {
  const r = await fetch('https://your-api-host/api/open/sso/token', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({
      code,
      client_id: process.env.OPC_SITE_KEY,
      client_secret: process.env.OPC_SECRET,
      redirect_uri: redirectUri
    })
  })
  if (!r.ok) throw new Error('授权码兑换失败')
  return r.json() // { access_token, user: { id, nickname, avatar } }
}

两种接法 ​

有后端(推荐)

完整链路:前端只把用户导向授权地址(带 client_id / redirect_uri / state)。code 回跳到你的回调接口,服务端兑换令牌、建自己的会话,之后怎么管登录态是你的事。

纯前端(没有后端)

secret 不能进浏览器,没法安全地走 code 换令牌。两条务实路线:

1)只做「跳过去登录」:跳授权地址,用户在沉思波侧登录后回跳你的静态页——适合「引导注册 / 领取」这类不要求站内登录态的场景;

2)登录交给托管侧或后端即服务:把兑换动作放在任意一处你信任的服务端(云函数、小后端),前端只收「已登录」的结果。

要的是「站内有登录态」,就一定得有能藏住 secret 的服务端。

常见坑 ​

  • redirect_uri 没登记:回 400「redirect_uri 未在应用登记范围内」——建应用时就把回调地址写进允许来源,改地址先去后台改登记;
  • state 不校验:等于把登录接口开给跨站伪造;state 用随机串、绑定会话、回跳时比对;
  • code 重复使用:code 一次一用,第二次兑换必失败;重试要走完整授权,不要重放旧 code;
  • token 过期不续:access_token 默认约 2 小时(以响应 expires_in 为准),过期就重新走授权码流,别当永久票据;
  • 把 secret 写进前端或命令行历史:见下文安全边界。

安全边界 ​

红线

secret 只在服务端:不进前端仓库、不进页面、不进 URL、不进日志;只在 code 换令牌时出现在请求体里。

泄露就轮换:secret 一旦可能外泄,到后台 · 密钥与轮换轮换,旧值立即作废。

令牌当凭证保管:access_token 等同登录态,只走 HTTPS,只存在你的服务端或 HttpOnly Cookie 里。

我们不代管你的业务数据:SSO 只给身份字段(标识、昵称、脱敏邮箱一类),你的用户资料、订单、内容归你自己管。隐私口径见隐私政策。

下一步 ​

本页讨论

0 条评论

还没有评论,来说第一句。

Ctrl + Enter 发布