Files
mengyastore/API_DOCS.md

16 KiB
Raw Permalink Blame History

萌芽账户认证中心 API 文档

访问 GET /GET /api(无鉴权)可得到 JSON 格式的简要说明(服务名、版本、/api/docs/api/health 入口、路由前缀摘要)。

接入地址:

  • 统一登录前端:https://auth.shumengya.top
  • 后端 APIhttps://auth.api.shumengya.top
  • 本地开发 APIhttp://<host>:8080

对外接入建议:

  1. 第三方应用按钮跳转到统一登录前端。
  2. 登录成功后回跳到业务站点。
  3. 业务站点使用回跳带回的 token 调用后端 API。

示例按钮:

<a href="https://auth.shumengya.top/?redirect_uri=https%3A%2F%2Fapp.example.com%2Fauth%2Fcallback&state=abc123">
  使用萌芽统一账户认证登录
</a>

回跳说明:

  • 用户已登录时,统一登录前端会提示“继续授权”或“切换账号”。
  • 登录成功后会回跳到 redirect_uri(或 return_url),并在 URL #fragment(哈希)中带上令牌与用户信息(见下表)。
  • 第三方应用拿到 token 后,建议调用 POST /api/auth/verify(无副作用、适合网关鉴权)或 GET /api/auth/me(会更新访问记录,适合业务拉全量资料)校验并解析用户身份。

统一登录前端:查询参数

参数 必填 说明
redirect_uri return_url 至少其一 登录成功后的回跳地址,须进行 URL 编码;可为绝对 URL 或相对路径(相对路径相对统一登录站点解析)。
return_url 同上 redirect_uri 同义,二者都传时优先 redirect_uri
state OAuth 风格透传字符串;回跳时原样写入哈希参数,供业务防 CSRF 或关联会话。
prompt 预留;前端可读,当前可用于将来扩展交互策略。
client_id 第三方应用稳定标识(字母数字开头,可含 _.:-,最长 64。写入用户「应用接入记录」并随登录请求提交给后端。
client_name 展示用名称(最长 128client_id 配对;可选。

回跳 URL# 哈希参数

成功授权后,前端将使用 URLSearchParams 写入哈希,例如:https://app.example.com/auth/callback#token=...&expiresAt=...&account=...&username=...&state=...

参数 说明
token JWT调用受保护接口时放在请求头 Authorization: Bearer <token>
expiresAt 过期时间RFC3339与签发侧一致当前默认为登录时起算 7 天)。
account 账户名(与 JWT sub 一致)。
username 展示用昵称,可能为空。
state 若登录请求携带了 state,则原样返回。

业务站点回调页应用脚本读取 location.hash,解析后仅在 HTTPS 环境token 存于内存或安全存储,并尽快用后端 POST /api/auth/verify 校验(勿仅信任哈希中的明文字段)。

第三方后端接入建议

  1. 仅信服务端:回调页将 token 交给自有后端,由后端请求 POST https://<api-host>/api/auth/verifyJSON body{"token":"..."}),根据 validuser.account 建立会话。
  2. CORS:浏览器直连 API 时须后端已配置 CORS本服务默认允许任意 Origin);若从服务端发起请求则不受 CORS 限制。
  3. 令牌过期verify / me 返回 401 或 verifyvalid:false 时,应引导用户重新走统一登录。

认证与统一登录

登录获取统一令牌

POST /api/auth/login

请求:

{
  "account": "demo",
  "password": "demo123",
  "clientId": "my-app",
  "clientName": "我的应用"
}

clientId / clientName 可选;规则与请求头 X-Auth-Client / X-Auth-Client-Name 一致。传入且格式合法时,会在登录成功后写入该用户的 应用接入记录(见下文 authClients)。

响应:

{
  "token": "jwt-token",
  "expiresAt": "2026-03-14T12:00:00Z",
  "user": {
    "account": "demo",
    "username": "示例用户",
    "email": "demo@example.com",
    "level": 0,
    "sproutCoins": 10,
    "secondaryEmails": ["demo2@example.com"],
    "phone": "13800000000",
    "avatarUrl": "https://example.com/avatar.png",
    "websiteUrl": "https://example.com",
    "bio": "### 简介",
    "createdAt": "2026-03-14T12:00:00Z",
    "updatedAt": "2026-03-14T12:00:00Z"
  }
}

若账户已被管理员封禁,返回 403,且不会签发 JWT,响应示例:

{
  "error": "account is banned",
  "banReason": "违规内容"
}

banReason 可能为空字符串或省略。

常见 HTTP 状态码(登录)

状态码 含义
200 成功,返回 tokenexpiresAtuser
400 请求体非法或缺少 account / password
401 账户不存在或密码错误(统一文案 invalid credentials)。
403 账户已封禁(见上文 JSON
500 服务器内部错误(读库、签发 JWT 失败等)。

JWT 概要:算法 HS256;载荷含 account(与 sub 一致)、iss(见 data/config/auth.json)、iat / exp。客户端只需透传字符串,勿在前端解析密钥

校验令牌

POST /api/auth/verify

请求:

{
  "token": "jwt-token"
}

响应:

{
  "valid": true,
  "user": { "account": "demo", "...": "..." }
}

若账户已封禁,返回 200validfalse(不返回 user 对象),示例:

{
  "valid": false,
  "error": "account is banned",
  "banReason": "违规内容"
}

令牌过期、签名错误、issuer 不匹配等解析失败时返回 401,示例:{"valid": false, "error": "invalid token"}

verifyme 的取舍:仅校验身份、不改变用户数据时用 verify;需要最新资料、签到状态或写入「最后访问」时用 GET /api/auth/me(需 Bearer

应用接入记录(可选):第三方在 POST /api/auth/verifyGET /api/auth/me 上携带请求头:

  • X-Auth-Client:应用 ID格式同登录 JSON 的 clientId
  • X-Auth-Client-Name:可选展示名

校验成功且用户未封禁时,服务端会更新该用户 JSON 中的 authClients 数组(clientIddisplayNamefirstSeenAtlastSeenAt)。POST /api/auth/verify 的响应体 user 仍为 Public(),不含 authClients,避免向调用方泄露用户在其他应用的接入情况;GET /api/auth/me 与管理员列表中的 userOwnerPublic包含 authClients,用户可在统一登录前端的个人中心查看。

获取当前用户信息

GET /api/auth/me

请求头: Authorization: Bearer <jwt-token>

可选(由前端调用 https://cf-ip-geo.smyhub.com/api 等接口解析后传入,用于记录「最后访问 IP」与「最后显示位置」

  • X-Visit-Ip:客户端公网 IP与地理接口返回的 ip 一致即可)
  • X-Visit-Location:展示用位置文案(例如将 geo.countryNameregionNamecityName 拼接为 中国 四川 成都

服务端回退(避免浏览器跨域导致头缺失):若未传 X-Visit-Location,后端会用 X-Visit-Ip;若也未传 X-Visit-Ip,则用连接的 ClientIP()(请在前置反向代理上正确传递 X-Forwarded-For 等,并在生产环境为 Gin 配置可信代理)。随后服务端请求 GEO_LOOKUP_URL(默认 https://cf-ip-geo.smyhub.com/api?ip=<ip>)解析展示位置并写入用户记录。

响应:

{
  "user": { "account": "demo", "...": "..." },
  "checkIn": {
    "rewardCoins": 1,
    "checkedInToday": false,
    "lastCheckInDate": "",
    "lastCheckInAt": "",
    "today": "2026-03-14"
  }
}

user 还会包含 lastVisitAtlastVisitDatecheckInDayscheckInStreakvisitDaysvisitStreak 等统计字段。

在登录用户本人、管理员列表等场景下,user 还可包含 lastVisitIplastVisitDisplayLocation(最近一次通过 /api/auth/me 上报的访问 IP 与位置文案)。公开用户资料接口 GET /api/public/users/:accountPOST /api/auth/verifyuser 中不包含这两项(避免公开展示或第三方校验时令牌响应携带访问隐私)。

说明:密码不会返回。

若账户在登录后被封禁,持旧 JWT 调用 GET /api/auth/mePUT /api/auth/profilePOST /api/auth/check-in、辅助邮箱等需登录接口时,返回 403,正文同登录封禁响应(error + 可选 banReason)。客户端应作废本地令牌。

每日签到

POST /api/auth/check-in

请求头: Authorization: Bearer <jwt-token>

响应:

{
  "checkedIn": true,
  "alreadyCheckedIn": false,
  "rewardCoins": 1,
  "awardedCoins": 1,
  "message": "签到成功",
  "user": { "account": "demo", "...": "..." }
}

更新当前用户资料

PUT /api/auth/profile

请求头: Authorization: Bearer <jwt-token>

请求(字段可选):

{
  "password": "newpass",
  "username": "新昵称",
  "phone": "13800000000",
  "avatarUrl": "https://example.com/avatar.png",
  "websiteUrl": "https://example.com",
  "bio": "### 新简介"
}

说明:websiteUrl 须为 http/https 地址;可传空字符串清除;未写协议时服务端会补全为 https://

响应:

{
  "user": { "account": "demo", "...": "..." }
}

用户广场

获取用户公开主页

GET /api/public/users/{account}

说明:

  • 仅支持账户名 account,不支持昵称查询。
  • 适合第三方应用展示用户公开资料。
  • 若该账户已被封禁,返回 404 {"error":"user not found"}(与不存在账户相同,避免公开资料泄露)。
  • 响应中含该用户最近一次被服务端记录的访问 IPlastVisitIp)与展示用地理位置(lastVisitDisplayLocation,与本人中心一致);POST /api/auth/verify 返回的用户 JSON 不含上述两项。

响应:

{
  "user": {
    "account": "demo",
    "username": "示例用户",
    "level": 3,
    "sproutCoins": 10,
    "avatarUrl": "https://example.com/avatar.png",
    "websiteUrl": "https://example.com",
    "lastVisitIp": "203.0.113.1",
    "lastVisitDisplayLocation": "中国 广东省 深圳市",
    "bio": "### 简介"
  }
}

公开注册策略

GET /api/public/registration-policy

无需鉴权。用于前端判断是否展示「邀请码」输入框。

响应:

{
  "requireInviteCode": false
}

requireInviteCodetrue 时,POST /api/auth/register 必须携带有效 inviteCode(见下节)。

注册账号(发送邮箱验证码)

POST /api/auth/register

请求:

{
  "account": "demo",
  "password": "demo123",
  "username": "示例用户",
  "email": "demo@example.com",
  "inviteCode": "ABCD1234"
}
  • inviteCode:可选。若服务端开启「强制邀请码」,则必填且须为管理员发放的未过期、未用尽邀请码。邀请码不区分大小写;成功完成 verify-email 创建用户后才会扣减使用次数。

响应:

{
  "sent": true,
  "expiresAt": "2026-03-14T12:10:00Z"
}

验证邮箱并完成注册

POST /api/auth/verify-email

请求:

{
  "account": "demo",
  "code": "123456"
}

响应:

{
  "created": true,
  "user": { "account": "demo", "...": "..." }
}

忘记密码(发送重置验证码)

POST /api/auth/forgot-password

请求:

{
  "account": "demo",
  "email": "demo@example.com"
}

响应:

{
  "sent": true,
  "expiresAt": "2026-03-14T12:10:00Z"
}

重置密码

POST /api/auth/reset-password

请求:

{
  "account": "demo",
  "code": "123456",
  "newPassword": "newpass"
}

响应:

{ "reset": true }

申请添加辅助邮箱(发送验证码)

POST /api/auth/secondary-email/request

请求头: Authorization: Bearer <jwt-token>

请求:

{
  "email": "demo2@example.com"
}

响应:

{
  "sent": true,
  "expiresAt": "2026-03-14T12:10:00Z"
}

验证辅助邮箱

POST /api/auth/secondary-email/verify

请求头: Authorization: Bearer <jwt-token>

请求:

{
  "email": "demo2@example.com",
  "code": "123456"
}

响应:

{
  "verified": true,
  "user": { "account": "demo", "...": "..." }
}

管理端接口(需要管理员 Token

管理员 Token 存放在 data/config/admin.json 中;如果文件不存在,后端启动时会自动生成并写入该文件。 请求时可使用以下任一方式携带:

  • Query?token=<admin-token>
  • HeaderX-Admin-Token: <admin-token>

签到奖励设置

GET /api/admin/check-in/config

PUT /api/admin/check-in/config

请求:

{
  "rewardCoins": 1
}
  • HeaderAuthorization: Bearer <admin-token>

注册策略与邀请码

GET /api/admin/registration

响应含 requireInviteCodeinvites 数组(每项含 codenotemaxUsesusesexpiresAtcreatedAt)。maxUses 为 0 表示不限次数。

PUT /api/admin/registration

请求:

{ "requireInviteCode": true }

POST /api/admin/registration/invites

请求:

{
  "note": "内测批次",
  "maxUses": 10,
  "expiresAt": "2026-12-31T15:59:59Z"
}

expiresAt 可省略;须为 RFC3339。响应 201invite 内含服务端生成的 8 位邀请码。

DELETE /api/admin/registration/invites/{code}

删除指定邀请码(code 与存储大小写可能不同,按不区分大小写匹配)。

获取用户列表

GET /api/admin/users

响应:

{
  "total": 1,
  "users": [{ "account": "demo", "...": "..." }]
}

新建用户

POST /api/admin/users

请求:

{
  "account": "demo",
  "password": "demo123",
  "username": "示例用户",
  "email": "demo@example.com",
  "level": 0,
  "sproutCoins": 10,
  "secondaryEmails": ["demo2@example.com"],
  "phone": "13800000000",
  "avatarUrl": "https://example.com/avatar.png",
  "websiteUrl": "https://example.com",
  "bio": "### 简介"
}

更新用户

PUT /api/admin/users/{account}

请求(字段可选):

{
  "password": "newpass",
  "username": "新昵称",
  "level": 1,
  "secondaryEmails": ["demo2@example.com"],
  "sproutCoins": 99,
  "websiteUrl": "https://example.com",
  "banned": true,
  "banReason": "违规说明(最多 500 字)"
}
  • banned:是否封禁;解封时请传 false,并可将 banReason 置为空字符串。
  • banReason:仅当用户处于封禁状态时允许设为非空;封禁时若首次写入会记录 bannedAtRFC3339存于用户 JSON

管理员列表 GET /api/admin/users 中每条 user 可含 bannedbanReason(不含 bannedAt 亦可从存储文件中查看)。

删除用户

DELETE /api/admin/users/{account}

响应:

{ "deleted": true }

数据存储说明

  • 用户数据:data/users/*.json
  • 注册待验证:data/pending/*.json
  • 密码重置记录:data/reset/*.json
  • 辅助邮箱验证:data/secondary/*.json
  • 管理员 Tokendata/config/admin.json
  • JWT 配置:data/config/auth.json
  • 邮件配置:data/config/email.json
  • 注册策略与邀请码:data/config/registration.json

快速联调用示例

# 服务根路径 JSON 说明
curl -s http://localhost:8080/ | jq .

# 登录
curl -X POST http://localhost:8080/api/auth/login \
  -H 'Content-Type: application/json' \
  -d '{"account":"demo","password":"demo123"}'

# 校验令牌(推荐第三方网关先调此接口)
curl -X POST http://localhost:8080/api/auth/verify \
  -H 'Content-Type: application/json' \
  -d '{"token":"<jwt-token>"}'

# 使用令牌获取用户信息(会更新访问记录)
curl http://localhost:8080/api/auth/me \
  -H 'Authorization: Bearer <jwt-token>'