LocalAI 认证与授权实战:从 API Key 到多用户 RBAC、OAuth/OIDC 与用量追踪
LocalAI 默认以「开箱即用」的方式对外提供服务,但在生产环境中你需要为实例加上访问控制。本文基于 LocalAI 官方特性文档 Authentication & Authorization 并结合仓库源码,系统讲解两套认证机制:legacy API Key 认证(共享密钥)与完整的多用户认证体系(角色、会话、OAuth/OIDC、按用户用量追踪),以及二者同时启用时的授权回退顺序。读完本文,你将掌握如何通过环境变量启用认证、理解「私有一切、显式放行」的路由鉴权模型、为团队配置 GitHub OAuth / OIDC 单点登录,并利用 invite 邀请码、用户级 API Key 与用量面板落地多租户治理。
双认证模式总览
LocalAI 提供两种相互独立的认证模式:
- Legacy API Key 认证(legacy):通过一个或多个共享密钥保护整个实例,简单直接,但所有 Key 一律拥有完整管理员权限,无角色区分;
- 用户认证系统(user auth):基于数据库存储的用户、角色、会话与个人 API Key,支持管理员/普通用户两级 RBAC、注册审批流、邀请链接、GitHub OAuth 与通用 OIDC 单点登录,并对每个用户、每个 API Key 做 token 用量追踪。
两条路线可叠加使用(见下文「组合认证模式」),用于从共享 Key 平滑迁移到多用户账户体系。关键区别一句话概括:legacy Key 是「门禁」,能进门的都是管理员;用户体系则是「门禁 + 分诊」,进门后还要看角色和权限。
Legacy API Key 认证
Legacy API Key 是保护 LocalAI 实例成本最低的方式。通过环境变量或 CLI flag 设置一个或多个 Key:
# 单 Key
LOCALAI_API_KEY=sk-my-secret-key localai run
# 多 Key(逗号分隔)
LOCALAI_API_KEY=key1,key2,key3 localai run
在源码中该配置项位于 core/cli/run.go,同时支持别名 API_KEY,最终注入 ApplicationConfig.ApiKeys:
APIKeys []string `env:"LOCALAI_API_KEY,API_KEY" help:"List of API Keys to enable API authentication..."`
客户端可通过以下任意一种方式携带 Key:
Authorization: Bearer <key>请求头x-api-key: <key>请求头xi-api-key: <key>请求头tokenCookie
对应的提取逻辑在 core/http/auth/middleware.go 的 extractKey 函数中,四种来源被统一解析为待校验的密钥字符串。校验时使用常量时间比较(crypto/subtle.ConstantTimeCompare)防止时序侧信道攻击,见 middleware.go。
Legacy API Key 授予完整管理员权限,不存在角色隔离。 当认证开启时,命中 legacy Key 的请求会在中间件中被构造成一个合成的管理员用户(ID 为 legacy-api-key,角色 admin),见 middleware.go:
syntheticUser := &User{
ID: "legacy-api-key",
Name: "API Key User",
Role: RoleAdmin,
}
c.Set(contextKeyUser, syntheticUser)
c.Set(contextKeyRole, RoleAdmin)
因此,凡是需要多用户按角色隔离权限的部署场景,应当改用用户认证系统。
此外,API Key 还可在运行期通过 Runtime Settings 界面管理,相关文档见 Runtime Settings。
受保护路由与匿名路由
当配置了数据库认证(用户体系)或 legacy API Key 后,LocalAI 的 HTTP 面默认全部私有。匿名请求只有在满足以下条件之一时才能通过:
- 命中「公共发现 API」或「匿名引导路由」白名单;
- 命中显式部署级豁免(
PathWithoutAuth前缀); - 命中 legacy GET 豁免正则;
- 命中采用替代认证方式的路由组(如
/api/node/前缀)。
如果两种认证模式都未配置,鉴权中间件不做任何限制,全部请求放行。
这一「白名单判断」模型在 core/http/auth/public_routes.go 的 isPublicRoute 中实现——先精确匹配,再按以 / 结尾的 Prefix 规则做前缀匹配;整体判断流程见 middleware.go,中间件按「已认证 → 公共路由 → PathWithoutAuth 覆盖 → 替代认证 → legacy GET 豁免 → 拒绝」的顺序执行。
公开发现 API
以下发现类请求无需凭据即可匿名访问:
GET /.well-known/localai.jsonGET /api/instructionsGET /api/instructions/{name}/swagger及/swagger/下的 SwaggerGET请求
注意:这些发现 API 只描述服务器的接口面,它们对外宣告的端点本身仍然需要凭据,除非该端点同样出现在公开名单或引导路由名单中。
对应源码注册见 public_routes.go:
// Discovery.
{Method: http.MethodGet, Path: "/api/instructions"},
{Method: http.MethodGet, Path: "/api/instructions/", Prefix: true},
{Method: http.MethodGet, Path: "/swagger"},
{Method: http.MethodGet, Path: "/swagger/", Prefix: true},
{Method: http.MethodGet, Path: "/.well-known/localai.json"},
匿名引导路由
为支撑健康检查、凭据获取与登录 UI,LocalAI 允许以下请求匿名通过,但这不会让 API 的其他部分变得公开:
- 健康检查:
GET /healthz与GET /readyz - 认证状态与 token 登录:
GET /api/auth/status与POST /api/auth/token-login - 本地注册与登录:
POST /api/auth/register与POST /api/auth/login - GitHub OAuth:
GET /api/auth/github/login与GET /api/auth/github/callback - OIDC:
GET /api/auth/oidc/login与GET /api/auth/oidc/callback - 认证预检请求:
/api/auth/下的OPTIONS - SPA 壳路由:
GET /、HEAD /,以及/app、/browse、/login、/invite/*、/explorer的GET;/app/与/browse/子路径也通过GET放行 - SPA 静态资源:
GET /favicon.svg,以及/assets/、/locales/、/static/下的GET - 品牌读取:
GET /api/branding与/branding/asset/下的GET;品牌修改仍需管理员凭据
上述规则完整对应 public_routes.go 中 publicRouteRegistry 的三个分组(Health / Authentication bootstrap / SPA / Assets / Branding),可作为排查「为什么某个路由免鉴权」的第一手依据。
需要凭据的路由
在没有显式部署豁免的前提下,其余所有路由都需要凭据,包括 GET /version、全部模型与后端管理 API、全部推理路由。MCP 与 moderation 别名路由同样是私有的:
POST /v1/mcp/chat/completions、POST /mcp/v1/chat/completions、POST /mcp/chat/completionsPOST /v1/moderations与POST /moderations
生成内容的输出 URL 同样受保护,覆盖 /generated-audio/、/generated-images/、/generated-videos/ 与 /generated-3d/ 下的所有 URL。
显式部署豁免(overrides)
嵌入式部署可以向 ApplicationConfig.PathWithoutAuth 追加路径前缀。命中前缀后,该前缀下的所有 HTTP 方法都会绕过全局认证(但路由自身注册的路由级鉴权仍会执行)。因此应尽量将豁免范围收窄,默认列表为空。
PathWithoutAuth 的校验在 middleware.go 的 isExemptPath 中通过字符串前缀匹配完成,配置结构定义见 application_config.go,且测试 middleware_test.go 明确验证了「调用方提供的自定义前缀」会被保留。
另有两个 legacy flag 提供仅 GET 的兼容性豁免(仅在配置了 legacy API Key 时生效):
LOCALAI_DISABLE_API_KEY_REQUIREMENT_FOR_HTTP_GET=true:开启该豁免开关;LOCALAI_HTTP_GET_EXEMPTED_ENDPOINTS:设置被豁免的GET路由正则表达式列表。
在 core/cli/run.go 中可以看到两个 flag 的定义与默认正则:
DisableApiKeyRequirementForHttpGet bool `env:"LOCALAI_DISABLE_API_KEY_REQUIREMENT_FOR_HTTP_GET" default:"false" help:"If true, a valid API key is not required to issue GET requests to portions of the web ui..." group:"hardening"`
HttpGetExemptedEndpoints []string `env:"LOCALAI_HTTP_GET_EXEMPTED_ENDPOINTS" default:"^/$,^/app(/.*)?$,^/browse(/.*)?$,^/login/?$,^/explorer/?$,^/assets/.*$,^/static/.*$,^/swagger.*$" help:"..." group:"hardening"`
默认豁免正则覆盖 /、/app、/browse、/login、/explorer、/assets、/static 与 /swagger 的 GET 请求,恰好对应 Web UI 的 SPA 静态资源。端点表达式只有在开启 LOCALAI_DISABLE_API_KEY_REQUIREMENT_FOR_HTTP_GET 时才生效,自定义表达式需要谨慎审查,因为可能把受保护的读取接口暴露出去。中间件对该豁免的执行逻辑见 middleware.go,仅当 Method == GET 且匹配任意正则时才放行。
用户认证系统
用户认证系统提供以下能力:
- 用户账户:email、姓名与头像
- 基于角色的访问控制(RBAC):admin 与 user 两级
- 基于会话的认证:安全 Cookie
- OAuth 登录(GitHub)与 OIDC 单点登录(Keycloak、Google、Okta、Authentik 等)
- 按用户的 API Key:面向程序化访问
- 管理路由门禁:管理端点仅限 admin
- 按用户用量追踪:token 消耗指标
启用认证
设置 LOCALAI_AUTH=true,或提供 GitHub OAuth Client ID / OIDC Client ID(会自动启用认证):
# 启用并使用 SQLite(默认,存储于 {DataPath}/database.db)
LOCALAI_AUTH=true localai run
# 启用并使用 GitHub OAuth
GITHUB_CLIENT_ID=your-client-id \
GITHUB_CLIENT_SECRET=your-client-secret \
LOCALAI_BASE_URL=http://localhost:8080 \
localai run
# 启用并使用 OIDC 提供商(如 Keycloak)
LOCALAI_OIDC_ISSUER=https://keycloak.example.com/realms/myrealm \
LOCALAI_OIDC_CLIENT_ID=your-client-id \
LOCALAI_OIDC_CLIENT_SECRET=your-client-secret \
LOCALAI_BASE_URL=http://localhost:8080 \
localai run
# 启用并使用 PostgreSQL
LOCALAI_AUTH=true \
LOCALAI_AUTH_DATABASE_URL=postgres://user:pass@host/dbname \
localai run
配置项参考
| 环境变量 | 默认值 | 说明 |
|---|---|---|
LOCALAI_AUTH |
false |
启用用户认证与授权 |
LOCALAI_AUTH_DATABASE_URL |
{DataPath}/database.db |
数据库 URL——PostgreSQL 用 postgres://...,文件路径则使用 SQLite(同时兼容别名 DATABASE_URL) |
GITHUB_CLIENT_ID |
GitHub OAuth App Client ID(设置后自动启用认证) | |
GITHUB_CLIENT_SECRET |
GitHub OAuth App Client Secret | |
LOCALAI_OIDC_ISSUER |
OIDC issuer URL,用于自动发现(如 https://accounts.google.com) |
|
LOCALAI_OIDC_CLIENT_ID |
OIDC Client ID(设置后自动启用认证) | |
LOCALAI_OIDC_CLIENT_SECRET |
OIDC Client Secret | |
LOCALAI_BASE_URL |
OAuth 回调使用的对外 Base URL(如 http://localhost:8080) |
|
LOCALAI_ADMIN_EMAIL |
登录时自动提升为 admin 角色的邮箱地址 | |
LOCALAI_REGISTRATION_MODE |
approval |
注册模式:open、approval 或 invite |
LOCALAI_DISABLE_LOCAL_AUTH |
false |
禁用本地 email/密码注册与登录(用于仅 OAuth/OIDC 的部署) |
源码层面,这些变量统一由 core/cli/run.go 的 auth 配置组声明,并映射进 ApplicationConfig.Auth(类型 AuthConfig,见 core/config/application_config.go)。其中还包含两个文档未在表格中列出的进阶项:LOCALAI_AUTH_HMAC_SECRET(API Key / 会话 / 邀请码的 HMAC 哈希密钥,留空自动生成)与 LOCALAI_DEFAULT_API_KEY_EXPIRY(API Key 默认过期时长,如 90d、1y,空表示不过期)。
网络存储警告。 基于文件的 SQLite 依赖 POSIX 文件锁,在网络文件系统上(SMB/CIFS/NFS,例如 Azure Files / Azure Container Apps 共享卷)并不可靠。此类存储上的认证数据库可能以
database is locked报错导致迁移失败。当数据目录位于共享/网络存储时请改用 PostgreSQL(LOCALAI_AUTH_DATABASE_URL=postgres://...),或将database.db放在本地卷上。
需要说明:
LOCALAI_REGISTRATION_MODE在官方文档中的默认记录为approval;在 core/cli/run.go 中该 flag 的帮助文本将open声明为default值,实际生效语义以 core/http/auth 中注册流程对三种模式(含 approval 待审批)的处理为准,生产部署建议显式设置该变量。
禁用本地认证
若希望强制仅使用 OAuth/OIDC 登录,阻止用户以 email/密码注册或登录,可设置 LOCALAI_DISABLE_LOCAL_AUTH=true(或传 --disable-local-auth):
# 仅 OAuth 的配置(无 email/密码)
LOCALAI_DISABLE_LOCAL_AUTH=true \
GITHUB_CLIENT_ID=your-client-id \
GITHUB_CLIENT_SECRET=your-client-secret \
LOCALAI_BASE_URL=http://localhost:8080 \
localai run
禁用后:
- 登录页不显示 email/密码表单(UI 通过
/api/auth/status返回的providers列表判断); POST /api/auth/register返回403 Forbidden;POST /api/auth/login返回403 Forbidden;- OAuth/OIDC 登录不受影响,正常工作。
角色
系统只有两种角色:
- Admin(管理员):可访问全部端点,包括模型管理、后端配置、系统设置、追踪、agent 与用户管理。
- User(普通用户):仅可访问推理端点——chat completions、embeddings、图像/视频/音频生成、TTS、MCP chat,以及自己的用量统计。
第一个登录的用户会被自动授予 admin 角色。 后续用户可通过管理员的用户管理 API 提升为 admin,或在启动时用 LOCALAI_ADMIN_EMAIL 指定其邮箱实现自动提升。
这一逻辑与源码 core/http/auth/roles.go 的 AssignRole 完全对应:在创建用户的同一事务中统计用户总数,第一条记录(count == 0)返回 RoleAdmin;否则若邮箱与 adminEmail 匹配(大小写不敏感)也返回 RoleAdmin;其余返回 RoleUser。同一文件中还定义了 StatusActive = "active"、StatusPending = "pending"、StatusDisabled = "disabled" 三种用户状态。
从数据库模型 core/http/auth/models.go 可以看到 User 结构不仅存角色与状态,还保存 Provider(local / github / oidc / agent-worker,见常量定义)与 PasswordHash(bcrypt 哈希,OAuth-only 用户为空)。
注册模式
| 模式 | 说明 |
|---|---|
open |
任何人都能注册并立即生效 |
approval |
新用户处于 pending 状态,等待管理员批准;若注册时提供了有效邀请码则立即激活(跳过审批等待)。(默认) |
invite |
注册必须使用管理员生成的邀请链接;没有邀请则注册被拒绝 |
OAuth 回调中的新用户创建逻辑(core/http/auth/oauth.go)在事务内按模式处理:invite 模式无邀请码直接报 invite_required,邀请码无效报 invalid_invite;approval 模式则创建 pending 用户。
邀请链接
管理员可在 Web UI 的 Users → Invites 标签页生成单次使用、限时的邀请链接,或通过 API 创建:
# 创建邀请链接(默认 7 天过期)
curl -X POST http://localhost:8080/api/auth/admin/invites \
-H "Authorization: Bearer <admin-key>" \
-H "Content-Type: application/json" \
-d '{"expiresInHours": 168}'
# 列出全部邀请
curl http://localhost:8080/api/auth/admin/invites \
-H "Authorization: Bearer <admin-key>"
# 撤销未使用的邀请
curl -X DELETE http://localhost:8080/api/auth/admin/invites/<invite-id> \
-H "Authorization: Bearer <admin-key>"
把邀请 URL(/invite/<code>)分享给用户,打开后注册表单会预填邀请码。LocalAI 仅在用户提交注册时才校验邀请码。邀请码单次使用、不可复用;过期或已用邀请会被拒绝。
在 core/http/auth/models.go 中,InviteCode 只保存邀请码的 HMAC-SHA256 哈希(Code 字段,唯一索引)与用于展示的前缀 CodePrefix,原始邀请码不落库——与用户 API Key 的存储策略一致。
对于 GitHub OAuth,邀请码以查询参数附加在登录 URL 上(/api/auth/github/login?invite_code=<code>),并在 OAuth 流程期间存入 Cookie(见 oauth.go);OIDC 的邀请码机制与 GitHub OAuth 相同(/api/auth/oidc/login?invite_code=<code>)。
仅管理员端点
启用认证后,以下端点要求 admin 角色(中间件为 middleware.go 的 RequireAdmin,返回 401 Unauthorized / 403 Forbidden 的标准错误结构):
模型与后端管理:
GET /api/models、POST /api/models/install/*、POST /api/models/delete/*GET /api/backends、POST /api/backends/install/*、POST /api/backends/delete/*GET /api/operations、POST /api/operations/*/cancel、POST /api/operations/*/pause、POST /api/operations/*/dismissGET /api/operations/history、DELETE /api/operations/historyGET /models/available、GET /models/galleries、GET /models/jobs/*GET /backends、GET /backends/available、GET /backends/galleries
系统与监控:
GET /api/traces、GET /api/traces/summary、GET /api/traces/{id}、POST /api/traces/clearGET /api/backend-traces、GET /api/backend-traces/{id}、POST /api/backend-traces/clearGET /api/backend-logs/*、POST /api/backend-logs/*/clearGET /api/resources、GET /api/settings、POST /api/settingsGET /system、GET /backend/monitor、POST /backend/shutdown、POST /backend/load
P2P:
GET /api/p2p/*
Agents 与 Jobs:
- 所有
/api/agents/*端点 - 所有
/api/agent/tasks/*与/api/agent/jobs/*端点
所有已认证用户可访问的端点:
POST /v1/chat/completions、POST /v1/embeddings、POST /v1/completionsPOST /v1/images/generations、POST /v1/audio/*、POST /tts、POST /vad、POST /videoGET /v1/models、POST /v1/tokenize、POST /v1/detokenize、POST /v1/detectionPOST /v1/mcp/chat/completions、POST /v1/messages、POST /v1/responsesPOST /stores/*、GET /api/cors-proxyGET /version、GET /api/features、GET /metricsGET /api/auth/usage(自己的用量数据)
除纯角色门禁外,源码还提供了更细粒度的授权中间件:RequireFeature 与基于 RouteFeatureRegistry 的 RequireRouteFeature 检查用户功能开关(agents、skills、collections、mcp_jobs 等),RequireModelAccess 依据 ModelAllowlist 校验用户可用的模型(从路径参数/查询参数/JSON 体/form 值中提取模型名,见 extractModelFromRequest),而 RequireQuota 则在推理路由上强制按用户配额并返回 429 + Retry-After。权限数据模型 UserPermission / PermissionMap / ModelAllowlist 均定义在 models.go。
Web UI 访问控制
启用认证后,React UI 侧边栏会根据用户角色动态显示/隐藏区块:
- 所有用户可见:Home、Chat、Images、Video、TTS、Sound、Talk、Usage、API docs 链接
- 管理员额外可见:Models,Build 控制台(Agents、Skills、Memory、Jobs、Training、Recognition),Operate 控制台(Backends、Activity、Nodes、Usage、Traces、Users、Middleware、Settings)
仅管理员的页面在路由层也有保护——直接访问管理 URL 时,非管理员会被重定向回首页。
GitHub OAuth 配置
- 在 GitHub Settings → Developer settings → OAuth Apps → New OAuth App 创建 OAuth App;
- 将 Authorization callback URL 设为
{LOCALAI_BASE_URL}/api/auth/github/callback; - 设置环境变量
GITHUB_CLIENT_ID与GITHUB_CLIENT_SECRET; - 将
LOCALAI_BASE_URL设为可公开访问的 URL。
OIDC 配置
任何兼容 OIDC 的身份提供商均可用于单点登录,包括 Keycloak、Google、Okta、Authentik、Azure AD 等。
步骤:
- 在 OIDC 提供商处创建客户端/应用;
- 将重定向 URL 设为
{LOCALAI_BASE_URL}/api/auth/oidc/callback; - 设置三个环境变量:
LOCALAI_OIDC_ISSUER、LOCALAI_OIDC_CLIENT_ID、LOCALAI_OIDC_CLIENT_SECRET。
LocalAI 使用 OIDC 自动发现机制(/.well-known/openid-configuration),并请求标准 scope:openid、profile、email。签名算法等安全细节在 core/http/auth/oauth_signing_algs_test.go 有对应测试覆盖。
提供商示例:
# Keycloak
LOCALAI_OIDC_ISSUER=https://keycloak.example.com/realms/myrealm
# Google
LOCALAI_OIDC_ISSUER=https://accounts.google.com
# Authentik
LOCALAI_OIDC_ISSUER=https://authentik.example.com/application/o/localai/
# Okta
LOCALAI_OIDC_ISSUER=https://your-org.okta.com
用户 API Key
已认证用户可以创建个人 API Key 用于程序化访问:
# 创建 API Key(需要会话认证)
curl -X POST http://localhost:8080/api/auth/api-keys \
-H "Cookie: session=<session-id>" \
-H "Content-Type: application/json" \
-d '{"name": "My Script Key"}'
用户 API Key 继承创建者角色:admin 创建的 Key 拥有管理员权限,普通用户创建的 Key 只有用户级权限。从数据模型看(models.go),API Key 只存储 KeyHash(HMAC-SHA256,uniqueIndex)与 KeyPrefix(前 8 位用于展示),并可选 ExpiresAt 过期时间与 LastUsed 最近使用时间戳,原始 Key 永不落库。创建/列出/撤销 Key 的路由实现位于 core/http/routes/auth.go,撤销后立即失效。
中间件中的凭据解析顺序(middleware.go tryAuthenticate)值得关注,它决定了同名凭据的优先级:
sessionCookie → 会话校验(Web UI);Authorization: Bearer <token>→ 先尝试作为会话 token,再尝试作为命名 API Key;x-api-key/xi-api-key请求头 → 命名 API Key;tokenCookie → 命名 API Key。
会话记录本身在 models.go 中被定义为「会话 token 的 HMAC-SHA256 哈希」(主键),并带过期时间与轮换时间戳 RotatedAt;Cookie 会话还支持会话轮换(MaybeRotateSession)。
Auth API 端点一览
| 方法 | 端点 | 说明 | 所需认证 |
|---|---|---|---|
GET |
/api/auth/status |
认证状态、当前用户、可用 providers | 无 |
POST |
/api/auth/token-login |
用用户或 legacy API Key 换取浏览器会话 | 无 |
POST |
/api/auth/register |
用 email 与密码注册 | 无 |
POST |
/api/auth/login |
用 email 与密码登录 | 无 |
GET |
/api/auth/github/login |
开始 GitHub OAuth | 无 |
GET |
/api/auth/github/callback |
GitHub OAuth 回调(内部) | 无 |
GET |
/api/auth/oidc/login |
开始 OIDC 登录 | 无 |
GET |
/api/auth/oidc/callback |
OIDC 回调(内部) | 无 |
POST |
/api/auth/logout |
结束会话 | 是 |
GET |
/api/auth/me |
当前用户信息 | 是 |
POST |
/api/auth/api-keys |
创建 API Key | 是 |
GET |
/api/auth/api-keys |
列出当前用户的 API Key | 是 |
DELETE |
/api/auth/api-keys/:id |
撤销 API Key | 是 |
GET |
/api/auth/usage |
用户自己的用量统计 | 是 |
GET |
/api/auth/usage/sources |
用户自己按 API Key / 来源的明细 | 是 |
GET |
/api/auth/admin/users |
列出全部用户 | 管理员 |
PUT |
/api/auth/admin/users/:id/role |
修改用户角色 | 管理员 |
DELETE |
/api/auth/admin/users/:id |
删除用户 | 管理员 |
GET |
/api/auth/admin/usage |
全部用户的用量统计 | 管理员 |
GET |
/api/auth/admin/usage/sources |
全部用户按 API Key / 来源的明细 | 管理员 |
POST |
/api/auth/admin/invites |
创建邀请链接 | 管理员 |
GET |
/api/auth/admin/invites |
列出全部邀请 | 管理员 |
DELETE |
/api/auth/admin/invites/:id |
撤销未使用的邀请 | 管理员 |
全部端点的路由注册集中在 core/http/routes/auth.go,其中管理员组通过 auth.RequireAdmin() 统一挂载。另外该文件还在认证端点上实现了按 IP 的简单限流器(rateLimiter,见 auth.go),用于抵御暴力登录尝试。
用量追踪
启用认证后,LocalAI 会自动记录推理端点的按用户 token 用量。用量数据包括:
- 每次请求的 prompt tokens、completion tokens 与 total tokens
- 使用的 模型 与被调用的 端点
- 请求耗时
- 用于时间序列聚合的 时间戳
查看用量
用量可通过 Web UI 的 Usage 页面(所有已认证用户可见)或 API 查看:
# 查看自己的用量(默认最近 30 天)
curl http://localhost:8080/api/auth/usage?period=month \
-H "Authorization: Bearer <key>"
# 管理员:查看全部用户的用量
curl http://localhost:8080/api/auth/admin/usage?period=week \
-H "Authorization: Bearer <admin-key>"
# 管理员:按指定用户过滤
curl "http://localhost:8080/api/auth/admin/usage?period=month&user_id=<user-id>" \
-H "Authorization: Bearer <admin-key>"
Period 取值:
day— 最近 24 小时,按小时分桶week— 最近 7 天,按天分桶month— 最近 30 天,按天分桶(默认)all— 全部时间,按月分桶
响应格式:
{
"usage": [
{
"bucket": "2026-03-18",
"model": "gpt-4",
"user_id": "abc-123",
"user_name": "Alice",
"prompt_tokens": 1500,
"completion_tokens": 800,
"total_tokens": 2300,
"request_count": 12
}
],
"totals": {
"prompt_tokens": 1500,
"completion_tokens": 800,
"total_tokens": 2300,
"request_count": 12
}
}
用量仪表盘
Web UI 的 Usage 页面提供:
- 周期选择器 — 在 day / week / month / 全部时间之间切换
- 汇总卡片 — 总请求数、prompt tokens、completion tokens、total tokens
- By Model 表格 — 按模型明细,带可视化用量条
- By User 表格(仅管理员)— 跨所有模型的按用户明细
- Sources 标签页 — 按 API Key 与来源的明细(见下)
按 API Key 的明细
Usage 页面的 Sources 标签页在同一份数据上提供第三个维度:按 API Key 与请求来源切分的流量。追踪三种来源类别:
- API key — 使用命名用户 API Key 认证的请求(
Authorization: Bearer lai-...、x-api-key或tokenCookie)。每个 Key 会显示其标签(写入时快照,因此已撤销的 Key 仍会显示原始名称)。 - Web UI — 使用浏览器会话 Cookie 认证的请求。
- Legacy — 使用环境变量配置的
LOCALAI_API_KEY认证的请求。仅管理员可见。
Sources 标签页对每个已认证用户可见。非管理员只能看到自己的 Key 加上自己的 Web UI 流量(legacy 在服务端被过滤);管理员能看到所有用户的每个 Key。
页面布局包括:
- 来源混合条(source mix ribbon):三类来源的百分比切分
- Top-N + Other 堆叠时间图:按 total tokens 取前 7 个来源,其余归入 Other
- 可搜索、可排序的表格:列出每个 Key 以及 Web UI 与 Legacy 伪行;点击某行可将图表过滤到该来源
端点
| 方法 | 路径 | 认证 | 说明 |
|---|---|---|---|
GET |
/api/auth/usage/sources |
本人 | 调用者按来源的明细;不含 legacy |
GET |
/api/auth/admin/usage/sources |
管理员 | 全部用户按来源的明细;支持 user_id 与 api_key_id 过滤;包含 legacy |
两个端点都接受与 /api/auth/usage 相同的 period 参数(day、week、month、all)。
# 最近一周自己的按来源用量
curl "http://localhost:8080/api/auth/usage/sources?period=week" \
-H "Authorization: Bearer <key>"
# 管理员:跨全部用户过滤到单个 API Key
curl "http://localhost:8080/api/auth/admin/usage/sources?period=month&api_key_id=<key-id>" \
-H "Authorization: Bearer <admin-key>"
响应形状:
{
"buckets": [
{ "bucket": "2026-05-19", "source": "apikey",
"api_key_id": "uuid", "api_key_name": "ci-runner",
"total_tokens": 20000, "request_count": 142, "...": "..." },
{ "bucket": "2026-05-19", "source": "web",
"total_tokens": 300, "request_count": 11, "...": "..." }
],
"totals": {
"by_source": {
"apikey": { "tokens": 1234567, "requests": 8420 },
"web": { "tokens": 92000, "requests": 211 }
},
"by_key": [
{ "api_key_id": "uuid", "api_key_name": "ci-runner",
"tokens": 2100000, "requests": 8420,
"last_used": "2026-05-20T12:34:56Z" }
],
"grand_total": { "tokens": 1334777, "requests": 8645 }
},
"truncated": false
}
by_key 列表在服务端按 tokens 降序排序,最多返回 200 条。当还有更多 Key 符合条件时,响应会设置 "truncated": true,以便 UI 展示提示。
旧数据迁移
该功能上线前记录的用量行没有 source 字段。启动时 InitDB 会为它们回填:当用户 ID 为合成的 legacy-api-key 时回填为 legacy,其余回填为 web。迁移是幂等的,升级后已有聚合数据依然正确。
从实现上看,用量记录、按来源切分与聚合逻辑封装在 core/http/auth/usage.go,并有 usage_test.go 覆盖查询与聚合行为;请求来源由中间件写入 Echo context(auth_source),见 middleware.go 中的上下文键定义与 middleware.go 对 legacy 来源的设置。
组合认证模式
Legacy API Key 与用户认证系统可以同时启用。两者都配置时:
- 先检查用户会话与用户 API Key;
- 再回退检查 Legacy API Key——匹配时授予 admin 级访问权限;
- 这为从共享 API Key 平滑迁移到按用户账户提供了路径。
对应 middleware.go 的中间件主流程:第 2 步先尝试用户体系认证(tryAuthenticate),第 3 步在未认证且存在 legacy Key 时用 isValidLegacyKey 回退并构造合成 admin。两种模式都没启用时(!authEnabled && !hasLegacyKeys),请求直接透传。
构建要求
用户认证系统需要 CGO 以支持 SQLite,并通过 auth build tag 启用——Docker 构建默认已包含该 tag。
# 从源码构建并开启 auth 支持
GO_TAGS=auth make build
# 或直接用 go build
go build -tags auth ./...
仓库 Makefile 中 GO_TAGS?= 支持通过环境变量注入 tag,构建命令会将其透传给 go build -tags "$(GO_TAGS)"。默认 Dockerfile 设置了 GO_TAGS="auth",因此所有 Docker 镜像都带 auth 支持。从源码构建时若不带 auth tag,设置 LOCALAI_AUTH=true 不会生效——系统会在无认证状态下运行。
源码中这种「编译期选择」体现在数据库适配层:core/http/auth/db_sqlite.go 与 core/http/auth/db_nosqlite.go 分别对应启用/禁用 SQLite 的实现,配合 //go:build auth 约束选择编译单元;相关测试覆盖见 db_sqlite_test.go 与 middleware_test.go。
小结与源码导航
- 简单共享密钥防护:用
LOCALAI_API_KEY,但注意它等同管理员; - 生产级多用户治理:
LOCALAI_AUTH=true+ PostgreSQL(网络存储必选)+ 按需接入 GitHub OAuth / OIDC + invite 注册模式; - 牢记「默认私有、白名单放行」模型,
PathWithoutAuth与 legacy GET 豁免应尽量收窄; - 想让用户自助管理密钥与用量,先部署 Web UI 并用
token-login/ 会话让账户体系跑通,再推广用户 API Key。
若想深入实现细节,可按以下路径阅读仓库源码:
- 鉴权中间件主流程(凭据解析顺序、legacy 回退、GET 豁免、admin 门禁):core/http/auth/middleware.go
- 公开/引导/SPA 路由白名单注册表:core/http/auth/public_routes.go
- 用户/会话/API Key/邀请码/权限数据模型:core/http/auth/models.go
- 角色分配与首用户管理员逻辑:core/http/auth/roles.go
- OAuth/OIDC 回调、邀请码 Cookie 传递:core/http/auth/oauth.go
- Auth 相关 REST 路由与限流器:core/http/routes/auth.go
- 环境变量声明与默认值:core/cli/run.go
- 配置结构(
AuthConfig等):core/config/application_config.go
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0629
MiniCPM5-2BMiniCPM5-2B 是一款面向端侧、本地部署和资源受限场景的 2B 稠密 Transformer,能够达到同尺寸开源模型 SOTA 水平。Markdown00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
HivisionIDPhotos⚡️HivisionIDPhotos: a lightweight and efficient AI ID photos tools. 一个轻量级的AI证件照制作算法。Python07
DragonOSDragonOS is an operating system developed from scratch using Rust, with Linux compatibility. It is designed for **Serverless** scenarios. 使用Rust从0自研内核,具有Linux兼容性的操作系统,面向云计算Serverless场景而设计。Rust00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00