OpenWebUi-安全与认证配置

3.9. 安全与认证配置

这一章讲的是“谁能登录、谁能调用 API、用户身份如何同步、会话如何保活”。

如果你是个人用户,这部分很多配置可以先不动;但只要进入团队协作、公司内网、对接脚本或自动化系统,这一章就会变成必修课。

在开始之前,先解释几个经常会混在一起的术语:

  • 认证(Authentication):证明“你是谁”。例如账号密码登录、LDAP 登录、Google 登录。
  • 授权(Authorization):决定“你能做什么”。例如能不能看某个模型、能不能导出模型、能不能管理工具。
  • API Key:发给程序用的密钥,适合脚本、集成平台、外部服务调用 Open WebUI API。
  • LDAP:企业常见的目录服务协议,很多公司的 AD(Active Directory)也兼容这套方式。
  • OAuth / OIDC:第三方登录体系。OAuth 偏“授权”,OIDC 是建立在 OAuth 之上的“身份登录”标准。
  • SCIM:自动开通和回收账号的标准,不负责“登录”,而负责“账号生命周期同步”。

API 密钥认证

这是什么

Open WebUI 支持为用户生成 API Key,让外部脚本、自动化流程、内部平台、工作流引擎通过 HTTP API 访问它。

典型场景包括:

  • 用 Python、Node.js 或 Shell 脚本调用聊天接口
  • 用企业内部系统转发请求到 Open WebUI
  • 给工作流平台、Bot、自动化任务提供一个稳定认证方式

它和“浏览器登录 Cookie”不是一回事:

  • 浏览器登录主要给人用
  • API Key 主要给程序用

如何启用

管理员面板 → 设置 → 通用 → 启用 API Key

启用后,用户就可以在自己的账号设置中创建 API Key。

当前版本除了总开关外,还支持 API Key Endpoint Restrictions,也就是“限制 API Key 只能访问哪些接口”。这个能力很重要,因为它可以避免把一把过大的密钥发给外部系统。

用户如何生成密钥

点击头像 → 设置 → 账号 → API 密钥 → 点击“创建新密钥”

创建后要立即保存,因为这类密钥通常不会反复明文展示。

调用示例

1
2
3
4
curl -X POST http://localhost:3000/api/chat \
-H "Authorization: Bearer sk-..." \
-H "Content-Type: application/json" \
-d '{"message": "Hello"}'

官方建议理解

官网当前的方向不是“默认把所有接口都敞开”,而是:

  • 可以启用 API Key
  • 可以进一步启用接口级限制
  • 在可信内网环境里可以更宽松,在生产环境则建议更严格

我的管理方式

在我的环境里,API Key 主要给“程序”而不是“人”使用,所以我通常这样管理:

  1. 浏览器用户走正常登录,不给所有人默认要求 API Key。
  2. 只有确实需要脚本调用的账号,才生成 API Key。
  3. 给外部系统时,优先启用接口限制,不给过大的权限面。
  4. 定期清理不再使用的密钥,避免历史脚本长期持有可用凭证。
  5. 如果只是临时测试,我会单独创建测试用密钥,不和正式环境长期复用。

LDAP 集成

这是什么

LDAP 可以理解为“公司统一通讯录 / 账号目录”的标准接口。

如果你所在的组织已经有 AD、OpenLDAP 或其他企业目录系统,那么 Open WebUI 可以直接对接这个目录,让员工使用公司账号登录,而不是在 Open WebUI 里重复创建本地账号。

适合谁

  • 公司内网部署
  • 已有统一身份管理
  • 希望账号、邮箱、用户名来自企业目录

个人部署、小团队、临时测试环境,通常不需要上 LDAP。

官方配置思路

LDAP 推荐先通过环境变量初始化,然后在管理员面板里继续维护。官网特别强调一件事:

如果启用了持久化配置(默认就是),很多环境变量只在第一次启动时读入;后续修改通常要在管理后台里改,而不是只改 docker-compose

这点非常重要,也是很多人“明明改了环境变量,怎么界面没变”的根源。

当前版本常见环境变量

1
2
3
4
5
6
7
8
9
10
11
12
environment:
- ENABLE_LDAP=true
- LDAP_SERVER_LABEL=OpenLDAP
- LDAP_SERVER_HOST=ldap.example.com
- LDAP_SERVER_PORT=389
- LDAP_ATTRIBUTE_FOR_MAIL=mail
- LDAP_ATTRIBUTE_FOR_USERNAME=uid
- LDAP_APP_DN=cn=admin,dc=example,dc=org
- LDAP_APP_PASSWORD=your_password
- LDAP_SEARCH_BASE=dc=example,dc=org
- LDAP_SEARCH_FILTER=(uid=%(user)s)
- LDAP_USE_TLS=true

你会发现,这一版和很多旧博客里写的 LDAP_SERVER_URLLDAP_BIND_DN 之类名字不完全一样。写文档时一定要以当前版本为准。

参数解释

参数说明
ENABLE_LDAP是否启用 LDAP 登录
LDAP_SERVER_LABEL在界面里显示的 LDAP 名称
LDAP_SERVER_HOST / LDAP_SERVER_PORTLDAP 服务器地址和端口
LDAP_ATTRIBUTE_FOR_MAIL从 LDAP 条目里取邮箱的字段
LDAP_ATTRIBUTE_FOR_USERNAME从 LDAP 条目里取用户名的字段
LDAP_APP_DN / LDAP_APP_PASSWORD用于查询目录的服务账号
LDAP_SEARCH_BASE搜索用户的根 DN
LDAP_SEARCH_FILTER登录时查找用户的过滤器
LDAP_USE_TLS是否启用 TLS / StartTLS

正确的排错顺序

不要一上来就怀疑 Open WebUI。

官网推荐的思路是:

  1. 先验证 LDAP 服务器本身通不通
  2. 再验证用户条目能不能查到
  3. 最后才看 Open WebUI 配置

例如:

1
2
3
ldapsearch -x -H ldap://ldap.example.com:389 \
-D "cn=admin,dc=example,dc=org" -w your_password \
-b "dc=example,dc=org" "(uid=jdoe)"

如果这一步都查不到用户,就不要继续在 WebUI 里盲改了。

我的管理方式

我自己的判断规则很简单:

  • 如果是企业内部正式使用,并且员工已经有统一账号体系,我会优先接 LDAP。
  • 如果只是个人站点、朋友共用、测试环境,我不会为了“看起来企业化”硬上 LDAP。
  • 上 LDAP 后,我会尽量让用户名、邮箱、显示名都来自目录系统,避免 Open WebUI 自己成为第二套主数据来源。

OAuth / OIDC / SSO 集成

这是什么

这一组就是“第三方登录”。

最常见的使用方式是:

  • 用 Google 账号登录
  • 用 Microsoft / Entra ID 账号登录
  • 用 GitHub 账号登录
  • 用企业自己的 OIDC 服务登录

如果说 LDAP 更像“公司内网目录登录”,那 OAuth / OIDC 更像“现代网站统一登录”。

先说结论:本地开发完全可以配

而且你现在这个仓库已经改成了:

  • docker-compose.local.yaml 自动读取 .env.local
  • 本地端口由 OPEN_WEBUI_PORT 控制
  • Google、Microsoft、GitHub 三家都可以直接写在 .env.local

所以对读者来说,最容易跟做的方式就是:

  1. 去各自官网创建 OAuth 应用
  2. 把回调地址填成 Open WebUI 本地回调
  3. 把拿到的密钥填进 .env.local
  4. 重启本地容器

本地统一配置模板

如果你的本地端口是 3000,那么 .env.local 先这样写:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
OPEN_WEBUI_PORT=3000
WEBUI_URL=http://localhost:3000
ENABLE_OAUTH_SIGNUP=true
OAUTH_MERGE_ACCOUNTS_BY_EMAIL=true

GOOGLE_CLIENT_ID=
GOOGLE_CLIENT_SECRET=
GOOGLE_REDIRECT_URI=http://localhost:3000/oauth/google/login/callback

MICROSOFT_CLIENT_ID=
MICROSOFT_CLIENT_SECRET=
MICROSOFT_CLIENT_TENANT_ID=common
MICROSOFT_REDIRECT_URI=http://localhost:3000/oauth/microsoft/login/callback

GITHUB_CLIENT_ID=
GITHUB_CLIENT_SECRET=
GITHUB_CLIENT_REDIRECT_URI=http://localhost:3000/oauth/github/login/callback

写完后执行:

1
docker compose -f docker-compose.local.yaml up -d --build

如果你的端口不是 3000,例如 5050,那上面所有 localhost:3000 都要统一改成 localhost:5050

适用场景

如果你想直接使用 Google 账号登录,这是最常见的一种配置。

第 1 步:去 Google 官方后台创建应用

访问:

https://console.cloud.google.com/apis/credentials

进入后:

  1. 创建或选择一个 Project
  2. 打开 APIs & Services
  3. 进入 Credentials(凭证)
  4. 点击 Create Credentials
  5. 选择 OAuth client ID
  6. Application type 选择 Web application

第 2 步:登记回调地址

Authorized redirect URIs 中填写:

1
http://localhost:3000/oauth/google/login/callback

第 3 步:把拿到的密钥填入 .env.local

1
2
3
GOOGLE_CLIENT_ID=你的 Google Client ID
GOOGLE_CLIENT_SECRET=你的 Google Client Secret
GOOGLE_REDIRECT_URI=http://localhost:3000/oauth/google/login/callback

第 4 步:重启本地 Open WebUI

1
docker compose -f docker-compose.local.yaml up -d --build

注意事项

  • Google 开发环境允许 http://localhost
  • 回调地址必须和后台里登记的 URI 完全一致
  • 如果你改了端口,Google 后台也要一起改

适用场景

如果用户本身就在 Microsoft 365、Entra ID、企业账号体系里,这一项非常常见。

第 1 步:去 Microsoft Entra 后台注册应用

访问:

https://portal.azure.com/

进入后:

  1. 打开 Microsoft Entra ID
  2. 进入 App registrations
  3. 点击 New registration
  4. 创建一个应用

第 2 步:配置回调地址

Authentication 页面里添加:

1
http://localhost:3000/oauth/microsoft/login/callback

第 3 步:生成 Secret 并写入 .env.local

Certificates & secrets 中生成一个 Client secret,然后填入:

1
2
3
4
MICROSOFT_CLIENT_ID=你的 Microsoft Client ID
MICROSOFT_CLIENT_SECRET=你的 Microsoft Client Secret
MICROSOFT_CLIENT_TENANT_ID=common
MICROSOFT_REDIRECT_URI=http://localhost:3000/oauth/microsoft/login/callback

如果你只允许某一个租户登录,把 common 改成你的实际 Tenant ID 即可。

第 4 步:重启本地 Open WebUI

1
docker compose -f docker-compose.local.yaml up -d --build

注意事项

  • Microsoft 也支持本地 localhost 回调
  • 本地开发可以先用 common
  • 正式环境建议单独使用正式域名和正式应用注册

适用场景

如果你的用户本身大量使用 GitHub,这是最容易理解、也最容易测试的一项。

第 1 步:去 GitHub Developer Settings 创建 OAuth App

访问:

https://github.com/settings/developers

进入后:

  1. 打开 OAuth Apps
  2. 点击 New OAuth App

第 2 步:填写回调地址

最关键的是 Authorization callback URL

1
http://localhost:3000/oauth/github/login/callback

第 3 步:把密钥填入 .env.local

1
2
3
GITHUB_CLIENT_ID=你的 GitHub Client ID
GITHUB_CLIENT_SECRET=你的 GitHub Client Secret
GITHUB_CLIENT_REDIRECT_URI=http://localhost:3000/oauth/github/login/callback

第 4 步:重启本地 Open WebUI

1
docker compose -f docker-compose.local.yaml up -d --build

注意事项

  • GitHub 对 callback URL 也是精确匹配
  • 如果后台填的是 127.0.0.1,本地配置也要统一成 127.0.0.1
  • 不要后台填 127.0.0.1,本地却写 localhost

为了更容易排错,我建议不要一上来三个一起配,而是按这个顺序:

  1. 先配一个,例如 GitHub
  2. 测通之后,再加 Google
  3. 最后再加 Microsoft

这样如果出现问题,最容易定位。

最常见的报错就是:

  • redirect_uri_mismatch
  • 端口改了,但开放平台后台没改
  • .env.local 改了,但容器没重启
  • 后台写的是 127.0.0.1,本地写的是 localhost

SCIM 自动开通与回收

这是什么

SCIM 不是登录协议,而是“账号生命周期同步协议”。

它解决的问题是:

  • 新员工入职时,自动在 Open WebUI 创建账号
  • 员工信息变化时,自动同步更新
  • 员工离职时,自动停用账号
  • 用户组成员关系自动同步

所以可以把它理解成“自动开账号、改账号、停账号”的标准接口。

和 OAuth / LDAP 的关系

  • LDAP / OAuth / OIDC:解决“怎么登录”
  • SCIM:解决“账号怎么自动创建和同步”

很多企业会同时使用:

  • 用 OIDC 登录
  • 用 SCIM 做账号与组同步

当前版本配置

1
2
3
4
environment:
- SCIM_ENABLED=true
- SCIM_TOKEN=your-secure-random-token
- SCIM_AUTH_PROVIDER=oidc

参数解释

参数说明
SCIM_ENABLED是否启用 SCIM
SCIM_TOKEN调用 SCIM API 的 Bearer Token
SCIM_AUTH_PROVIDER用于把 SCIM externalId 和对应认证提供商关联起来,例如 microsoftoidc

这里的 SCIM_AUTH_PROVIDER 很容易被漏掉,但当前版本里它是重要配置,尤其是在账户关联和 externalId 保存上。

对接端点

SCIM Base URL:

1
https://your-domain.com/api/v1/scim/v2/

常见资源端点:

资源端点
用户/api/v1/scim/v2/Users
/api/v1/scim/v2/Groups

我的管理方式

我把 SCIM 视为“企业级增强项”:

  • 小规模自用:完全不需要
  • 团队规模不大,但已有统一登录:先上 OAuth / LDAP,SCIM 可以以后再说
  • 企业正式上生产:如果人事变动频繁、合规要求高,就值得上 SCIM

它的价值不在“让用户多一个登录按钮”,而在于减少人工维护账号、降低离职账号遗留风险。

这是什么

用户在浏览器里登录后,Open WebUI 需要一种机制记住登录状态,这通常会涉及:

  • JWT:登录令牌
  • Session Cookie:浏览器保存的登录凭证
  • Secret Key:服务器用来签名和加密敏感数据的密钥

当前版本重点配置

1
2
3
4
5
environment:
- WEBUI_SECRET_KEY=your-persistent-secret-key
- JWT_EXPIRES_IN=4w
- WEBUI_SESSION_COOKIE_SAME_SITE=Lax
- WEBUI_SESSION_COOKIE_SECURE=true

参数解释

参数说明
WEBUI_SECRET_KEY最关键的持久化密钥,用于会话签名和敏感数据解密
JWT_EXPIRES_IN登录令牌过期时间,例如 4w7d
WEBUI_SESSION_COOKIE_SAME_SITECookie 的跨站策略
WEBUI_SESSION_COOKIE_SECURE是否只通过 HTTPS 发送 Cookie

为什么 WEBUI_SECRET_KEY 非常重要

官网 FAQ 专门提到,如果你每次重建容器都不保留同一个 WEBUI_SECRET_KEY,会出现两类典型问题:

  • 用户升级或重启后被全部强制退出
  • 一些已经加密保存的令牌、API Key、OAuth 凭证无法解密

所以这个值一定要固定,不要让容器每次随机生成。

我的管理方式

这一点我会非常保守:

  1. 生产环境固定设置 WEBUI_SECRET_KEY
  2. HTTPS 环境强制 WEBUI_SESSION_COOKIE_SECURE=true
  3. 不把 JWT_EXPIRES_IN 设成无限期
  4. 升级容器前先确认密钥仍然通过同样方式注入

这是最容易被忽略、但一出问题就会直接影响所有用户登录体验的一块。