openapi: 3.1.0 info: title: 管理端手机号登录 version: 1.1.0 paths: /api/v1/web/operator/login/session: get: operationId: operatorLoginSession summary: 初始化手机号登录页面 description: HTTPS,同源,不接受查询参数。签发 HttpOnly 登录 Cookie 和 CSRF token,20 分钟后过期,同一 Cookie 重载不会轮换 CSRF。短信不可用时仍可使用密码登录。 security: [] responses: '200': description: 页面状态;sms_enabled=false 不代表发送成功 headers: Set-Cookie: schema: { type: string } content: application/json: schema: type: object required: [ok, data] properties: ok: { type: boolean } data: type: object required: [sms_enabled, password_enabled, csrf_token] properties: sms_enabled: { type: boolean } password_enabled: { type: boolean } signup_enabled: { type: boolean, const: false } csrf_token: { type: string } default: { $ref: '#/components/responses/Failure' } /api/v1/web/operator/login/sms/send: post: operationId: operatorSendLoginSMS summary: 发送管理端登录验证码 description: 6 位随机验证码,5 分钟有效;每个手机号至少间隔 60 秒、每小时最多 5 次,家庭端和设备绑定入口共用此限额。同一请求号重试不重复发送。手机号加密、验证码仅存 keyed hash;不在响应中返回验证码。 security: [{ OperatorLoginCookie: [] }] parameters: - { $ref: '#/components/parameters/RequestID' } - { $ref: '#/components/parameters/CSRF' } requestBody: required: true content: application/json: schema: { $ref: './onboarding.yaml#/components/schemas/SMSSendInput' } responses: '200': description: 网关已接受发送请求 content: application/json: schema: { $ref: './onboarding.yaml#/components/schemas/SMSResponse' } default: { $ref: '#/components/responses/Failure' } /api/v1/web/operator/login/sms/verify: post: operationId: operatorVerifyLoginSMS summary: 验证验证码并创建管理端登录会话 description: 验证码只能在发起发送的浏览器会话使用,最多尝试 5 次,成功后一次性消费。已有短信账号复用原身份;管理端不开放自助注册,必须预先绑定已有有效管理身份。账号 Cookie 24 小时有效,调用家庭写接口还需 CSRF;保留原管理角色,写操作仍按现有角色权限校验。读取 /api/v1/me 或事件流需附带 X-Login-Portal=operator。 security: [{ OperatorLoginCookie: [] }] parameters: - { $ref: '#/components/parameters/RequestID' } - { $ref: '#/components/parameters/CSRF' } requestBody: required: true content: application/json: schema: { $ref: './onboarding.yaml#/components/schemas/SMSVerifyInput' } responses: '200': description: 签发 HttpOnly 账号 Cookie,消费匿名登录会话;前端继续读取 /api/v1/me 校验业务账号 headers: Set-Cookie: schema: { type: string } content: application/json: schema: type: object required: [ok, data] properties: ok: { type: boolean } data: type: object required: [csrf_token, data] properties: csrf_token: { type: string } data: type: object required: [authenticated] properties: authenticated: { type: boolean } default: { $ref: '#/components/responses/Failure' } /api/v1/web/operator/login/captcha: post: operationId: operatorLoginCaptcha summary: 获取或更换密码登录、发送短信前的图片验证码 description: 返回 PNG 图片和随机 ID,不返回答案。绑定登录 Cookie,2 分钟有效;刷新使旧图失效;每次校验均消费验证码。校验状态保存在 Redis,依赖不可用时拒绝登录。 security: [{ OperatorLoginCookie: [] }] parameters: - { $ref: '#/components/parameters/RequestID' } - { $ref: '#/components/parameters/CSRF' } requestBody: required: true content: application/json: schema: { type: object, additionalProperties: false } responses: '200': description: 图片验证码 content: application/json: schema: type: object required: [ok, data] properties: ok: { type: boolean } data: type: object required: [captcha_id, mode, expires_at_ms] properties: captcha_id: { type: string, format: uuid } mode: { type: string, enum: [image, tencent, slide] } captcha_app_id: { type: string, description: 腾讯云模式对应客户端的公开 CaptchaAppId,不含密钥 } image: { type: string, pattern: '^data:image/(png|jpeg);base64,' } thumb_image: { type: string, pattern: '^data:image/png;base64,' } image_width: { type: integer, minimum: 1 } image_height: { type: integer, minimum: 1 } thumb_width: { type: integer, minimum: 1 } thumb_height: { type: integer, minimum: 1 } thumb_x: { type: integer, minimum: 0, description: 拼图块的初始展示横坐标 } thumb_y: { type: integer, minimum: 0, description: 拼图块所在水平轨道 } expires_at_ms: { type: integer, format: int64 } default: { $ref: '#/components/responses/Failure' } /api/v1/web/operator/login/captcha/verify: post: operationId: operatorVerifyLocalSlideCaptcha summary: 校验本地滑块位置并换取一次性票据 description: 同源、CSRF 和运营登录会话校验;挑战每次提交即消费。成功票据绑定原会话、客户端、策略版本和原到期时间,只能用于一次短信发送或密码登录。 security: [{ OperatorLoginCookie: [] }] parameters: - { $ref: '#/components/parameters/RequestID' } - { $ref: '#/components/parameters/CSRF' } requestBody: required: true content: application/json: schema: { $ref: './login-captcha.yaml#/components/schemas/SlideCaptchaVerifyInput' } responses: '200': description: 位置正确,返回一次性校验票据 content: application/json: schema: { $ref: './login-captcha.yaml#/components/schemas/SlideCaptchaVerifyResponse' } default: { $ref: '#/components/responses/Failure' } /api/v1/web/operator/login/password: post: operationId: operatorLoginPassword summary: 手机号、密码和图片验证码登录 description: 使用与短信登录一致的手机号身份,密码登录不自动注册。每手机号 10 分钟最多 10 次密码尝试;每登录会话最多 20 次。失败返回统一的手机号或密码错误,不暴露手机号是否注册。密码正确但没有该账号的有效设备验证 Cookie 时返回 403 NEW_DEVICE_SMS_REQUIRED,不消费登录会话、不发账号 Cookie;完成现有 LOGIN 短信验证后签发 30 天 HttpOnly 设备验证 Cookie。已验证设备成功消费登录会话,签发 24 小时账号 Cookie。 security: [{ OperatorLoginCookie: [] }] parameters: - { $ref: '#/components/parameters/RequestID' } - { $ref: '#/components/parameters/CSRF' } requestBody: required: true content: application/json: schema: type: object additionalProperties: false required: [phone_e164, password, captcha_id] anyOf: - required: [captcha_code] - required: [captcha_ticket] properties: phone_e164: { type: string, pattern: '^\+[1-9][0-9]{7,14}$' } password: { type: string, minLength: 1, maxLength: 256, writeOnly: true } captcha_id: { type: string, format: uuid } captcha_code: { type: string, pattern: '^[0-9]{6}$', writeOnly: true } captcha_ticket: { type: string, maxLength: 4096, writeOnly: true } captcha_randstr: { type: string, maxLength: 256, writeOnly: true, description: 腾讯云 Web 模式必填,本地滑块模式无需提供 } responses: '200': $ref: '#/paths/~1api~1v1~1web~1operator~1login~1sms~1verify/post/responses/200' default: { $ref: '#/components/responses/Failure' } /api/v1/web/operator/account/password: get: operationId: operatorPasswordState summary: 查看当前手机号和密码设置状态 security: [{ OperatorAccountCookie: [] }] responses: '200': description: 仅返回脱敏手机号、是否已设置密码、是否具有最近 10 分钟内且未消费的短信验证,不返回密码或摘要 content: application/json: schema: type: object properties: ok: { type: boolean } data: type: object required: [available] properties: available: { type: boolean } phone_masked: { type: string } password_configured: { type: boolean } sms_verified_recently: { type: boolean } default: { $ref: '#/components/responses/Failure' } post: operationId: operatorSetPassword summary: 设置、修改或重置当前手机号的密码 description: 需要当前有效账号 Cookie、同源及 CSRF;同时需要最近 10 分钟内且未消费的短信验证,或正确的当前密码。新密码 6–64 个 Unicode 字符且不超过 256 字节。修改后清除短信证明并撤销该身份的其他管理登录会话,保留本次会话。密码使用随机盐 Argon2id 存储。 security: [{ OperatorAccountCookie: [] }] parameters: - { $ref: '#/components/parameters/RequestID' } - { $ref: '#/components/parameters/CSRF' } requestBody: required: true content: application/json: schema: type: object additionalProperties: false required: [password] properties: password: { type: string, minLength: 6, maxLength: 64, writeOnly: true } current_password: { type: string, maxLength: 256, writeOnly: true } responses: '200': description: 密码已保存 content: application/json: schema: type: object properties: ok: { type: boolean } data: type: object properties: password_configured: { type: boolean } default: { $ref: '#/components/responses/Failure' } /api/v1/web/operator/account/session: get: operationId: operatorAccountSession summary: 恢复管理端登录会话并获取 CSRF security: [{ OperatorAccountCookie: [] }] responses: '200': description: 有效管理账号会话 content: application/json: schema: type: object properties: ok: { type: boolean } data: type: object required: [authenticated, csrf_token] properties: authenticated: { type: boolean } csrf_token: { type: string } default: { $ref: '#/components/responses/Failure' } /api/v1/web/operator/auth/logout: post: operationId: operatorLogout summary: 撤销当前管理端会话并清除其 Cookie description: 同源与 CSRF 校验;家庭端会话不受影响。 security: [{ OperatorAccountCookie: [] }] parameters: - { $ref: '#/components/parameters/RequestID' } - { $ref: '#/components/parameters/CSRF' } requestBody: required: true content: application/json: schema: { type: object, additionalProperties: false } responses: '200': description: 当前管理会话已退出 default: { $ref: '#/components/responses/Failure' } components: securitySchemes: OperatorAccountCookie: type: apiKey in: cookie name: __Host-platform-operator description: 仅本地 HTTP 模式使用 platform-operator。与家庭端会话、密码独立;写操作需要同源及 CSRF。 OperatorLoginCookie: type: apiKey in: cookie name: __Host-platform-operator-login description: Secure、HttpOnly、SameSite=Lax;仅本地 HTTP 模式使用 platform-operator-login。 parameters: RequestID: in: header name: X-Request-ID required: true schema: { type: string, format: uuid } description: UUID v4,发送结果未知时用同一 ID 和正文重试。 CSRF: in: header name: X-CSRF-Token required: true schema: { type: string } responses: Failure: description: 400/401/403/409/413/415/429/500/503;SMS_CODE_INVALID、LOGIN_SESSION_EXPIRED、SMS_RATE_LIMITED、SMS_SEND_UNCERTAIN、SMS_UNAVAILABLE、CAPTCHA_INVALID、CAPTCHA_UNAVAILABLE、PASSWORD_INVALID、PASSWORD_POLICY、CURRENT_PASSWORD_INVALID、SMS_REAUTH_REQUIRED、RATE_LIMITED 等,不返回验证码或密码。 content: application/json: schema: { $ref: './onboarding.yaml#/components/schemas/Failure' }