端到端地将 MCP 服务器置于 Casdoor MCP 网关之后
本指南将通过一个完整的示例进行演示:一家公司向其员工发布一个 MCP 服务器,员工可以从 ChatGPT、Claude、Cursor 和 VS Code 中使用它。Casdoor 既是位于服务器前方的网关,也是为用户签入提供授权的授权服务器。MCP 服务器本身不需要任何 OAuth 2.0 代码。
学习目标
- 准备一个组织,以便 MCP 客户端在其中注册并登录。
- 在 Casdoor 中注册一个 MCP 服务器,并决定谁可以使用它以及哪些工具可用。
- 连接四个常见的 MCP 客户端。
- 由一个 Casdoor 实例为多个组织提供服务。
- 检查、审计并撤销访问权限。
前提条件
- 可通过 HTTPS 从互联网访问的 Casdoor 实例,例如
https://auth.example.com。ChatGPT 和 Claude 自定义连接器会从云端调用它。 - 支持 Streamable HTTP 传输协议的 MCP 服务器,例如
https://tickets.example.com/mcp,可选地使用静态承载令牌进行保护 - 拥有 Casdoor 管理控制台的超级管理员权限
示例
Acme 运营着一个用于其工单系统的 MCP 服务器。Acme 希望:
- 员工使用其 Acme 账户登录,通过 Acme 已在 Casdoor 中设置的登录方式。
- 仅支持团队可以使用该服务器。
- 任何人不得通过 AI 客户端调用
delete_ticket工具。 - 服务器自身的 API 令牌绝不会离开 Casdoor。
- 管理员可以查看并撤销每个客户端的访问权限。
步骤 1:准备组织
- 在 Casdoor 管理控制台中,转到 User Management > Organizations 并打开
acme。 - 开启 Use organization issuer。该组织将成为自己的授权服务器,颁发者(issuer)为
https://auth.example.com/orgs/acme,发现文档为https://auth.example.com/.well-known/oauth-authorization-server/orgs/acme。发现此配置的 MCP 客户端将在acme中注册,并在acme的登录页面上为用户签入。 - 开启 Enable dynamic client registration。这允许客户端通过客户端 ID 元数据文档(Claude、VS Code、ChatGPT)标识自身,或自行注册(Cursor、旧版客户端)。只有全局管理员才能更改此开关。
- 保存组织设置。
如果不启用 Use organization issuer,网关会将 Casdoor 的全局颁发者列为授权服务器,并且客户端会在 built-in 中注册。对于除 built-in 之外发布 MCP 服务器的每个组织,请开启此选项。
步骤 2:决定谁可以使用服务器
网关允许服务器所在组织中有权登录到服务器应用程序的用户进入。
- 转到 Identity > Applications 并在
acme中添加一个应用程序,例如app-tickets。它不需要重定向 URL:MCP 客户端不会登录到它,它仅承载访问规则。 - 要仅允许支持团队进入,请转到 Authorization > Permissions 并在
acme中添加一个权限:- Resource type:
Application - Resources:
app-tickets - Groups 或 Roles: 支持团队
- Actions:
Read, Effect:Allow
- Resource type:
如果没有对该应用设置任何权限,acme 的每个用户都可以使用该服务器。acme 的管理员和全局管理员始终可以使用它。
步骤 3:注册 MCP 服务器
-
前往 LLM AI > MCP Servers 并在
acme中添加一个服务器:字段 值 Name ticketsURL https://tickets.example.com/mcpApplication app-ticketsAccess token 工单服务器的 API 令牌(如果服务器需要) -
保存服务器,然后在 Tool 下选择 Sync。Casdoor 会获取该服务器的工具列表。
-
屏蔽
delete_ticket并保存。网关会对调用被屏蔽的工具或不在列表中的工具返回错误,并且不会转发该请求。 -
复制 Base URL:
https://auth.example.com/api/server/acme/tickets。这是用户在他们的 MCP 客户端中添加的 URL。
网关会从每个请求中移除 Cookie 标头和用户的 Authorization 标头,并改为发送服务器的 Access token。Casdoor 绝不会在其 API 响应中返回该令牌。
对于除 built-in 之外的组织中的服务器,URL 必须是公共互联网地址:网关拒绝代表租户访问私有地址和回环地址。若要在内部网络中发布 MCP 服务器,请在 built-in 中注册它。
步骤 4:检查发现链
每个 MCP 客户端都遵循相同的链路。在连接客户端之前,使用 curl 进行检查:
curl -i -X POST https://auth.example.com/api/server/acme/tickets
Casdoor 返回 HTTP 401 并指定此服务器的受保护资源元数据:
WWW-Authenticate: Bearer realm="casdoor", resource_metadata="https://auth.example.com/.well-known/oauth-protected-resource/api/server/acme/tickets"
curl https://auth.example.com/.well-known/oauth-protected-resource/api/server/acme/tickets
{
"resource": "https://auth.example.com/api/server/acme/tickets",
"authorization_servers": ["https://auth.example.com/orgs/acme"],
"bearer_methods_supported": ["header"],
"scopes_supported": ["openid", "profile", "email", "offline_access"]
}
curl https://auth.example.com/.well-known/oauth-authorization-server/orgs/acme
在本文档中,请检查:
issuer是https://auth.example.com/orgs/acme,与authorization_servers中的值相同。client_id_metadata_document_supported为true,且registration_endpoint以?organization=acme结尾。token_endpoint_auth_methods_supported包含none,且code_challenge_methods_supported包含S256。
步骤 5:连接客户端
每个用户将网关 URL 添加到其客户端,并使用其 Acme 账户登录。
| Client | Where to add the server | How it identifies itself | Guide |
|---|---|---|---|
| ChatGPT | Plugins > + > Add custom MCP server | CIMD https://chatgpt.com/oauth/client.json | Connect ChatGPT |
| Claude Desktop, claude.ai | Customize > Connectors > Add custom connector | CIMD https://claude.ai/oauth/mcp-oauth-client-metadata | Connect Claude |
| Claude Code | claude mcp add --transport http tickets <url> | CIMD https://claude.ai/oauth/claude-code-client-metadata | Connect Claude |
| Cursor | ~/.cursor/mcp.json | DCR | Connect Cursor |
| VS Code | .vscode/mcp.json | CIMD https://vscode.dev/oauth/client-metadata.json | Connect VS Code |
例如,在 Cursor 中:
{
"mcpServers": {
"tickets": {
"url": "https://auth.example.com/api/server/acme/tickets"
}
}
}
以及在 VS Code 中:
{
"servers": {
"tickets": {
"type": "http",
"url": "https://auth.example.com/api/server/acme/tickets"
}
}
}
使用客户端 ID 元数据文档的客户端会在 Casdoor 中获得一个应用,其 URL 作为客户端 ID,标签为 cimd,名称以 cimd_ 开头。自行注册的客户端会为每次注册获得一个应用,标签为 dcr。你无需为这些客户端创建应用。
服务器的保护措施
| Check | Where | What happens otherwise |
|---|---|---|
令牌属于 acme 的用户 | Gateway | HTTP 401 |
用户可以登录到 app-tickets | Gateway | HTTP 401 |
| 令牌是为该服务器签发的 (RFC 8707) | Gateway | HTTP 401: "The access token is for another resource" |
| 工具在列表中且被允许 | Gateway | JSON-RPC error, the call isn't forwarded |
| 客户端是自行注册的客户端 | Every API | 其令牌只能调用登录流程、/api/userinfo、/api/mcp 和 /api/server/*,绝不能调用管理 API |
客户端请求令牌时携带 resource=https://auth.example.com/api/server/acme/tickets,Casdoor 会将该 URL 放入 aud 声明中。针对另一个 MCP 服务器(即使是同一组织下的)的令牌在此处无效,因此恶意服务器无法重放其接收到的令牌。
服务多个组织
一个 Casdoor 实例可以为多个组织发布 MCP 服务器,例如 SaaS 的客户。请为每个组织重复步骤 1 到 3。然后:
- 每个组织都有自己的 issuer、登录页面、用户和权限。
globex的用户无法在acme的页面上登录,也无法使用acme的服务器。 - ChatGPT、Claude 和 VS Code 的 client ID metadata documents 各有一个 URL,供所有组织使用。Casdoor 在
built-in中为每个 URL 保留一个共享应用,并将其作为用户登录的组织的应用提供服务。每个组织仍必须开启 Enable dynamic client registration 以接受这些客户端。 - 令牌携带用户所属组织的 issuer,即使在刷新后也是如此。
- 自行注册的客户端 (DCR) 会在发现文档所在的组织中注册,通过注册端点的
organization参数进行。
检查、审计和撤销
- Clients:Identity > Applications 列出了带有
cimd和dcr标签的客户端。删除dcr_应用即可移除注册。 - Tokens:Auditing > Tokens 列出了每个令牌及其用户、应用、scope 和资源。删除令牌可立即切断客户端连接。
- Users:禁用用户后,其令牌将在下一次请求时停止在网关处工作。
- Tools:在服务器的编辑页面上阻止某个工具。更改将在下一次调用时生效。
让企业用户跳过浏览器
当组织的员工通过 Okta 等企业身份提供商登录时,支持 Enterprise-Managed Authorization(Claude Team 和 Enterprise)的客户端可以在无需浏览器登录的情况下获取网关令牌:身份提供商签发 ID-JAG,Casdoor 将其交换为访问令牌。请参阅 Enterprise-managed authorization。
故障排除
登录后网关返回 401
- 服务器没有设置 Application。如果没有应用,只有管理员可以使用该服务器。请选择一个应用,参见 Step 2。
- 应用上的权限排除了该用户。
- 用户属于与服务器不同的组织。
- 客户端请求了针对其他资源的令牌。检查令牌的
audclaim:它必须是网关 URL。
客户端在错误的组织上对用户进行登录
Use organization issuer 已关闭,因此受保护资源元数据指定的是全局 issuer。请将其开启,并重新连接客户端。
"client ID metadata documents are not accepted"
组织的 Enable dynamic client registration 已关闭。Casdoor 还必须能够通过互联网访问客户端的文档 URL。
网关返回 502
Casdoor 无法访问服务器的 URL。请检查 URL,并且对于非 built-in 的组织,确保其为公共地址。