摸鱼派第三方登录(OAuth / OpenID)接入说明【新】

摸鱼帮大当家••1254 浏览•7 评论

摸鱼派第三方登录(OAuth / OpenID)接入说明

版本:2026-07-06

1. 能力概览

摸鱼派提供一套兼容 OpenID 2.0 登录流程的第三方授权能力。第三方站点可以引导用户跳转到摸鱼派完成登录与授权,随后在服务端校验回跳参数,并使用授权得到的访问令牌读取用户资源。

当前支持:

  • 使用摸鱼派账号登录第三方站点
  • 按授权范围读取用户基础信息、用户详细资料、积分记录、公开发帖记录、VIP 信息
  • 使用 Authorization: Bearer <access_token> 调用 OAuth 用户资源接口
  • 使用 refresh_token 无感续签 access_token

生产环境域名为 https://fishpi.cn。本文示例中的 https://example.com 请替换为第三方站点自己的服务端地址。

2. 接入流程

  1. 第三方站点生成 /openid/login 授权地址,并让用户浏览器跳转到摸鱼派。
  2. 用户在摸鱼派确认授权范围。
  3. 摸鱼派回跳到第三方站点的 openid.return_to。
  4. 第三方站点服务端调用 /openid/verify 校验回跳参数。
  5. 校验通过后获得 access_token、refresh_token 与最终授权范围。
  6. 第三方站点使用 access_token 调用用户资源接口。
  7. access_token 过期后,使用 /openid/token 续签。

3. 授权范围

第三方站点在 /openid/login 中通过 fishpi.scope 声明需要的权限。多个权限可使用空格或英文逗号分隔。

Scope 说明
profile.read 读取用户基础信息
profile.detail.read 读取用户详细资料
points.read 读取用户积分余额与积分流水
articles.read 读取用户公开发帖记录
membership.read 读取用户 VIP 信息

说明:

  • 不传 fishpi.scope 时,默认按 profile.read 处理。
  • 第三方站点请求的权限会在授权页默认勾选,且不可取消。
  • 用户可以额外勾选站点未请求的权限。
  • 最终授权范围缺少站点请求的权限时,本次授权失败。
  • 用户资源接口只接受 Authorization: Bearer <access_token>,不接受 apiKey。

4. 发起登录

GET /openid/login

第三方站点构造授权地址,并让用户浏览器跳转到该地址。

请求:

Key 必填 说明 示例
openid.ns 是 固定为 OpenID 2.0 协议地址 http://specs.openid.net/auth/2.0
openid.mode 是 固定为checkid_setup checkid_setup
openid.return_to 是 授权完成后的回调地址 https://example.com/callback
openid.realm 是 信任域,必须是openid.return_to 的前缀 https://example.com
openid.identity 是 固定为 OpenID identifier_select http://specs.openid.net/auth/2.0/identifier_select
openid.claimed_id 是 同openid.identity http://specs.openid.net/auth/2.0/identifier_select
fishpi.scope 否 授权范围,空格或逗号分隔 profile.read profile.detail.read membership.read

请求示例:

https://fishpi.cn/openid/login?openid.ns=http%3A%2F%2Fspecs.openid.net%2Fauth%2F2.0&openid.mode=checkid_setup&openid.return_to=https%3A%2F%2Fexample.com%2Fcallback&openid.realm=https%3A%2F%2Fexample.com&openid.claimed_id=http%3A%2F%2Fspecs.openid.net%2Fauth%2F2.0%2Fidentifier_select&openid.identity=http%3A%2F%2Fspecs.openid.net%2Fauth%2F2.0%2Fidentifier_select&fishpi.scope=profile.read+profile.detail.read+membership.read

URL 参数需要按规范编码。生产环境回调地址建议使用 HTTPS,本地开发可使用 localhost 或 127.0.0.1。

5. 接收授权回跳

用户确认授权后,摸鱼派会重定向到 openid.return_to,并在 URL 中携带 OpenID 参数。

常见回跳参数:

Key 说明
openid.ns 协议声明
openid.mode 固定为id_res
openid.op_endpoint 摸鱼派 OpenID 服务入口
openid.claimed_id 用户唯一标识 URL
openid.identity 用户唯一标识 URL
openid.return_to 回调地址
openid.response_nonce 一次性随机串
openid.assoc_handle 关联信息
openid.signed 参与签名的字段列表
openid.sig 签名值

openid.claimed_id / openid.identity 末尾通常包含用户 oId,例如:

https://fishpi.cn/openid/id/1659430635383

回跳参数不能直接作为可信登录结果,必须先调用 /openid/verify 校验。

6. 校验回跳参数

POST /openid/verify

第三方站点服务端必须调用该接口校验回跳参数真实性。

请求:

Key 说明
Content-Type application/json
请求体 回跳收到的openid.* 参数
openid.mode 调用校验时改为check_authentication

请求示例:

curl --location --request POST 'https://fishpi.cn/openid/verify' \
--header 'Content-Type: application/json' \
--data-raw '{
  "openid.ns": "http://specs.openid.net/auth/2.0",
  "openid.mode": "check_authentication",
  "openid.op_endpoint": "https://fishpi.cn/openid",
  "openid.claimed_id": "https://fishpi.cn/openid/id/1659430635383",
  "openid.identity": "https://fishpi.cn/openid/id/1659430635383",
  "openid.return_to": "https://example.com/callback",
  "openid.response_nonce": "2026-07-06T00:00:00Zxxxxxxxxxx",
  "openid.assoc_handle": "1234567890",
  "openid.signed": "op_endpoint,claimed_id,identity,return_to,response_nonce,assoc_handle",
  "openid.sig": "xxx"
}'

成功响应为纯文本:

ns:http://specs.openid.net/auth/2.0
is_valid:true
scope:profile.read profile.detail.read membership.read
token_type:Bearer
access_token:xxx
expires_in:604800
refresh_token:xxx
refresh_expires_in:31104000

失败响应为纯文本:

ns:http://specs.openid.net/auth/2.0
is_valid:false

响应字段:

Key 说明 示例
is_valid 校验结果 true
scope 用户最终授权范围 profile.read profile.detail.read membership.read
token_type 令牌类型 Bearer
access_token OAuth 用户资源访问令牌 xxx
expires_in access_token 有效期,单位秒 604800
refresh_token OAuth 续签令牌 xxx
refresh_expires_in refresh_token 空闲有效期,单位秒 31104000

/openid/verify 只用于校验登录回跳参数。续签令牌请使用 /openid/token。

7. 续签访问令牌

POST /openid/token

使用 refresh_token 续签 OAuth 访问令牌。不需要登录态,不接受 apiKey。

请求:

Key 说明 示例
grant_type 固定为refresh_token refresh_token
refresh_token OAuth 续签令牌 xxx

请求示例:

curl --location --request POST 'https://fishpi.cn/openid/token' \
--header 'Content-Type: application/json' \
--data-raw '{
  "grant_type": "refresh_token",
  "refresh_token": "xxx"
}'

成功响应:

{
  "code": 0,
  "msg": "",
  "data": {
    "token_type": "Bearer",
    "access_token": "xxx",
    "expires_in": 604800,
    "refresh_token": "xxx",
    "refresh_expires_in": 31104000,
    "scope": "profile.read profile.detail.read membership.read"
  }
}

响应字段:

Key 说明 示例
code 0 为成功,-1 为失败 0
msg 错误消息
data.token_type 令牌类型 Bearer
data.access_token 新访问令牌 xxx
data.expires_in 访问令牌有效期,单位秒 604800
data.refresh_token 新续签令牌 xxx
data.refresh_expires_in 续签令牌空闲有效期,单位秒 31104000
data.scope 原授权范围 profile.read profile.detail.read membership.read

续签规则:

  • access_token 默认有效期为 7 天。
  • refresh_token 默认空闲有效期为 360 天。
  • refresh_token 最长不超过首次授权后 720 天。
  • 每次续签都会返回新的 refresh_token,旧 refresh_token 立即失效。
  • 已轮换的旧 refresh_token 再次使用时,同一令牌族下仍有效的令牌会被撤销。

请把 refresh_token 保存在服务端或可信客户端,不要暴露到前端页面、URL、日志、埋点或报错信息中。

8. 查询用户基础信息

GET /openid/user/profile

查询当前 OAuth 授权用户的基础信息。需要 profile.read 权限。

请求:

Key 说明 示例
Authorization Bearer 访问令牌 Bearer xxx

请求示例:

curl --location --request GET 'https://fishpi.cn/openid/user/profile' \
--header 'Authorization: Bearer xxx'

成功响应:

{
  "code": 0,
  "msg": "",
  "data": {
    "userId": "1659430635383",
    "userName": "admin",
    "userNickname": "管理员",
    "userAvatarURL": "https://file.fishpi.cn/a.png"
  }
}

响应字段:

Key 说明 示例
code 0 为成功,-1 为失败 0
msg 错误消息
data.userId 用户oId 1659430635383
data.userName 用户名 admin
data.userNickname 昵称 管理员
data.userAvatarURL 头像 https://file.fishpi.cn/a.png

9. 查询用户详细资料

GET /openid/user/detail

查询当前 OAuth 授权用户的详细资料。需要 profile.detail.read 权限。

请求:

Key 说明 示例
Authorization Bearer 访问令牌 Bearer xxx

请求示例:

curl --location --request GET 'https://fishpi.cn/openid/user/detail' \
--header 'Authorization: Bearer xxx'

成功响应:

{
  "code": 0,
  "msg": "",
  "data": {
    "userName": "admin",
    "userOnlineFlag": true,
    "onlineMinute": 1000,
    "userURL": "https://example.com",
    "userNickname": "管理员",
    "userCity": "北京",
    "userAvatarURL": "https://file.fishpi.cn/a.png",
    "userPoint": 183939,
    "userIntro": "摸鱼中",
    "oId": "1659430635383",
    "userNo": "1",
    "userAppRole": "0",
    "sysMetal": {
      "list": []
    },
    "followerCount": 10,
    "followingUserCount": 5,
    "userRole": "普通用户",
    "cardBg": ""
  }
}

响应字段:

Key 说明 示例
code 0 为成功,-1 为失败 0
msg 错误消息
data.userName 用户名 admin
data.userOnlineFlag 是否在线 true
data.onlineMinute 在线分钟数 1000
data.userURL 个人链接 https://example.com
data.userNickname 昵称 管理员
data.userCity 公开城市 北京
data.userAvatarURL 头像 https://file.fishpi.cn/a.png
data.userPoint 当前积分 183939
data.userIntro 简介 摸鱼中
data.oId 用户oId 1659430635383
data.userNo 用户编号 1
data.userAppRole 站内身份 0
data.sysMetal 展示勋章 {"list":[]}
data.followerCount 粉丝数 10
data.followingUserCount 关注数 5
data.userRole 角色名 普通用户
data.cardBg 卡片背景

本接口只返回当前 Bearer token 所属用户,不支持传 userId 代查。不会返回邮箱、手机号、QQ、登录 IP、密码、2FA 密钥、token/key 或完整用户对象。userCity 仅在用户公开地理位置时返回。

10. 查询用户 VIP 信息

GET /openid/user/membership

查询当前 OAuth 授权用户的 VIP 状态。需要 membership.read 权限。

请求:

Key 说明 示例
Authorization Bearer 访问令牌 Bearer xxx

请求示例:

curl --location --request GET 'https://fishpi.cn/openid/user/membership' \
--header 'Authorization: Bearer xxx'

成功响应:

{
  "code": 0,
  "msg": "",
  "data": {
    "active": true,
    "state": 1,
    "lvCode": "VIP1",
    "expiresAt": 1760000000000,
    "configJson": "{}"
  }
}

响应字段:

Key 说明 示例
code 0 为成功,-1 为失败 0
msg 错误消息
data.active VIP 是否有效 true
data.state 会员状态 1
data.lvCode VIP 等级编码 VIP1
data.expiresAt 到期时间,毫秒;0 为永久 1760000000000
data.configJson 用户 VIP 配置 {}

非 VIP 或已过期响应:

{
  "code": 0,
  "msg": "",
  "data": {
    "active": false,
    "state": 0,
    "lvCode": "",
    "expiresAt": 0,
    "configJson": ""
  }
}

本接口只返回当前 Bearer token 所属用户,不支持传 userId 代查。不会返回价格、支付、退款或后台维护记录。

11. 查询用户积分记录

GET /openid/user/points

分页查询当前 OAuth 授权用户的积分余额和积分记录。需要 points.read 权限。

请求:

Key 说明 示例
Authorization Bearer 访问令牌 Bearer xxx
p 页码,默认1 1
size 每页数量,默认20,最大 200 20

请求示例:

curl --location --request GET 'https://fishpi.cn/openid/user/points?p=1&size=20' \
--header 'Authorization: Bearer xxx'

成功响应:

{
  "code": 0,
  "msg": "",
  "data": {
    "userId": "1659430635383",
    "userPoint": 183939,
    "records": [
      {
        "oId": "1760000000000",
        "fromId": "sys",
        "toId": "1659430635383",
        "sum": 20,
        "type": 8,
        "time": 1760000000000,
        "dataId": "1760000000000",
        "memo": "hello",
        "operation": "+",
        "balance": 183939,
        "displayType": "活动收益",
        "description": "签到奖励"
      }
    ],
    "pagination": {
      "paginationCurrentPageNum": 1,
      "paginationPageSize": 20,
      "paginationRecordCount": 100,
      "paginationPageCount": 5,
      "paginationPageNums": [1, 2, 3, 4, 5]
    }
  }
}

响应字段:

Key 说明 示例
code 0 为成功,-1 为失败 0
msg 错误消息
data.userId 用户oId 1659430635383
data.userPoint 当前积分 183939
data.records 积分记录列表
data.records[].oId 记录oId 1760000000000
data.records[].fromId 支出用户oId sys
data.records[].toId 收入用户oId 1659430635383
data.records[].sum 积分数量 20
data.records[].type 记录类型 8
data.records[].time 记录时间 1760000000000
data.records[].dataId 关联数据 1760000000000
data.records[].memo 备注 hello
data.records[].operation 收支方向 +
data.records[].balance 操作后余额 183939
data.records[].displayType 类型名称 活动收益
data.records[].description 记录描述 签到奖励
data.pagination.paginationCurrentPageNum 当前页 1
data.pagination.paginationPageSize 每页数量 20
data.pagination.paginationRecordCount 总记录数 100
data.pagination.paginationPageCount 总页数 5
data.pagination.paginationPageNums 页码列表 [1,2,3,4,5]

description 为已格式化展示文本,客户端可直接展示。

12. 查询用户发帖记录

GET /openid/user/articles

分页查询当前 OAuth 授权用户的公开发帖记录。需要 articles.read 权限。

请求:

Key 说明 示例
Authorization Bearer 访问令牌 Bearer xxx
p 页码,默认1 1
size 每页数量,默认20,最大 100 20

请求示例:

curl --location --request GET 'https://fishpi.cn/openid/user/articles?p=1&size=20' \
--header 'Authorization: Bearer xxx'

成功响应:

{
  "code": 0,
  "msg": "",
  "data": {
    "userId": "1659430635383",
    "articles": [
      {
        "oId": "1760000000000",
        "articleTitle": "摸鱼",
        "articlePermalink": "/article/1760000000000",
        "articleTags": "摸鱼,生活",
        "articleCreateTime": 1760000000000,
        "articleUpdateTime": 1760000000000,
        "articleCommentCount": 3,
        "articleViewCount": 100,
        "articleType": 0,
        "articlePerfect": 0
      }
    ],
    "pagination": {
      "paginationCurrentPageNum": 1,
      "paginationPageSize": 20,
      "paginationRecordCount": 100,
      "paginationPageCount": 5,
      "paginationPageNums": [1, 2, 3, 4, 5]
    }
  }
}

响应字段:

Key 说明 示例
code 0 为成功,-1 为失败 0
msg 错误消息
data.userId 用户oId 1659430635383
data.articles 发帖记录列表
data.articles[].oId 帖子oId 1760000000000
data.articles[].articleTitle 标题 摸鱼
data.articles[].articlePermalink 链接 /article/1760000000000
data.articles[].articleTags 标签 摸鱼,生活
data.articles[].articleCreateTime 创建时间 1760000000000
data.articles[].articleUpdateTime 更新时间 1760000000000
data.articles[].articleCommentCount 回帖数 3
data.articles[].articleViewCount 浏览数 100
data.articles[].articleType 帖子类型 0
data.articles[].articlePerfect 是否优选 0
data.pagination.paginationCurrentPageNum 当前页 1
data.pagination.paginationPageSize 每页数量 20
data.pagination.paginationRecordCount 总记录数 100
data.pagination.paginationPageCount 总页数 5
data.pagination.paginationPageNums 页码列表 [1,2,3,4,5]

发帖记录只返回公开、非匿名、未删除发帖,不包含正文、草稿、IP、UA。

13. 权限与错误处理

资源接口统一使用 Bearer 访问令牌:

Authorization: Bearer <access_token>

常见失败场景:

场景 结果
缺少Authorization 无权限
access_token 伪造或过期 无权限
access_token 缺少接口所需 scope 无权限
使用refresh_token 调资源接口 无权限
/openid/token 使用错误 grant_type 参数错误
重复使用已轮换的refresh_token 无权限,同一令牌族撤销

接口失败响应通常为 JSON:

{
  "code": -1,
  "msg": "无权限"
}

/openid/verify 保持 OpenID 兼容响应,失败时返回纯文本 is_valid:false。

14. 安全建议

  • access_token 只用于调用 OAuth 用户资源接口。
  • refresh_token 只用于 /openid/token 续签。
  • 服务端应保存最终授权 scope,调用资源接口前先判断权限。
  • 不要把令牌写入前端源码、URL、日志、错误堆栈或第三方统计。
  • 回调参数必须由服务端调用 /openid/verify 校验后再建立登录态。
  • 第三方站点只应请求业务必需的 scope。

💬 全部回帖 (7)
荆芥芥芥芥芥芥

收到

章鱼哥

太强辣!

小弘饱饱

感谢使用手册

奶茶加辣(长大版)

tql辣

Cloud

太强辣!

LIYA

技术贴!

小A

😋学习学习

参与讨论

随机发现

随机发现尚未读取。