Traefik
casdoor-forward-auth 可以在 Traefik 后面的任何服务前加上 Casdoor 单点登录,无需修改服务本身。 Traefik 通过内置的 forwardAuth 中间件对每个请求询问 casdoor-forward-auth:已登录用户带着请求头中的身份信息访问服务,其他人先被送到 Casdoor 登录页。
The same service also works with Caddy (forward_auth) and Nginx (auth_request).
工作原理
- 对受保护服务的每个请求,Traefik 都会调用 casdoor-forward-auth 的
/auth。 - 带有效会话 Cookie 时,
/auth返回200,并带上X-Forwarded-User等请求头,Traefik 会把它们复制到发给你的服务的请求中。 - 没有会话时,页面请求会被跳转到 Casdoor。其他请求(
POST、PUT……)会得到401,因为它们无法跟随登录跳转。 - 登录后,Casdoor 跳转到
/callback。 casdoor-forward-auth 检查state、交换授权码、校验访问令牌,把用户保存在签名的HttpOnly会话 Cookie 中,然后把用户送回原来请求的页面。
casdoor-forward-auth 在服务器端不保存状态,所以只要共用同一个 Cookie 密钥,就可以运行多个副本。
先决条件
- Traefik v2 或 v3
- 一个 Casdoor 实例(见 服务器安装)
- 两个指向 Traefik 的主机名,例如 casdoor-forward-auth 用
auth.example.com,受保护的服务用app.example.com。只有一个主机时,见 使用单个主机。
第 1 步:配置 Casdoor 应用
-
在 Casdoor 中创建或编辑一个应用。
-
把 casdoor-forward-auth 的回调地址加到 Redirect URLs:
https://auth.example.com/callback -
记下 Client ID 和 Client secret。

第 2 步:部署 casdoor-forward-auth 和 Traefik
创建 docker-compose.yml:
services:
traefik:
image: traefik:v3.1
command:
- --providers.docker=true
- --providers.docker.exposedbydefault=false
- --entrypoints.web.address=:80
ports:
- "80:80"
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
casdoor-forward-auth:
image: ghcr.io/casdoor/casdoor-forward-auth:latest
environment:
CASDOOR_ENDPOINT: https://door.casdoor.com
CLIENT_ID: <client ID>
CLIENT_SECRET: <client secret>
EXTERNAL_URL: http://auth.example.com
COOKIE_DOMAIN: example.com
COOKIE_SECRET: <random string of at least 32 characters>
labels:
- traefik.enable=true
- traefik.http.routers.casdoor-auth.rule=Host(`auth.example.com`)
- traefik.http.routers.casdoor-auth.entrypoints=web
- traefik.http.services.casdoor-auth.loadbalancer.server.port=9999
- traefik.http.middlewares.casdoor.forwardauth.address=http://casdoor-forward-auth:9999/auth
- traefik.http.middlewares.casdoor.forwardauth.authResponseHeaders=X-Forwarded-User,X-Forwarded-User-Id,X-Forwarded-Organization,X-Forwarded-Email,X-Forwarded-Groups,X-Forwarded-Roles
# the protected service
whoami:
image: traefik/whoami
labels:
- traefik.enable=true
- traefik.http.routers.whoami.rule=Host(`app.example.com`)
- traefik.http.routers.whoami.entrypoints=web
- traefik.http.routers.whoami.middlewares=casdoor
替换占位值:
CASDOOR_ENDPOINT:你的 Casdoor 服务器地址CLIENT_ID和CLIENT_SECRET:第 1 步中得到的值EXTERNAL_URL:casdoor-forward-auth 的公网地址。<EXTERNAL_URL>/callback必须是应用的 Redirect URL 之一COOKIE_DOMAIN:受保护主机的父域名,这样会话 Cookie 会发送到所有这些主机COOKIE_SECRET:一个随机密钥,例如用openssl rand -hex 32生成。保持不变:修改它会让所有人登出
生产环境使用 https:// 地址,这样 Cookie 只通过 HTTPS 发送。
启动服务:
docker compose up -d
要保护另一个服务,在它的路由器上加上 traefik.http.routers.<router>.middlewares=casdoor。不要给 casdoor-forward-auth 自身的路由器加这个中间件。
使用文件提供者
如果用文件而不是 Docker 标签配置 Traefik,在动态配置中定义中间件和路由器:
http:
middlewares:
casdoor:
forwardAuth:
address: http://casdoor-forward-auth:9999/auth
authResponseHeaders:
- X-Forwarded-User
- X-Forwarded-User-Id
- X-Forwarded-Organization
- X-Forwarded-Email
- X-Forwarded-Groups
- X-Forwarded-Roles
routers:
casdoor-auth:
rule: Host(`auth.example.com`)
service: casdoor-auth
app:
rule: Host(`app.example.com`)
service: app
middlewares:
- casdoor
services:
casdoor-auth:
loadBalancer:
servers:
- url: http://casdoor-forward-auth:9999
app:
loadBalancer:
servers:
- url: http://app:8080
casdoor-forward-auth 也可以不用 Docker 运行:go install github.com/casdoor/casdoor-forward-auth@latest,然后用同样的环境变量或 JSON 配置文件启动(casdoor-forward-auth -config config.json,见 conf/config.json)。
使用单个主机
只有一个主机名时,把 casdoor-forward-auth 挂在服务的某个路径下,例如 EXTERNAL_URL=https://app.example.com/_auth,不设置 COOKIE_DOMAIN。把这个路径路由到 casdoor-forward-auth,不加中间件:
routers:
casdoor-auth:
rule: Host(`app.example.com`) && PathPrefix(`/_auth`)
service: casdoor-auth
app:
rule: Host(`app.example.com`)
service: app
middlewares:
- casdoor
把 forwardAuth 地址设为 http://casdoor-forward-auth:9999/_auth/auth,并在 Casdoor 的 Redirect URLs 中加上 https://app.example.com/_auth/callback。
第 3 步:测试集成
- 打开受保护的服务,例如
http://app.example.com。 - 你会被跳转到 Casdoor 登录页。
- 登录后回到你打开的页面,服务会收到身份请求头。
traefik/whoami会把它们打印出来,可以在那里检查。
身份请求头
| 请求头 | 值 |
|---|---|
X-Forwarded-User | 用户名,例如 alice |
X-Forwarded-User-Id | 用户 ID |
X-Forwarded-Organization | 用户所属的组织 |
X-Forwarded-Email | 邮箱地址 |
X-Forwarded-Groups | 逗号分隔的群组,例如 built-in/dev,built-in/ops |
X-Forwarded-Roles | 逗号分隔的角色名 |
casdoor-forward-auth 总是返回所有这些请求头(可能为空),所以 Traefik 会覆盖客户端在同名请求头中 发送的任何值。确保受保护的服务只能通过 Traefik 访问,否则任何人都能直接发送这些请求头。
Restricting access
By default every user who can sign in to the Casdoor application is let in. To let in only users with certain Casdoor roles or groups, add roles and/or groups (comma separated) to the address of the middleware. Define one middleware per rule:
- traefik.http.middlewares.casdoor-admin.forwardauth.address=http://casdoor-forward-auth:9999/auth?roles=admin,ops
A user passes with at least one of the listed roles and, if groups is given too (e.g., groups=built-in/dev), at least one of the listed groups. Everyone else who is signed in gets 403. ALLOWED_ROLES and ALLOWED_GROUPS set one rule for all services.
This also covers "sign up first, get access later": new users, e.g., from Google sign-up, have no role and get 403 until an admin assigns them one on the Roles page of Casdoor. Roles and groups are read at login, so after a change the user has to open /logout and sign in again, or wait for the session to end (SESSION_TTL).
配置
每项设置都可以通过环境变量或 JSON 配置文件提供:
| 环境变量 | 默认值 | 映射的工具 |
|---|---|---|
CASDOOR_ENDPOINT | required | Casdoor 服务器地址 |
CLIENT_ID | required | Casdoor 应用的 Client ID |
CLIENT_SECRET | 必填 | Casdoor 应用的 Client secret |
EXTERNAL_URL | 必填 | casdoor-forward-auth 的公网地址,可以包含路径 |
COOKIE_SECRET | 必填 | 用于签名 Cookie 的密钥,至少 32 个字符 |
COOKIE_DOMAIN | 空 | 会话 Cookie 的域,例如 example.com |
COOKIE_NAME | casdoor_forward_auth | 会话 Cookie 的名称 |
SESSION_TTL | 24h | 会话有效期,不 会超过 Casdoor 签发的访问令牌的有效期 |
ALLOWED_REDIRECT_DOMAINS | EXTERNAL_URL 的主机和 .<COOKIE_DOMAIN> | 登录后允许跳回的域名,逗号分隔 |
ALLOWED_ROLES | 空 | Comma-separated Casdoor roles. Only users with at least one of them are let in, see Restricting access |
ALLOWED_GROUPS | empty | Comma-separated Casdoor groups, e.g., built-in/dev. Only users in at least one of them are let in |
CERTIFICATE | empty | 用于校验访问令牌的 PEM 证书。默认从 Casdoor 的 /.well-known/jwks 获取 |
LISTEN_ADDR | :9999 | 监听地址 |
/logout 清除 casdoor-forward-auth 的会话(带 ?rd=<url> 可在之后跳转)。用户在 Casdoor 中仍保持登录。
故障排除
Casdoor 显示 "Redirect URI ... doesn't exist in the allowed Redirect URI list"
<EXTERNAL_URL>/callback 不在应用的 Redirect URLs 中。协议、主机、端口和路径都必须一致。
登录后又被跳回登录页
会话 Cookie 没有发送到受保护的主机:
- 把
COOKIE_DOMAIN设为同时覆盖EXTERNAL_URL和受保护主机的域名。 EXTERNAL_URL使用https://时 Cookie 是Secure的,所以受保护的服务也必须通过 HTTPS 提供。
"the login state is missing or has expired"
登录用时超过 10 分钟,或者浏览器拦截了 /login 设置的 Cookie。重新打开受保护的页面,开始新的登录。
前端请求返回 401
没有会话时,只有 GET 和 HEAD 请求会被跳转到登录。其他方法的 API 调用会得到 401;让用户刷新页面登录即可。
从 traefik-casdoor-auth 升级
casdoor-forward-auth 在 v2 之前叫 traefik-casdoor-auth,需要一个 Traefik 插件。升级方法:
- 移除本地插件(
experimental.localPlugins和plugins-local),改用上面所示的forwardAuth中间件。 - 重命名配置项:
casdoorClientId→clientId,casdoorClientSecret→clientSecret,pluginEndpoint→externalUrl。不再需要casdoorOrganization和casdoorApplication,新增的cookieSecret是必填项。 - Casdoor 中的 Redirect URL 仍然是
<externalUrl>/callback。
资源
- casdoor-forward-auth on GitHub
- Traefik forwardAuth 中间件
- ELK:用 casdoor-forward-auth 或 elk-auth-casdoor 保护 Kibana