跳到主内容

端到端地将 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:准备组织​

  1. 在 Casdoor 管理控制台中,转到 User Management > Organizations 并打开 acme。
  2. 开启 Use organization issuer。该组织将成为自己的授权服务器,颁发者(issuer)为 https://auth.example.com/orgs/acme,发现文档为 https://auth.example.com/.well-known/oauth-authorization-server/orgs/acme。发现此配置的 MCP 客户端将在 acme 中注册,并在 acme 的登录页面上为用户签入。
  3. 开启 Enable dynamic client registration。这允许客户端通过客户端 ID 元数据文档(Claude、VS Code、ChatGPT)标识自身,或自行注册(Cursor、旧版客户端)。只有全局管理员才能更改此开关。
  4. 保存组织设置。

如果不启用 Use organization issuer,网关会将 Casdoor 的全局颁发者列为授权服务器,并且客户端会在 built-in 中注册。对于除 built-in 之外发布 MCP 服务器的每个组织,请开启此选项。

步骤 2:决定谁可以使用服务器​

网关允许服务器所在组织中有权登录到服务器应用程序的用户进入。

  1. 转到 Identity > Applications 并在 acme 中添加一个应用程序,例如 app-tickets。它不需要重定向 URL:MCP 客户端不会登录到它,它仅承载访问规则。
  2. 要仅允许支持团队进入,请转到 Authorization > Permissions 并在 acme 中添加一个权限:
    • Resource type: Application
    • Resources: app-tickets
    • Groups 或 Roles: 支持团队
    • Actions: Read, Effect: Allow

如果没有对该应用设置任何权限,acme 的每个用户都可以使用该服务器。acme 的管理员和全局管理员始终可以使用它。

步骤 3:注册 MCP 服务器​

  1. 前往 LLM AI > MCP Servers 并在 acme 中添加一个服务器:

    字段值
    Nametickets
    URLhttps://tickets.example.com/mcp
    Applicationapp-tickets
    Access token工单服务器的 API 令牌(如果服务器需要)
  2. 保存服务器,然后在 Tool 下选择 Sync。Casdoor 会获取该服务器的工具列表。

  3. 屏蔽 delete_ticket 并保存。网关会对调用被屏蔽的工具或不在列表中的工具返回错误,并且不会转发该请求。

  4. 复制 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 账户登录。

ClientWhere to add the serverHow it identifies itselfGuide
ChatGPTPlugins > + > Add custom MCP serverCIMD https://chatgpt.com/oauth/client.jsonConnect ChatGPT
Claude Desktop, claude.aiCustomize > Connectors > Add custom connectorCIMD https://claude.ai/oauth/mcp-oauth-client-metadataConnect Claude
Claude Codeclaude mcp add --transport http tickets <url>CIMD https://claude.ai/oauth/claude-code-client-metadataConnect Claude
Cursor~/.cursor/mcp.jsonDCRConnect Cursor
VS Code.vscode/mcp.jsonCIMD https://vscode.dev/oauth/client-metadata.jsonConnect 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。你无需为这些客户端创建应用。

服务器的保护措施​

CheckWhereWhat happens otherwise
令牌属于 acme 的用户GatewayHTTP 401
用户可以登录到 app-ticketsGatewayHTTP 401
令牌是为该服务器签发的 (RFC 8707)GatewayHTTP 401: "The access token is for another resource"
工具在列表中且被允许GatewayJSON-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。
  • 应用上的权限排除了该用户。
  • 用户属于与服务器不同的组织。
  • 客户端请求了针对其他资源的令牌。检查令牌的 aud claim:它必须是网关 URL。

客户端在错误的组织上对用户进行登录​

Use organization issuer 已关闭,因此受保护资源元数据指定的是全局 issuer。请将其开启,并重新连接客户端。

"client ID metadata documents are not accepted"​

组织的 Enable dynamic client registration 已关闭。Casdoor 还必须能够通过互联网访问客户端的文档 URL。

网关返回 502​

Casdoor 无法访问服务器的 URL。请检查 URL,并且对于非 built-in 的组织,确保其为公共地址。

另请参阅​