使用 Quarkus MCP Server 安全 MCP 准备
简介
最新版本的模型上下文协议 (MCP) 规范 引入了一个 授权流程。
虽然新的 MCP 规范需要一段时间才能得到广泛支持,但您已经可以按照 以前的 MCP 版本为客户端和服务器添加身份验证。
您只需要一个可以接收访问令牌并将其传递给 MCP 服务器的 MCP 客户端,以及一个可以验证令牌的 MCP 服务器。
在这篇文章中,我们将详细介绍如何使用 Quarkus MCP SSE Server 强制执行身份验证。
我们将首先使用 Keycloak 作为 OpenID Connect (OIDC) 提供程序登录,并使用 Keycloak JWT 访问令牌通过开发模式下的 Quarkus MCP SSE Server Dev UI
访问服务器。
其次,我们将展示如何使用 GitHub OAuth2 登录,并使用 GitHub 二进制访问令牌,在生产模式下通过 MCP Inspector 和 curl
工具访问服务器。
步骤 1 - 创建使用 SSE 传输的 MCP 服务器
首先,让我们创建一个安全的 Quarkus MCP SSE 服务器,该服务器需要身份验证才能建立服务器发送事件 (SSE) 连接,并在调用工具时进行身份验证。
您可以在 Quarkus MCP SSE Server 示例中找到完整的项目源代码。
Maven 依赖
添加以下依赖项
<dependency>
<groupId>io.quarkiverse.mcp</groupId>
<artifactId>quarkus-mcp-server-sse</artifactId> (1)
<version>1.1.1</version>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-oidc</artifactId> (2)
</dependency>
1 | 需要 quarkus-mcp-server-sse 来支持 MCP SSE 传输。 |
2 | quarkus-oidc 是保护对 MCP SSE 端点的访问所必需的。它的版本在 Quarkus BOM 中定义。 |
工具
让我们创建一个只有在当前 MCP 请求通过身份验证时才能调用的工具
package org.acme;
import io.quarkiverse.mcp.server.TextContent;
import io.quarkiverse.mcp.server.Tool;
import io.quarkus.security.Authenticated;
import io.quarkus.security.identity.SecurityIdentity;
import jakarta.inject.Inject;
public class ServerFeatures {
@Inject
SecurityIdentity identity;
@Tool(name = "user-name-provider", description = "Provides a name of the current user") (1)
@Authenticated (2)
TextContent provideUserName() {
return new TextContent(identity.getPrincipal().getName()); (3)
}
}
1 | 提供一个可以返回当前用户名称的工具。注意 user-name-provider 工具名称,您将在稍后使用它进行工具调用。 |
2 | 需要经过身份验证的工具访问 - 是的,与未经身份验证的 MCP 服务器工具的唯一区别是 @Authenticated ,就是这样!另请参阅下面的 配置 部分中如何保护主 MCP SSE 端点。 |
3 | 使用注入的 SecurityIdentity 返回当前用户的姓名。 |
配置
最后,让我们配置我们的安全 MCP 服务器
quarkus.http.auth.permission.authenticated.paths=/mcp/sse (1)
quarkus.http.auth.permission.authenticated.policy=authenticated
1 | 在初始握手期间,强制执行对主 MCP SSE 端点的身份验证访问。另请参阅上面的 工具 部分中如何使用注解保护该工具,尽管您也可以通过在配置中列出主端点和工具端点来保护对该工具的访问,例如:quarkus.http.auth.permission.authenticated.paths=/mcp/sse,/mcp/messages/* 。 |
我们已准备好在开发模式下测试我们的安全 MCP 服务器。
步骤 2 - 在开发模式下访问 MCP 服务器
在开发模式下启动 MCP 服务器
mvn quarkus:dev
我们在上面的 配置 部分中设置的配置属性足以在开发模式下启动应用程序。
OIDC 配置由 Dev Services for Keycloak 在开发模式下自动提供。它创建默认领域、客户端并添加两个用户 alice
和 bob
,以便您可以立即开始使用 OIDC。您还可以注册一个自定义 Keycloak 领域以与现有领域、客户端和用户注册一起使用。
您还可以登录到 OIDC Dev UI 中的其他 OIDC 和 OAuth2 提供程序,有关更多详细信息,请参阅 使用 Quarkus MCP Server Dev UI 访问 MCP 服务器 部分。
使用 OIDC Dev UI 登录并复制访问令牌
转到 Dev UI,找到 OpenId Connect 卡片

按照 Keycloak Provider
链接并使用 alice
名称和 alice
密码 登录到 Keycloak。
您也可以从 OIDC DevUI 登录到其他提供程序,例如 |
以 alice
身份登录 Keycloak
后,使用提供的复制按钮复制获取的访问令牌

使用 Quarkus MCP Server Dev UI 访问 MCP 服务器
请确保登录并复制访问令牌,如上面的 使用 OIDC Dev UI 登录并复制访问令牌 部分中所述。
转到 Dev UI,找到 MCP Server 卡片

选择其 Tools
选项并选择 Call
user-name-provider
工具

将复制的 Keycloak 访问令牌粘贴到工具的 Bearer token
字段中,并请求新的 MCP SSE 会话

进行工具调用并获取包含 alice
用户名的响应

在开发模式下一切正常;现在是看看它在生产模式下如何工作的时候了。在此之前,停止在开发模式下运行的 MCP 服务器。
步骤 3 - 在生产模式下访问 MCP 服务器
注册 GitHub OAuth2 应用程序
之前都是在开发模式下 - 使用 Quarkus devservices 来尝试。现在,让我们转到生产模式。如果您已经有一个 Keycloak 实例正在运行,那么您可以使用它。但是为了说明 OAuth2 如何与 Keycloak 之外的其他工具一起工作,我们将在应用程序以生产模式运行时切换到 GitHub OAuth2。
首先,从注册 GitHub OAuth2 应用程序开始。
按照 GitHub OAuth2 注册流程,并确保注册 https://:8080/login
回调 URL。
接下来,使用在 GitHub OAuth2 应用程序注册期间生成的客户端 ID 和密钥来 更新配置以支持 GitHub。
更新配置以支持 GitHub
用于在开发模式下运行 MCP 服务器的 配置 已足够,因为 Keycloak Dev Service 支持 OIDC 登录。
为了在生产模式下使用 GitHub,我们按如下方式更新配置
quarkus.http.auth.permission.authenticated.paths=/mcp/sse (1)
quarkus.http.auth.permission.authenticated.policy=authenticated
%prod.quarkus.oidc.provider=github (2)
%prod.quarkus.oidc.application-type=service (3)
%prod.quarkus.oidc.login.provider=github (4)
%prod.quarkus.oidc.login.client-id=github-application-client-id (5)
%prod.quarkus.oidc.login.credentials.secret=github-application-client-secret (5)
1 | 在初始握手期间,强制执行对主 MCP SSE 端点的身份验证访问。另请参阅上面的 工具 部分中如何使用注解保护该工具。 |
2 | 默认的 Quarkus OIDC 配置要求只能使用 GitHub 访问令牌来访问 MCP SSE 服务器。 |
3 | 默认情况下,quarkus.oidc.provider=github 仅支持授权码流。 quarkus.oidc.application-type=service 会覆盖它,并要求使用 Bearer 令牌。 |
4 | 使用 GitHub 授权码流通过专用的 Quarkus OIDC login 租户 配置来支持登录端点。 |
5 | 使用在 注册 GitHub OAuth2 应用程序 部分中生成的客户端 ID 和密钥。 |
请注意使用 |
实现登录端点
目前,MCP 客户端无法自行使用授权码流。因此,我们实现了一个 OAuth2 登录端点,该端点将返回一个 GitHub 令牌,供用户与可以使用 Bearer 令牌的 MCP 客户端一起使用。
添加另一个依赖项以支持 Qute 模板
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-rest-qute</artifactId> (1)
</dependency>
1 | 需要 quarkus-rest-qute 来生成 HTML 页面。它的版本在 Quarkus BOM 中定义。 |
并实现登录端点
package org.acme;
import io.quarkus.oidc.AccessTokenCredential;
import io.quarkus.oidc.UserInfo;
import io.quarkus.qute.Template;
import io.quarkus.qute.TemplateInstance;
import io.quarkus.security.Authenticated;
import jakarta.inject.Inject;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.Produces;
@Path("/login")
@Authenticated
public class LoginResource {
@Inject
UserInfo userInfo; (1)
@Inject
AccessTokenCredential accessToken; (2)
@Inject
Template accessTokenPage;
@GET
@Produces("text/html")
public TemplateInstance poem() {
return accessTokenPage
.data("name", userInfo.getName())
.data("accessToken", accessToken.getToken()); (3)
}
}
1 | GitHub 访问令牌是二进制的,Quarkus OIDC 通过使用它们来请求 GitHub 特定的 UserInfo 表示来间接验证它们。 |
2 | AccessTokenCredential 用于捕获二进制 GitHub 访问令牌。 |
3 | 用户登录到 GitHub 并重定向到此端点后,访问令牌将在使用简单 Qute 模板生成的 HTML 页面中返回给用户。当然,您不会在实际应用程序中这样做。这只是一个演示功能的示例。 |
打包并运行 MCP 服务器
打包 MCP 服务器应用程序
mvn package
运行它
java -jar target/quarkus-app/quarkus-run.jar
您还可以直接使用
|
登录到 GitHub 并复制访问令牌
访问 https://:8080/login
,登录到 GitHub,并复制返回的访问令牌

默认情况下,Quarkus GitHub 提供程序在 HTTP Authorization 标头中提交客户端 ID 和密钥。但是,GitHub 可能要求客户端 ID 和密钥都作为表单参数提交。 当您在登录到 GitHub 并重定向回 Quarkus MCP 服务器后收到 HTTP 401 错误时,请尝试将
|
使用 MCP Inspector 访问 MCP 服务器
MCP Inspector 是一种交互式开发工具,用于测试和调试 MCP 服务器。让我们使用它来调用带有身份验证的 MCP 服务器。
npx @modelcontextprotocol/inspector
请确保您已安装 modelcontextprotocol/inspector 0.6.0 或更高版本,因为它增加了对指定 Bearer 令牌身份验证的支持。 |
导航到浏览器中提供的 URL。
将传输类型下拉列表更改为 SSE
,并将 URL 更改为 https://:8080/mcp/sse
,以便它指向正在运行的 Quarkus MCP Server
选择授权按钮,并将从浏览器复制的 GitHub 访问令牌粘贴到 Bearer Token
字段中,然后连接到 Quarkus MCP SSE 服务器

接下来,进行 user-name-provider
工具调用

您将看到从您的 GitHub 帐户返回的名称。
使用 curl 访问 MCP 服务器
最后,让我们使用 curl
,并稍微了解 MCP 协议和 MCP SSE 传输的工作原理。
首先,打开一个新的终端窗口并在没有 GitHub 访问令牌的情况下访问主 SSE 端点
curl -v localhost:8080/mcp/sse
您将收到 HTTP 401 错误。
使用先前获得的访问令牌来访问 MCP 服务器
curl -v -H "Authorization: Bearer gho_..." localhost:8080/mcp/sse
并获取 SSE 响应,例如
< content-type: text/event-stream
<
event: endpoint
data: /messages/ZTZjZDE5MzItZDE1ZC00NzBjLTk0ZmYtYThiYTgwNzI1MGJ
SSE 连接已创建。请注意收到的 data
中的唯一路径,我们需要此路径来调用工具。我们无法直接调用该工具,我们首先需要遵循 MCP 握手协议。
打开另一个终端窗口并使用相同的 GitHub 访问令牌将 curl 初始化为 MCP 客户端,并使用 data
属性的值构建目标 URL 来访问该工具。
发送客户端初始化请求
curl -v -H "Authorization: Bearer gho_..." -H "Content-Type: application/json" --data @initialize.json https://:8080/mcp/messages/ZTZjZDE5MzItZDE1ZC00NzBjLTk0ZmYtYThiYTgwNzI1MGJ
其中 initialize.json
文件的内容如下
{
"jsonrpc": "2.0",
"id": 1,
"method": "initialize",
"params": {
"protocolVersion": "2024-11-05",
"capabilities": {
"roots": {
"listChanged": true
},
"sampling": {}
},
"clientInfo": {
"name": "CurlClient",
"version": "1.0.0"
}
}
}
发送客户端初始化确认
curl -v -H "Authorization: Bearer gho_..." -H "Content-Type: application/json" --data @initialized.json https://:8080/mcp/messages/ZTZjZDE5MzItZDE1ZC00NzBjLTk0ZmYtYThiYTgwNzI1MGJ
其中 initialized.json
文件的内容如下
{
"jsonrpc": "2.0",
"method": "notifications/initialized"
}
最后,发送将调用该工具的请求
curl -v -H "Authorization: Bearer gho_..." -H "Content-Type: application/json" --data @call.json https://:8080/mcp/messages/ZTZjZDE5MzItZDE1ZC00NzBjLTk0ZmYtYThiYTgwNzI1MGJ
其中 call.json
文件的内容如下
{
"jsonrpc": "2.0",
"id": 2,
"method": "tools/call",
"params": {
"name": "user-name-provider",
"arguments": {
}
}
}
现在查看包含 SSE 连接的终端窗口,您将看到从您的 GitHub 帐户返回的名称。
结论
在这篇博文中,我们介绍了如何轻松创建需要身份验证的 Quarkus MCP SSE 服务器,获取访问令牌并使用它在开发模式下通过 Quarkus MCP SSE Server Dev UI
访问 MCP 服务器工具,在生产模式下通过 MCP Inspector 和 curl 工具访问 MCP 服务器工具。您可以使用任何允许将 Bearer 令牌传递给服务器的 MCP 客户端。
请注意,对于 Quarkus MCP 服务器或 REST 端点,OAuth2 的完成方式实际上没有区别。最复杂的部分是为您的 OAuth2 提供程序正确配置设置 - 但是当一切完成后,您只需应用一些注解将相关方法标记为安全,Quarkus 就会为您处理身份验证。
这篇博文使用了以前版本的 MCP 协议。Quarkus 团队密切关注 MCP 授权规范的演变,并致力于支持所有可能的 MCP 授权方案。
请继续关注更多更新!