公开接口
Passport 提供了一套完整的 REST API,供开发者在任意语言或框架的应用中调用,实现认证鉴权功能。
本文档以
passport-service的路由注册(internal/router/router.go)为基准编写,所有接口路径、参数与响应结构均取自当前实现。
Base URL
根据部署环境选择:
# 生产环境
https://passport.vancone.com
# 测试环境(仅限内网访问)
http://passport.beta.vancone.com
所有接口统一前缀为 /api/passport/service/v1。
请求说明
认证方式
Token 通过以下两种方式之一传递,服务端优先读取 Cookie:
| 方式 | 名称 |
|---|---|
| Cookie | passport_token |
| 请求头 | passport-token |
登录成功后,服务端会同时下发两个 Cookie:passport_token(Token)与 passport_username(用户名或登录名)。
需要认证的接口
服务端按 passport.filter.url-patterns 配置对匹配的路由做 Token 校验,未匹配的路径不做校验。默认需要携带 Token 的接口为:
| 路径 | 说明 |
|---|---|
/api/passport/service/v1/account | 账号查询、更新(创建 POST 除外) |
/api/passport/service/v1/account/* | 登录历史、找回密码等子路径 |
/api/passport/service/v1/account-profile | 用户资料 |
/api/passport/service/v1/account-profile/* | 头像 |
/api/passport/service/v1/sync/* | 同步接口 |
/api/passport/service/v1/query/* | 分页查询 |
显式的免校验规则(代码硬编码,不受配置影响):
POST /api/passport/service/v1/account(账号注册)/api/passport/service/v1/account/forgot-password/*(找回密码全流程)
请求体格式
除「验证滑块验证码」接口使用 application/x-www-form-urlencoded 外,其余接口请求与响应均为 JSON 格式。
响应格式
所有响应遵循统一格式:
{
"code": 0,
"message": "success",
"data": { ... }
}
| 字段 | 说明 |
|---|---|
| code | 业务状态码,0 表示成功,其他值表示失败 |
| message | 响应消息 |
| data | 响应数据,无数据时该字段被省略 |
HTTP 状态码与业务状态码是两套独立的东西,请务必区分:
| HTTP 状态码 | 业务 code | 出现场景 |
|---|---|---|
| 200 | 0 | 成功 |
| 200 | 10001–10005、4001–4006 | 登录失败、找回密码失败。业务错误仍返回 200,必须解析 body 中的 code 判断 |
| 200 / 500 | -1 | 服务端异常、参数绑定失败等兜底错误 |
| 401 | 11002 | Token 缺失或无效 |
| 403 | 403 | 越权(例如尝试更新他人账号) |
| 404 | - | 资源不存在(如无头像时获取头像) |
API 列表
Login 模块
1.1 用户登录
GET /api/passport/service/v1/login
POST /api/passport/service/v1/login
登录必须先通过滑块验证码(见 8.2、8.3),否则会返回 10005。
请求参数(GET 走 query,POST 走 JSON body):
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| userId | string | 否 | 登录名。与 email 二选一,二者同时为空时按空账号处理 |
| string | 否 | 邮箱。userId 为空时作为登录标识 | |
| password | string | 是 | 密码明文,服务端以 BCrypt 比对 |
| captchaToken | string | 是 | 滑块验证码校验通过后返回的票据 |
| tenantId | string | 否 | 租户 ID |
响应:
{
"code": 0,
"message": "success",
"data": {
"token": "eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9...",
"username": "admin"
}
}
响应中只有
token与username两个字段。username取账号的name,未设置name时回退为userId。
业务结果:登录成功时同时下发 passport_token 与 passport_username 两个 Cookie,有效期由服务端配置 passport.cookie.expired-seconds 决定(默认 86400 秒)。
锁定策略:连续登录失败次数超过服务端配置 passport.login-failure-times-limit(默认 5)后,账号被锁定,返回 10003;登录成功后失败计数清零。
失败响应(HTTP 200):
{
"code": 10002,
"message": "Incorrect password"
}
1.2 登出
DELETE /api/passport/service/v1/login
响应:
{
"code": 0,
"message": "success"
}
说明:清除 passport_token 与 passport_username 两个 Cookie。服务端为无状态 JWT,不维护会话黑名单,登出仅清除客户端 Cookie。
App 登出:请求体可携带 { "refreshToken": "..." },服务端会删除对应的 refresh token 记录实现服务端吊销;Web 端不带请求体,行为不变。
1.3 App 登录(桌面 / 移动端)
POST /api/passport/service/v1/login/app
供原生 App(Electron 桌面端、Android)使用的独立登录端点。与 Web 登录(1.1)的区别:
- 不种 Cookie,Token 由客户端自行安全存储;
- 响应额外返回
refreshToken(有效期 30 天),用于静默续期(见 2.5); - 登录历史中的
clientType记录客户端类型。
请求体:
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| userId | string | 否 | 登录名。与 email 二选一 |
| string | 否 | 邮箱 | |
| password | string | 是 | 密码明文 |
| captchaToken | string | 是 | 滑块验证码票据(见 8.3),App 端同样必须过滑块 |
| tenantId | string | 否 | 租户 ID |
| clientType | string | 否 | 客户端类型:DESKTOP / ANDROID,缺省 APP |
响应:
{
"code": 0,
"message": "success",
"data": {
"token": "eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9...",
"username": "admin",
"refreshToken": "kN9xQ2p...43位URL安全随机串"
}
}
失败语义与 1.1 完全一致(HTTP 200 + 业务码 10001~10005)。
设计说明:App 登录与 Web 登录是两个独立端点(逻辑同一套,契约分开)——Token 生命周期、安全策略、后续废弃节奏(迁移 OIDC PKCE)都按客户端独立演进。
1.4 扫码登录
扫码登录(Web 展示二维码 + 扫码设备确认)包含创建会话、轮询状态、上报扫码、确认登录四个接口,已独立成文,见「扫码登录接入」。
Token 模块
2.1 生成签名
应用账号换取 Token 的第一步。
POST /api/passport/service/v1/token/signature
请求体:
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| accessKeyId | string | 是 | 应用账号的 Access Key ID |
| secretAccessKey | string | 是 | 应用账号的 Secret Access Key |
响应:
{
"code": 0,
"message": "success",
"data": {
"signature": "ABCDEF123456...",
"timestamp": "1640000000000"
}
}
签名计算方式(服务端实现,客户端调用本接口即可,无需自行计算):
timestamp = 当前毫秒时间戳
signature = UPPER(HEX(HMAC-SHA256(secretAccessKey, accessKeyId + timestamp)))
2.2 生成 Token
POST /api/passport/service/v1/token
请求体:
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| accessKeyId | string | 是 | Access Key ID |
| timestamp | string | 是 | 上一步返回的毫秒时间戳 |
| signature | string | 是 | 上一步返回的签名 |
响应:data 为 Token 字符串(不是对象)。
{
"code": 0,
"message": "success",
"data": "eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9..."
}
签名校验失败时返回 HTTP 500、code 为 -1,message 为 signature validation failed。
2.3 验证 Token
POST /api/passport/service/v1/token/validate
请求体:
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| token | string | 是 | 要验证的 Token |
响应:data 为 布尔值。
{
"code": 0,
"message": "success",
"data": true
}
2.4 获取 Token 剩余有效期
POST /api/passport/service/v1/token/expiration
请求体:
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| token | string | 是 | 要查询的 Token |
响应:data 为剩余秒数(整数)。
{
"code": 0,
"message": "success",
"data": 86399
}
说明:Token 无效或已过期时返回 0。Token 有效期固定为签发后 24 小时,不支持续期,过期后需重新登录或重新申请。
2.5 刷新 Token(App)
POST /api/passport/service/v1/token/refresh
使用 1.3 返回的 refreshToken 换取新的 access token 与新的 refresh token。旋转式:旧 refreshToken 在本次调用中被消费,立即作废。
请求体:
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| refreshToken | string | 是 | 1.3 或上次刷新返回的 refreshToken |
响应:
{
"code": 0,
"message": "success",
"data": {
"token": "eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9...",
"refreshToken": "新的refreshToken"
}
}
说明:
- 新 access token 按服务端存储的账号重新签发,权限变更在刷新后即时生效;
refreshToken无效或已过期返回 HTTP 401 /code 11002,客户端应回到登录页;- 该接口免滑块:
refreshToken本身即凭证; - 登出时携带
refreshToken调用DELETE /login可在服务端吊销(见 1.2)。
Token 结构
Token 为 ES256 签名的 JWT,Claims 如下:
| Claim | 说明 |
|---|---|
acid | 账号 ID |
uid | 用户 ID(登录名) |
tid | 租户 ID |
pids | 权限 ID 数组 |
exp | 过期时间(签发时间 + 24 小时) |
Account 模块
3.1 查询当前账号信息
GET /api/passport/service/v1/account
需要携带 Token。返回的是 Token 中 acid 对应的账号。
响应:
{
"code": 0,
"message": "success",
"data": {
"id": "1234567890",
"userId": "user123",
"type": "PERSONAL",
"name": "张三",
"email": "user@example.com",
"phone": "13800138000",
"tenantId": "0",
"applicationId": ""
}
}
字段说明:
| 字段 | 说明 |
|---|---|
| id | 账号 ID(雪花 ID),即 Token 中的 acid 声明 |
| userId | 登录名 |
| type | 账号类型:PERSONAL(个人账号)或 SERVICE(应用账号) |
| name | 账号名称 |
| email / phone | 邮箱、手机号,为空时字段被省略 |
| tenantId | 租户 ID |
| applicationId | 所属应用 ID,仅应用账号有值 |
| loginFailureCount | 连续登录失败次数,为 0 时字段被省略 |
- 出于安全考虑,响应中不包含
password字段。lastLoginTime、birthDay、applicationName为派生字段,当前实现不会填充,响应中不会出现。生日信息请通过「用户资料」接口获取(见 4.1,字段名为birthday)。
3.2 创建账号(注册)
POST /api/passport/service/v1/account
该接口免 Token 校验。
请求体:
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| userId | string | 是 | 登录名 |
| type | string | 是 | 账号类型:PERSONAL 或 SERVICE |
| name | string | 否 | 账号名称 |
| password | string | 否 | 密码。个人账号建议必填 |
| string | 否 | 邮箱地址 | |
| phone | string | 否 | 手机号码 |
| applicationId | string | 否 | 所属应用 ID,仅应用账号需要 |
响应:与 3.1 相同的账号对象。
说明:当前版本注册不需要邮箱验证码,注册完成即可登录,账号没有「待激活」状态。密码复杂度仅由前端校验,服务端未做强制约束,接入方如需约束请自行在业务侧实现。
3.3 更新账号
PUT /api/passport/service/v1/account
需要携带 Token。
请求体:
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| id | string | 是 | 账号 ID,必须与 Token 中的 acid 一致,否则返回 403 |
| name | string | 否 | 账号名称 |
| string | 否 | 邮箱地址,找回密码时使用 | |
| phone | string | 否 | 手机号码 |
响应:
{
"code": 0,
"message": "success"
}
重要警告:该接口采用全量保存。请求体中不要携带
password字段——若携带,提交的明文会直接覆盖数据库中的 BCrypt 密文,导致该账号永久无法登录。当前版本没有提供修改密码接口,修改密码请走「找回密码」流程(见 9.1–9.3)。
Account Profile 模块
4.1 查询用户资料
GET /api/passport/service/v1/account-profile
需要携带 Token。
响应:
{
"code": 0,
"message": "success",
"data": {
"id": "1234567890",
"name": "张三",
"gender": "MALE",
"birthday": "1990-01-01",
"bio": "这是一段个人简介",
"avatar": "/api/passport/service/v1/account-profile/avatar",
"updatedTime": "2024-01-01 10:00:00"
}
}
| 字段 | 说明 |
|---|---|
| id | 账号 ID,与账号的 id 相同 |
| name | 姓名 |
| gender | 性别:MALE / FEMALE / OTHERS |
| birthday | 生日 |
| bio | 个人简介 |
| avatar | 头像访问路径。已上传头像时为该固定路径,未上传时为空字符串 |
| updatedTime | 更新时间 |
资料中不包含
phone,这两个字段属于账号(Account)而非资料(Profile)。
4.2 更新用户资料
PUT /api/passport/service/v1/account-profile
需要携带 Token。
请求体:
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| name | string | 否 | 姓名 |
| gender | string | 否 | 性别:MALE / FEMALE / OTHERS |
| birthday | string | 否 | 生日(格式:YYYY-MM-DD) |
| bio | string | 否 | 个人简介 |
响应:
{
"code": 0,
"message": "success"
}
说明:资料的 id 由服务端从 Token 中取,不读取请求体中的 id,因此无法修改他人资料。
4.3 获取头像
GET /api/passport/service/v1/account-profile/avatar
需要携带 Token。返回当前账号的头像图片二进制流。
未上传头像时返回 HTTP 404,且不返回 JSON 响应体。
4.4 上传头像
POST /api/passport/service/v1/account-profile/avatar
需要携带 Token。使用 multipart/form-data 上传。
请求参数:
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| file | file | 是 | 头像文件。仅支持 .jpg / .jpeg / .png / .gif |
响应:
{
"code": 0,
"message": "success",
"data": "/api/passport/service/v1/account-profile/avatar"
}
说明:文件以「账号 ID + 原扩展名」命名,上传新头像会覆盖同账号的旧头像文件。
Query 模块
5.1 分页查询账号
GET /api/passport/service/v1/query/account
需要携带 Token。
请求参数:
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| pageNo | int | 否 | 页码,默认 1 |
| pageSize | int | 否 | 每页数量,默认 10 |
| search | string | 否 | 搜索关键字 |
| accountId | string | 否 | 指定账号 ID 查询 |
响应:
{
"code": 0,
"message": "success",
"data": {
"list": [
{
"id": "1234567890",
"userId": "user123",
"name": "张三"
}
],
"pageNo": 1,
"pageSize": 10,
"totalCount": 100,
"totalPage": 10
}
}
分页查询仅返回
id、userId、name三个字段,不包含邮箱、手机号等信息。如需完整账号信息,请先按id调用 3.1 查询账号接口。
分页字段说明:
| 字段 | 说明 |
|---|---|
| list | 当前页数据 |
| pageNo | 当前页码 |
| pageSize | 每页数量 |
| totalCount | 总记录数 |
| totalPage | 总页数 |
Login History 模块
6.1 查询登录历史
GET /api/passport/service/v1/account/login-history
需要携带 Token。返回当前账号最近的登录记录。
响应:
{
"code": 0,
"message": "success",
"data": [
{
"accountId": "1234567890",
"clientIp": "192.168.1.1",
"loginMethod": "WEB",
"LoginTime": "2024-01-01 10:00:00"
}
]
}
| 字段 | 说明 |
|---|---|
| accountId | 账号 ID |
| clientIp | 登录 IP |
| clientType | 客户端类型:DESKTOP / ANDROID / APP,Web 登录时该字段被省略 |
| loginMethod | 登录方式:WEB(页面登录)、API(接口换取 Token)或 QR_CODE(扫码登录) |
| LoginTime | 登录时间。注意该字段的 JSON 键首字母为大写,与其他字段风格不一致,取值时请照此处理 |
Sync 模块
用于应用账号(SDK 接入方)同步自身应用下的 API 与权限定义。需要携带应用账号的 Token。
7.1 同步 API 列表
GET /api/passport/service/v1/sync/api
响应:
{
"code": 0,
"message": "success",
"data": [
{
"id": "api123",
"name": "查询用户",
"path": "/api/user/*",
"method": "GET",
"description": "查询用户信息",
"applicationId": "app123",
"tenantId": "0",
"creator": "admin",
"updater": "admin",
"createdTime": "2024-01-01 10:00:00",
"updatedTime": "2024-01-01 10:00:00"
}
]
}
说明:返回当前应用账号所属应用的 API 列表。若账号未绑定应用,返回空数组。
7.2 同步权限列表
GET /api/passport/service/v1/sync/permission
响应:
{
"code": 0,
"message": "success",
"data": [
{
"id": "permission123",
"name": "用户管理权限",
"description": "用户管理相关权限",
"applicationId": "app123",
"applicationName": "用户服务",
"riskLevel": "LOW",
"apiCount": 5,
"tenantId": "0",
"creator": "admin",
"updater": "admin",
"createdTime": "2024-01-01 10:00:00",
"updatedTime": "2024-01-01 10:00:00",
"apiIds": ["api1", "api2", "api3", "api4", "api5"]
}
]
}
说明:权限归属应用,字段为 applicationId / applicationName(早期版本中的 serviceId / serviceName 已废弃)。
7.3 同步标志
GET /api/passport/service/v1/sync/flag
该接口尚未实现,当前调用会返回 HTTP 500。请勿在生产环境依赖此接口。
Verify 模块
8.1 生成邮箱验证码
GET /api/passport/service/v1/verify?email=user@example.com
请求参数:
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| string | 是 | 邮箱地址 |
响应:data 为 null,验证码通过邮件发送。
{
"code": 0,
"message": "success"
}
8.2 生成滑块验证码
GET /api/passport/service/v1/verify/image
响应:返回滑块验证码素材,不是图片流。
{
"code": 0,
"message": "success",
"data": {
"key": "captcha:xxxxxxxx",
"imageBase64": "iVBORw0KGgoAAAANSUhEUg...",
"tileBase64": "iVBORw0KGgoAAAANSUhEUg...",
"tileWidth": 60,
"tileHeight": 60,
"tileX": 120,
"tileY": 45
}
}
| 字段 | 说明 |
|---|---|
| key | 验证码标识,提交校验时回传 |
| imageBase64 | 底图(Base64) |
| tileBase64 | 滑块拼图块(Base64) |
| tileWidth / tileHeight | 拼图块尺寸 |
| tileX / tileY | 拼图块在底图中的目标位置 |
出于安全考虑,
tileX(缺口横坐标)由服务端返回,接入方据此渲染初始位置并采集用户拖动轨迹。
8.3 校验滑块验证码
POST /api/passport/service/v1/verify/image
该接口使用表单格式(application/x-www-form-urlencoded),不是 JSON。
请求参数:
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| point | string | 是 | 用户拖动滑块的落点坐标,格式 "x,y",例如 "118,45" |
| key | string | 是 | 8.2 返回的 key |
响应:
{
"code": 0,
"message": "success",
"data": {
"captchaToken": "captcha:token:xxxxxxxx",
"ok": true
}
}
说明:校验容差为 4 像素。ok 为 false 表示校验未通过,此时 captchaToken 为空。校验通过后返回的 captchaToken 用于登录(1.1)与找回密码(8.4),且为一次性凭证。
Forgot Password 模块
修改密码需通过以下三段式流程完成,该模块全部接口免 Token 校验。流程起点仍需先过滑块验证码。
9.1 发送验证码
POST /api/passport/service/v1/account/forgot-password/send-code
请求体:
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| captchaToken | string | 是 | 滑块验证码票据(见 8.3) |
| string | 是 | 注册时使用的邮箱 |
响应:
{
"code": 0,
"message": "success"
}
失败时返回 HTTP 200 与对应的业务码(见「错误码」),例如 4001 Email not registered。
9.2 校验验证码
POST /api/passport/service/v1/account/forgot-password/verify-code
请求体:
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| string | 是 | 邮箱 | |
| code | string | 是 | 邮件中的验证码 |
响应:
{
"code": 0,
"message": "success",
"data": {
"resetToken": "xxxxxxxx"
}
}
9.3 重置密码
POST /api/passport/service/v1/account/forgot-password/reset
请求体:
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| string | 是 | 邮箱 | |
| resetToken | string | 是 | 8.5 返回的 resetToken |
| newPassword | string | 是 | 新密码 |
响应:
{
"code": 0,
"message": "success"
}
完整流程:
- 调用 8.2 获取滑块验证码素材
- 调用 8.3 提交滑块位置,换取
captchaToken - 调用 9.1(携带
captchaToken与邮箱)发送邮件验证码 - 调用 9.2(携带邮箱与邮件验证码)换取
resetToken - 调用 9.3(携带邮箱、
resetToken、新密码)完成重置
错误码
通用
| 错误码 | HTTP 状态码 | 说明 |
|---|---|---|
| 0 | 200 | 成功 |
| -1 | 500 | 服务端异常或参数绑定失败,message 为具体错误描述 |
| 403 | 403 | 越权操作 |
| 11002 | 401 | Token 缺失或无效({"code":11002,"message":"Not authorized"}) |
登录(Login)
| 错误码 | 说明 |
|---|---|
| 10001 | Account not found(账号不存在) |
| 10002 | Incorrect password(密码错误) |
| 10003 | Account locked due to too many failed attempts(失败次数超限,账号已锁定) |
| 10004 | Account is disabled(账号已禁用) |
| 10005 | Captcha verification failed(滑块验证码校验失败) |
以上错误码均伴随 HTTP 200 返回,请通过 body 中的
code判断,不要只看 HTTP 状态码。
找回密码(Forgot Password)
| 错误码 | 说明 |
|---|---|
| 4001 | Email not registered(邮箱未注册) |
| 4002 | Invalid verification code(验证码错误) |
| 4003 | Verification code expired(验证码已过期) |
| 4004 | Invalid reset token(resetToken 无效) |
| 4005 | Too many attempts(尝试次数过多) |
| 4006 | Captcha verification failed(滑块验证码校验失败) |
| 5001 | Unknown error(兜底错误) |
同样伴随 HTTP 200 返回。
管理端(passport-admin)
管理端服务使用独立的错误码空间:
| 错误码 | 说明 |
|---|---|
| 10001 | Forbidden(无权限) |
| 10002 | 资源 ID 重复 |
| 10004 | 参数非法 |
认证流程
个人账号登录流程
- 调用「生成滑块验证码」获取验证码素材
- 调用「校验滑块验证码」提交滑块位置,换取
captchaToken - 调用「用户登录」,携带
captchaToken、登录名、密码 - 保存返回的
token,后续请求在passport-token请求头或passport_tokenCookie 中携带
扫码登录流程
Web 端与扫码设备端的完整接入步骤见「扫码登录接入」的「接入清单」。
App 登录与续期流程
- 调用「生成滑块验证码」+「校验滑块验证码」换取
captchaToken - 调用「App 登录」(1.3),安全保存返回的
token与refreshToken - access token 过期(或收到 401)时,调用「刷新 Token」(2.5)静默续期;刷新失败则回登录页
- 登出时携带
refreshToken调用DELETE /login,服务端吊销后清除本地存储
应用账号换取 Token 流程
- 调用「生成签名」,传入
accessKeyId与secretAccessKey,获得signature与timestamp - 调用「生成 Token」,传入
accessKeyId、timestamp、signature - 保存返回的 Token 字符串,后续请求在
passport-token请求头中携带 - Token 有效期 24 小时且不支持续期,过期后重新执行本流程(SDK 已内置该流程与缓存,参见「SDK 集成」章节)
权限判断
Token 的 pids 声明中携带了账号被授予的权限 ID 列表。接入方有两种做法:
- 直接解析 JWT 的
pids声明(需持有公钥校验签名) - 调用「同步权限列表」接口获取权限定义,与
pids比对
注意:Go SDK 当前版本不会解析
pids,AccountInfo.PermissionIds字段恒为空,需自行解析。