认证与请求
这一页说明前端如何维护登录态,以及请求层如何与后端接口协作。
登录态管理
- 通过
useAuthhook 管理认证状态 - Token 存储在
localStorage,key 来自@zenith/shared/src/constants.ts:
| 常量 | Key | 说明 |
|---|---|---|
TOKEN_KEY | zenith_token | Access Token |
REFRESH_TOKEN_KEY | zenith_refresh_token | Refresh Token,用于自动续期 |
PREFERENCES_KEY | zenith_preferences | 用户偏好设置(主题、布局等) |
TABS_STORAGE_KEY | zenith_tabs | 多标签页状态 |
useAuth 初始化时读取 TOKEN_KEY,存在 token 时请求 GET /api/auth/me 获取当前用户与权限;登录和注册成功后写入 access token / refresh token,再刷新用户信息。退出登录时清理 token、偏好设置和标签页缓存,并以静默请求通知 /api/auth/logout。
请求封装
前端 HTTP 请求统一封装在:
packages/web/src/utils/request.ts
主要职责:
- 自动附加 Bearer Token
- 非
FormData请求自动设置Content-Type: application/json - 统一处理接口响应
- Access Token 过期时,自动用 Refresh Token 换取新 token 并重试
- 在 401(无法续期)场景下跳转登录页
- 支持
silent静默错误提示、skipAuth跳过 401 自动刷新、postForm上传进度和download文件下载
request.ts 只是传输层
业务代码不直接调用 request.get 拉取页面数据。服务端状态(列表、详情、下拉源等)统一通过 TanStack Query 的域 hooks 管理(hooks/queries/),queryFn 内部才使用 request.get(url).then(unwrap)。完整规范见数据获取与服务端状态。
与后端的协作方式
请求头
需要认证的请求应自动带上:
Authorization: Bearer <token>FormData 上传不手动设置 Content-Type,由浏览器自动补充 multipart boundary。
401 刷新流程
请求返回 401 时,请求封装会读取 REFRESH_TOKEN_KEY 并调用:
POST /api/auth/refresh请求体为:
{
"refreshToken": "<refresh-token>"
}刷新成功后更新 TOKEN_KEY 中的 access token,并重试原请求;刷新失败或重试仍为 401 时,清理 TOKEN_KEY / REFRESH_TOKEN_KEY 并跳转到 ${BASE_URL}/login。并发刷新由内部 refreshing Promise 复用,避免同时发起多次刷新请求。
响应读取
优先按统一响应格式读取:
{
"code": 0,
"message": "success",
"data": {}
}429 响应会读取 Retry-After 头并返回 retryAfterSeconds;503 响应会触发 maintenance:enabled 事件,用于展示维护模式覆盖层。
共享类型
接口类型、实体定义和校验 schema 尽量复用 @zenith/shared,避免前后端各写一套。
开发环境代理配置
开发环境下,前端 Vite Dev Server 通过内置代理将 /api/* 请求转发到后端,避免跨域问题,同时让前端无需感知后端地址。
相关文件:
packages/web/.env.development— 开发环境变量packages/web/vite.config.ts— 代理规则定义
关键环境变量:
| 变量 | 说明 | 默认值 |
|---|---|---|
VITE_API_PROXY_TARGET | 代理目标(后端地址),仅在 Vite Dev Server 中生效,不会暴露到客户端 | http://localhost:3300 |
VITE_API_BASE_URL | 客户端 API 基础 URL。开发时留空,请求走相对路径 /api/... 由代理转发;生产部署时填写后端完整地址,如 https://api.yourdomain.com | 空(开发) |
VITE_WS_BASE_URL | WebSocket 基础 URL。开发时留空,由 useWebSocket.ts 自动从当前 Origin 推导并经代理转发;生产时填写如 wss://api.yourdomain.com | 空(开发) |
代理规则(vite.config.ts):
server: {
proxy: {
'/api': {
target: apiTarget, // 来自 VITE_API_PROXY_TARGET
changeOrigin: true,
ws: true, // 同时代理 WebSocket(/api/ws)
},
},
},各环境配置示例:
VITE_API_BASE_URL=
VITE_WS_BASE_URL=
VITE_API_PROXY_TARGET=http://localhost:3300VITE_API_BASE_URL=https://api.yourdomain.com
VITE_WS_BASE_URL=wss://api.yourdomain.com
# 生产构建无 Dev Server,无需 VITE_API_PROXY_TARGET注意:
VITE_API_PROXY_TARGET只在vite.config.ts中通过loadEnv读取,用于 Dev Server 代理配置;业务代码不通过import.meta.env读取它,后端代理地址不会进入生产包。
会员端独立请求实例
会员前台使用独立请求封装:
packages/web/src/member/utils/member-request.ts
会员 token 与后台管理员 token 隔离:
| 常量 | Key | 说明 |
|---|---|---|
MEMBER_TOKEN_KEY | zenith_member_token | 会员 Access Token |
MEMBER_REFRESH_TOKEN_KEY | zenith_member_refresh_token | 会员 Refresh Token |
memberRequest 携带会员 token;401 时调用 POST /api/member/auth/refresh 刷新,失败后清理会员 token 并跳转 /member.html#/login。会员端不要复用后台的 request.ts。会员端服务端状态同样由 TanStack Query 管理,使用独立的 memberQueryClient(member/lib/member-query.ts)与域 hooks(member/hooks/queries.ts)。
开发建议
- 新增接口前,先确认是否已有共享类型或校验 schema
- 对需要登录的页面,优先复用现有登录态与跳转机制
- 请求错误处理尽量集中在封装层,不把每个页面都写成“各自为战”
- 页面数据获取遵循数据获取与服务端状态中的域 hooks 模式,不在组件里手写 fetch 状态机