Files
SatoNano/docs/auth-api.md
2026-01-06 23:49:23 +08:00

15 KiB
Raw Permalink Blame History

用户认证系统 API 文档

概述

本文档描述 SatoNano 云服务综合管理平台的用户认证系统 API。

基础信息

  • Base URL: http://localhost:8000/api/v1
  • 认证方式: Bearer Token (JWT)
  • Content-Type: application/json
  • 支持登录方式: 用户名密码 / OAuth2 (Linux.do)

统一响应格式

所有 API 响应遵循统一格式:

{
  "success": true,
  "message": "操作成功",
  "data": { ... }
}

错误响应:

{
  "success": false,
  "message": "错误描述",
  "code": "ERROR_CODE",
  "details": { ... }
}

认证接口

1. 用户注册

创建新用户账户。

请求

POST /auth/register

请求体

字段 类型 必填 描述
username string 用户名3-32位字母开头只允许字母、数字、下划线
password string 密码8-128位需包含大小写字母和数字
email string 邮箱地址
nickname string 昵称最长64位

示例请求

{
  "username": "john_doe",
  "password": "SecurePass123",
  "email": "john@example.com",
  "nickname": "John"
}

成功响应 201 Created

{
  "success": true,
  "message": "注册成功",
  "data": {
    "id": "875cd9b4-3504-455d-9290-b6d1ba6b56e0",
    "username": "john_doe",
    "email": "john@example.com",
    "nickname": "John",
    "avatar_url": null,
    "bio": null,
    "is_active": true,
    "created_at": "2026-01-05T13:39:07.653138",
    "last_login_at": null
  }
}

错误响应

状态码 说明
409 用户名或邮箱已被注册
422 请求数据验证失败

2. 用户登录

使用用户名/邮箱和密码登录,获取访问令牌。

请求

POST /auth/login

请求体

字段 类型 必填 描述
username string 用户名或邮箱
password string 密码

示例请求

{
  "username": "john_doe",
  "password": "SecurePass123"
}

成功响应 200 OK

{
  "success": true,
  "message": "登录成功",
  "data": {
    "access_token": "eyJhbGciOiJIUzI1NiIs...",
    "refresh_token": "eyJhbGciOiJIUzI1NiIs...",
    "token_type": "Bearer",
    "expires_in": 1800
  }
}

错误响应

状态码 说明
401 用户名或密码错误
403 账户已被禁用

3. 刷新令牌

使用刷新令牌获取新的访问令牌。

请求

POST /auth/refresh

请求体

字段 类型 必填 描述
refresh_token string 刷新令牌

示例请求

{
  "refresh_token": "eyJhbGciOiJIUzI1NiIs..."
}

成功响应 200 OK

{
  "success": true,
  "message": "刷新成功",
  "data": {
    "access_token": "eyJhbGciOiJIUzI1NiIs...",
    "refresh_token": "eyJhbGciOiJIUzI1NiIs...",
    "token_type": "Bearer",
    "expires_in": 1800
  }
}

错误响应

状态码 说明
401 令牌无效或已过期
403 账户已被禁用

4. 用户退出

退出登录。客户端应删除本地存储的令牌。

请求

POST /auth/logout

请求头

Authorization: Bearer <access_token>

成功响应 200 OK

{
  "success": true,
  "message": "退出成功",
  "data": null
}

5. 修改密码

修改当前用户的密码。

请求

POST /auth/change-password

请求头

Authorization: Bearer <access_token>

请求体

字段 类型 必填 描述
current_password string 当前密码
new_password string 新密码8-128位

示例请求

{
  "current_password": "SecurePass123",
  "new_password": "NewSecurePass456"
}

成功响应 200 OK

{
  "success": true,
  "message": "密码修改成功",
  "data": null
}

错误响应

状态码 说明
400 当前密码错误 / 新密码不符合要求
401 未认证

OAuth2 接口

SatoNano 支持通过 Linux.do 平台进行 OAuth2 第三方登录。

特性

  • 主备端点自动切换:当首选 OAuth2 端点不可达时,自动回退到备用端点
  • 状态码验证:使用 state 参数防止 CSRF 攻击
  • 自动用户创建:首次登录自动创建本地用户账户

1. 获取授权 URL

获取 OAuth2 授权页面 URL用于重定向用户到第三方平台。

请求

GET /auth/oauth2/authorize

成功响应 200 OK

{
  "success": true,
  "message": "请重定向到授权 URL",
  "data": {
    "authorize_url": "https://connect.linux.do/oauth2/authorize?client_id=xxx&redirect_uri=xxx&response_type=code&state=xxx&scope=read",
    "state": "random-state-string"
  }
}

使用流程

  1. 前端调用此接口获取 authorize_url
  2. 将用户重定向到 authorize_url
  3. 用户在 Linux.do 完成授权
  4. Linux.do 重定向回应用的回调 URL

2. OAuth2 回调

处理 OAuth2 授权回调,完成登录流程。

请求

GET /auth/oauth2/callback?code=xxx&state=xxx

查询参数

参数 类型 必填 描述
code string OAuth2 授权码
state string 状态码(防 CSRF

成功响应 200 OK

{
  "success": true,
  "message": "登录成功",
  "data": {
    "access_token": "eyJhbGciOiJIUzI1NiIs...",
    "refresh_token": "eyJhbGciOiJIUzI1NiIs...",
    "token_type": "Bearer",
    "expires_in": 1800,
    "is_new_user": false
  }
}

响应字段说明

字段 说明
is_new_user true 表示首次登录,已自动创建账户

错误响应

状态码 说明
400 无效的状态码(可能是 CSRF 攻击或状态已过期)
401 OAuth2 认证失败(授权码无效等)
503 OAuth2 未配置或服务不可用

OAuth2 登录流程图

┌─────────────┐          ┌─────────────┐          ┌─────────────┐
│   前端应用   │          │   SatoNano  │          │  Linux.do   │
└─────┬───────┘          └──────┬──────┘          └──────┬──────┘
      │                         │                        │
      │ 1. GET /auth/oauth2/authorize                    │
      │─────────────────────────>                        │
      │                         │                        │
      │ 2. 返回 authorize_url + state                    │
      │<─────────────────────────                        │
      │                         │                        │
      │ 3. 重定向用户到 authorize_url                    │
      │──────────────────────────────────────────────────>
      │                         │                        │
      │                         │     4. 用户授权        │
      │                         │<───────────────────────│
      │                         │                        │
      │ 5. 重定向回 callback?code=xxx&state=xxx          │
      │<──────────────────────────────────────────────────
      │                         │                        │
      │ 6. GET /auth/oauth2/callback                     │
      │─────────────────────────>                        │
      │                         │                        │
      │                         │ 7. 用 code 换取 token  │
      │                         │───────────────────────>│
      │                         │                        │
      │                         │ 8. 返回 access_token   │
      │                         │<───────────────────────│
      │                         │                        │
      │                         │ 9. 获取用户信息        │
      │                         │───────────────────────>│
      │                         │                        │
      │                         │ 10. 返回 user info     │
      │                         │<───────────────────────│
      │                         │                        │
      │ 11. 返回 JWT 令牌       │                        │
      │<─────────────────────────                        │
      │                         │                        │

用户接口

1. 获取当前用户信息

获取当前登录用户的详细信息。

请求

GET /users/me

请求头

Authorization: Bearer <access_token>

成功响应 200 OK

{
  "success": true,
  "message": "操作成功",
  "data": {
    "id": "875cd9b4-3504-455d-9290-b6d1ba6b56e0",
    "username": "john_doe",
    "email": "john@example.com",
    "nickname": "John",
    "avatar_url": null,
    "bio": null,
    "is_active": true,
    "created_at": "2026-01-05T13:39:07.653138",
    "last_login_at": "2026-01-05T13:39:19.028376"
  }
}

2. 更新当前用户信息

更新当前登录用户的资料。

请求

PATCH /users/me

请求头

Authorization: Bearer <access_token>

请求体(所有字段可选)

字段 类型 描述
nickname string 昵称最长64位
email string 邮箱地址
avatar_url string 头像 URL最长512位
bio string 个人简介最长500位

示例请求

{
  "nickname": "Johnny",
  "bio": "Hello, World!"
}

成功响应 200 OK

{
  "success": true,
  "message": "更新成功",
  "data": {
    "id": "875cd9b4-3504-455d-9290-b6d1ba6b56e0",
    "username": "john_doe",
    "email": "john@example.com",
    "nickname": "Johnny",
    "avatar_url": null,
    "bio": "Hello, World!",
    "is_active": true,
    "created_at": "2026-01-05T13:39:07.653138",
    "last_login_at": "2026-01-05T13:39:19.028376"
  }
}

错误响应

状态码 说明
409 邮箱已被其他用户使用

3. 获取指定用户信息

获取指定用户的信息。

请求

GET /users/{user_id}

请求头

Authorization: Bearer <access_token>

路径参数

参数 类型 描述
user_id string 用户 UUID

成功响应 200 OK

{
  "success": true,
  "message": "操作成功",
  "data": {
    "id": "875cd9b4-3504-455d-9290-b6d1ba6b56e0",
    "username": "john_doe",
    "email": "john@example.com",
    "nickname": "Johnny",
    "avatar_url": null,
    "bio": "Hello, World!",
    "is_active": true,
    "created_at": "2026-01-05T13:39:07.653138",
    "last_login_at": "2026-01-05T13:39:19.028376"
  }
}

错误响应

状态码 说明
404 用户不存在

错误码说明

错误码 HTTP 状态 说明
AUTHENTICATION_ERROR 401 认证失败
INVALID_CREDENTIALS 401 用户名或密码错误
TOKEN_ERROR 401 令牌无效
TOKEN_EXPIRED 401 令牌已过期
OAUTH2_ENDPOINT_ERROR 401 OAuth2 服务不可用
OAUTH2_STATE_ERROR 400 OAuth2 状态码无效
OAUTH2_TOKEN_ERROR 401 OAuth2 令牌获取失败
OAUTH2_USERINFO_ERROR 401 OAuth2 用户信息获取失败
AUTHORIZATION_ERROR 403 权限不足
USER_DISABLED 403 账户已被禁用
RESOURCE_NOT_FOUND 404 资源不存在
USER_ALREADY_EXISTS 409 用户已存在
VALIDATION_ERROR 422 数据验证失败
PASSWORD_VALIDATION_ERROR 422 密码不符合要求

密码策略

默认密码要求:

  • 长度8-128 位
  • 必须包含至少一个大写字母
  • 必须包含至少一个小写字母
  • 必须包含至少一个数字

可通过环境变量配置:

PASSWORD_MIN_LENGTH=8
PASSWORD_MAX_LENGTH=128
PASSWORD_REQUIRE_UPPERCASE=true
PASSWORD_REQUIRE_LOWERCASE=true
PASSWORD_REQUIRE_DIGIT=true
PASSWORD_REQUIRE_SPECIAL=false

令牌说明

Access Token

  • 用于 API 认证
  • 默认有效期30 分钟
  • 通过 Authorization: Bearer <token> 请求头传递

Refresh Token

  • 用于获取新的 Access Token
  • 默认有效期7 天
  • 仅用于 /auth/refresh 接口

JWT Payload 结构

Access Token:

{
  "sub": "user-uuid",
  "iat": 1767620359,
  "exp": 1767622159,
  "type": "access",
  "username": "john_doe",
  "is_superuser": false,
  "oauth_provider": null
}

OAuth2 登录的用户 oauth_provider 字段为 "linuxdo"

Refresh Token:

{
  "sub": "user-uuid",
  "iat": 1767620359,
  "exp": 1768225159,
  "type": "refresh"
}

OAuth2 配置说明

Linux.do OAuth2 端点

端点 URL
authorize https://connect.linux.do/oauth2/authorize
token https://connect.linux.do/oauth2/token
userinfo https://connect.linux.do/api/user

config.yaml 中配置

# OAuth2 配置 (Linux.do)
oauth2_client_id: your_client_id
oauth2_client_secret: your_client_secret
oauth2_callback_path: /api/v1/auth/oauth2/callback

# 首选端点
oauth2_authorize_endpoint: https://connect.linux.do/oauth2/authorize
oauth2_token_endpoint: https://connect.linux.do/oauth2/token
oauth2_user_info_endpoint: https://connect.linux.do/api/user

# 备用端点(首选不可达时自动回退)
oauth2_authorize_endpoint_reserve: https://connect.linuxdo.org/oauth2/authorize
oauth2_token_endpoint_reserve: https://connect.linuxdo.org/oauth2/token
oauth2_user_info_endpoint_reserve: https://connect.linuxdo.org/api/user

# 请求超时(秒)
oauth2_request_timeout: 10

Linux.do 返回的用户信息

{
    "id": 1,
    "username": "neo",
    "name": "Neo",
    "active": true,
    "trust_level": 4,
    "email": "u1@linux.do",
    "avatar_url": "https://linux.do/xxxx",
    "silenced": false
}

主备端点切换逻辑

  1. 首先尝试首选端点(connect.linux.do
  2. 如果首选端点超时、连接失败或返回 5xx 错误,自动切换到备用端点(connect.linuxdo.org
  3. 如果备用端点也失败,抛出 OAUTH2_ENDPOINT_ERROR 错误