怡途Claw 所有 /api/app/* 接口都要带 JWT token。本文从后端代码拆解 send-verify-code、register、login 三个鉴权接口的请求响应,token 的 24 小时过期与 Bearer 头用法,以及注销即失效的校验细节。
前面几篇讲了怎么部署实例、对话、传文件、管会话。但所有 /api/app/* 接口都有一个前提:你得先拿到一个登录 token,放在请求头里,不然直接返回 401。
实际接进来会碰到几个问题:
- 调接口老返回
未登录,到底要带什么头? - token 是怎么来的,注册和登录返回的 token 有区别吗?
- token 能用多久,过期了怎么办?
- 用户注销了账号,他手上那个老 token 还能不能继续调接口?
怡途Claw 的鉴权用 JWT。本文直接从 /api/app/auth 这组接口和后端代码来看 token 是怎么签发、怎么校验的。
整体流程
三步:
- 没账号就先注册:发验证码 -> 注册(拿到 token)
- 有账号就登录:手机号或邮箱 + 密码 -> 登录(拿到 token)
- 之后所有
/api/app/*接口,请求头带Authorization: Bearer <token>
token 本身是一串 JWT,后端校验签名和过期时间就能知道是谁。但每次请求还会额外查一次库确认账号还在不在,原因后面讲。
第一步:发验证码 POST /api/app/auth/send-verify-code
注册前要先拿验证码。请求体只有一个字段:
{ "phone": "13800138000" }
手机号必须符合 1[3-9] 开头的 11 位格式,不然后端 DTO 直接挡掉,返回 手机号格式不正确。
接口有两个路径别名,/api/app/auth/send-verify-code 和 /api/app/auth/sms/send 指向同一个处理函数,都能用。
返回:
{ "success": true, "message": "验证码已发送", "phone": "13800138000" }
一个细节:在 development 或 test 环境下,返回里会多一个 devCode 字段,直接把 6 位验证码明文带回来,方便联调。生产环境没有这个字段,验证码只走短信。
第二步:注册 POST /api/app/auth/register
请求体:
{
"phone": "13800138000",
"verifyCode": "123456",
"password": "yourpass",
"confirmPassword": "yourpass",
"name": "可选昵称",
"inviteCode": "1234"
}
字段约束(DTO 层强制的):
verifyCode必须正好 6 位password和confirmPassword都至少 6 位,两次要一致name可选inviteCode可选,填的话必须是 4 位数字
注册成功直接返回 token,不用再调一次登录:
{
"success": true,
"data": {
"user": {
"id": 101,
"phone": "13800138000",
"name": "可选昵称",
"role": "user",
"agent": { "...": "该用户的 agent 概要" }
},
"token": "eyJhbGciOiJIUzI1NiIs..."
}
}
返回里的 agent 是该用户的 agent 概要信息,注册时一并带回来,前端可以直接拿去渲染。
第三步:登录 POST /api/app/auth/login
已经注册过的账号,直接登录拿 token。请求体:
{ "phone": "13800138000", "password": "yourpass" }
或者用邮箱:
{ "email": "you@example.com", "password": "yourpass" }
phone 和 email 都是可选的,二选一,password 必填。返回结构和注册一模一样:{ user, token }。
代码里登录会比注册多做一件事:更新 lastLoginAt。所以登录接口除了签发 token,还会顺手记一下本次登录时间。
token 里装了什么
后端签 token 时塞进去的 payload 是这三个字段:
{ "phone": "...", "sub": userId, "role": "user" }
sub 就是用户 id,后端校验 token 后从 sub 拿到当前用户。也就是说你在文件上传那篇里传文件、会话管理那篇里建会话,后端都是靠这个 sub 知道"这是谁家的实例"。
两个关键配置:
- 过期时间
1d,token 签发后 24 小时失效。过了这个时间再带上去调接口,后端ignoreExpiration设为 false,直接判过期拒绝。 - 签名密钥是服务端的
JWT_SECRET,token 是后端签的也是后端验的,客户端没法伪造sub去冒充别人。
怎么带 token 调接口
拿到 token 之后,所有挂在 JwtAuthGuard 下的 /api/app/* 接口都要在请求头加:
Authorization: Bearer eyJhbGciOiJIUzI1NiIs...
后端用 ExtractJwt.fromAuthHeaderAsBearerToken() 从这个头里把 token 抠出来。忘了带、带错格式,都是 401 未登录。
验证一下自己是谁,调 GET /api/app/auth/me:
curl https://www.yitukeji.cn/api/app/auth/me \
-H "Authorization: Bearer <你的token>"
返回当前用户资料,带 subscription、package、agent 三个聚合字段。能调通说明 token 有效。这个接口和 GET /api/app/users/profile 返回的内容基本一致,区别是 auth/me 语义上偏"我是谁",users/profile 偏"我的资料",按需选一个。
一个容易被忽略的细节:注销即失效
JWT 本身是无状态的,理论上 token 签发后在过期前一直有效,服务端不存它。但怡途Claw 的校验逻辑里多了一步:每次请求都会拿 sub 去数据库查一次用户,看 isActive 字段。
这意味着:
- 用户注销账号后(
isActive置为 false),他手上那个还没过期的老 token 立刻就废了,下一次请求直接返回账户已注销。 - 不用等 24 小时自然过期。
代价是每个鉴权请求多一次 DB 查询。代码注释里写得很直白:注销后能立即生效比性能重要。对一个按 token 计费、可能涉及客户数据的平台来说,这个取舍是合理的。
这也引出一个实操建议:客户端不要只把 token 存下来就完事,最好在收到 账户已注销 这类 401 时清掉本地 token 并跳回登录页,别拿着废 token 一直重试。
邀请码校验 GET /api/app/auth/invite-code/:inviteCode
注册时如果填了邀请码,可以先调这个接口校验合不合法:
curl https://www.yitukeji.cn/api/app/auth/invite-code/1234
合法返回邀请码对应的信息,不合法返回对应错误。这个接口不需要登录,可以放在注册表单填完邀请码、输入框失焦时做即时校验,避免提交到 register 才发现邀请码不对。
小结
接怡途Claw 的 API,鉴权这条线就三件事:
- 没账号走
send-verify-code->register,有账号走login,都能拿到 token。 - token 是 24 小时有效的 JWT,每次请求带
Authorization: Bearer <token>。 - 账号注销后老 token 立即失效,不用等过期。
token 拿到之后,前面几篇讲的部署、对话、文件、会话、实例生命周期那些接口就都能调了。这串 token 就是进入整套 /api/app/* 的门票。