用你的圆柚账号登录。创建应用、修改回调、重置密钥都要登录 —— 不是拿 client_secret 当密码。
原因:client_secret 只用于你服务端调用接口。 如果它能改配置,一旦泄露,别人就能把回调地址改到自己域名,之后真实用户的授权码会直接发到攻击者那边。
新建的应用是待审核状态:能配置、能测连通,平台启用后终端用户才能登录。
四步接完。前两步是自查,不用写代码。
client_id 与 client_secret 由系统生成,密钥只显示一次,当场复制走。GET /api/open/app,确认系统地址、回调地址已经登记。POST /api/open/ping,返回 ok: true 才算配好。所有接口用应用身份认证,不涉及用户令牌。请求头两个字段:
X-Client-Id: uy_2fc7fc3c1bf9
X-Client-Secret: <创建应用时给出的密钥>
client_secret 由平台在创建应用时生成,不会通过任何接口回显(你自己也读不出来)。丢了只能找平台重置。
| code | 含义 | 怎么处理 |
|---|---|---|
| 40130 | 缺少 X-Client-Id | 检查请求头是否被网关/框架丢掉 |
| 40131 | 缺少 X-Client-Secret | 同上 |
| 40132 | 未登记的接入方 | client_id 写错,或应用已被平台停用 |
| 40133 | 凭据无效 | secret 不对,找平台重置 |
client_id(uy_ 开头)。
早期接入方配置里写的是可读标识(如 iot),也照样能用——
服务端先按 client_id 找,找不到再按可读标识找,不会因为改造断线。
/oauth2/authorize?client_id=...)。可读 ID 可被枚举、可被仿冒
(钓鱼链接看着像官方的),还会被抢注;改名更会断掉已签发的授权码和审计记录。
GitHub、Google、Auth0、微信都是系统生成。
查我这个应用的配置。
curl -H "X-Client-Id: iot" -H "X-Client-Secret: $SECRET" \
https://rid.rounyou.com/api/open/app
{
"code": "iot",
"name": "闪应IoT管理后台",
"url": "https://sas.sainexa.com",
"ssoMode": "ticket",
"redirectUris": "https://sas.sainexa.com/oauth2/callback/uc",
"status": 1,
"secretConfigured": true
}
secretConfigured 只表示“配没配”,不返回密钥本身。
改我的配置。只提交要改的字段即可。
curl -X PUT -H "X-Client-Id: iot" -H "X-Client-Secret: $SECRET" \
-H "Content-Type: application/json" \
-d '{"redirectUris":"https://sas.sainexa.com/oauth2/callback/uc"}' \
https://rid.rounyou.com/api/open/app
{ "updated": ["redirectUris"] }
updated 告诉你哪些字段真的被写进去了。字段不在白名单里会被忽略,不报错——所以别只看 HTTP 200,要看 updated。
连通性自检。每次改完配置都跑一次。
curl -X POST -H "X-Client-Id: iot" -H "X-Client-Secret: $SECRET" \
https://rid.rounyou.com/api/open/ping
{ "ok": true, "code": "iot", "serverTime": "2026-10-30T13:03:23", "warnings": [] }
ok=false 时 warnings 会说明缺什么:
我这个系统最近的登录尝试。
curl -H "X-Client-Id: iot" -H "X-Client-Secret: $SECRET" \
"https://rid.rounyou.com/api/open/app/logs?limit=30"
{
"total": 2,
"rows": [
{ "time": "2026-10-30T13:11:52", "action": "auth/login", "result": "fail:40101" },
{ "time": "2026-10-30T13:11:36", "action": "auth/login", "result": "fail:40101" }
]
}
result 前缀 success 是成功,fail:<code> 是失败(code 与登录接口返回的一致)。
可改:url、redirectUris、ssoEndpoint、description、ssoMode
| 不可改 | 原因 |
|---|---|
| code | 应用身份,改了整个接入链路断掉 |
| clientSecret | 轮换属高危操作,走平台后台 |
| status | 上下线由平台决定 |
| loginPolicy | 决定安全强度(滑块、二次验证),不交给接入方自行放宽 |
ssoMode 只接受 ticket / token / uc_exchange / redirect,写错直接 400 —— 这类字段静默失效比报错更危险。
| status | 含义 | 终端用户能登录 |
|---|---|---|
| 0 | 待审核 —— 新建后的初始状态,能配置、能 ping | ❌ |
| 1 | 启用 —— 审核通过 | ✅ |
| -1 | 停用 —— 平台主动关掉 | ❌ |
待审核期间你能把回调地址、系统地址都配好,POST /ping 的
warnings 会明确写着「应用待审核」。平台在 UC 后台启用后,
ping 返回 ok: true,终端用户即可登录。
流程:提交账号密码 → 若要求验证码,弹出 UC 的滑块 iframe → 过完拿票再提交一次。
第一步,提交账号密码:
POST https://rid.rounyou.com/api/auth/login
Content-Type: application/json
{ "username": "kf_zhang", "password": "••••••", "clientId": "iot" }
两种结果:
// 直接成功
{ "code": 0, "data": { "accessToken": "eyJraWQiOi...", "expiresIn": 7200 } }
// 需要先过滑块
{ "code": 0, "data": { "needCaptcha": true, "preToken": "eyJraWQiOi..." } }
needCaptcha 不代表登录失败,只是要先过验证码。
很多接入方在这里直接报“账号或密码错误”,用户会一直重试——这是最常见的踩坑点。
第二步,弹出验证码组件(照抄即可,高度 252):
<div id="capMask" style="display:none">
<iframe src="https://rid.rounyou.com/widget/captcha"
title="安全验证"
style="width:320px;height:252px;border:0"></iframe>
</div>
第三步,监听组件回传。它只发两种消息:
| action | 含义 |
|---|---|
| ready | 图片已就绪,可以开始拖 |
| done | 拖完了,带 captchaTicket |
window.addEventListener('message', async (e) => {
// 只认来自 UC 的消息
if (e.origin !== 'https://rid.rounyou.com') return;
const d = e.data || {};
if (d.type !== 'uc-captcha' || d.action !== 'done') return;
const r = await fetch('https://rid.rounyou.com/api/auth/login', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
username: form.username,
password: form.password,
clientId: 'iot',
preToken: preToken, // 第一步拿到的
captchaTicket: d.captchaTicket // 组件回传的
})
});
const j = await r.json();
if (j.code === 0 && j.data.accessToken) {
// 登录成功
}
});
不想自己做登录页时用这条。浏览器跳到 UC,登录完带 code 回到你的回调地址。
GET https://rid.rounyou.com/oauth2/authorize
?client_id=iot
&redirect_uri=https://your-system/oauth2/callback/uc
&response_type=code
&state=随意值(自己校验,防 CSRF)
→ 302 回:https://your-system/oauth2/callback/uc?code=xxx&state=随意值
然后用 code 换令牌:
POST https://rid.rounyou.com/oauth2/token
Content-Type: application/x-www-form-urlencoded
grant_type=authorization_code&code=xxx&client_id=iot&redirect_uri=...
回调地址必须先在配置里登记过(redirectUris),否则会被拒绝回跳。
| 现象 | 多半是 | 怎么处置 |
|---|---|---|
| 浏览器报跨域 / 请求被拦 | 你从自定义域名调 UC,但该域名不在白名单 | 找平台把域名加进 CorsConfig |
| 登录一直提示“账号或密码错误” | 其实是 needCaptcha 被当成了失败 |
按「方式 A」接上验证码组件 |
| 滑完没反应 / 弹窗不出现 | iframe 尺寸太小,或没监听 postMessage |
用 320×252,且校验 e.origin 后取 captchaTicket |
| 改了配置但行为没变 | 字段不在白名单里,被静默忽略 | 看 PUT 返回的 updated,再跑一次 ping |
| 授权码回跳被拒 | redirect_uri 和登记的完全不一致(含 http/https、末尾斜杠) |
逐字符比对 GET /app 里的 redirectUris |
登录日志里全是 fail:40101 |
密码错,或账号不存在 | 让用户确认账号;连续失败会被临时锁定 |
fail:<code> 就能直接对照服务端日志,比“登不上”三个字有效得多。