初学者的模型上下文协议(MCP)课程

发表于 2025-08-12 12:52:05 | 已阅读: 166  次

将 Spring AI MCP 应用部署到 Azure Container Apps

(使用 OAuth2 保护 Spring AI MCP 服务器)
图示:使用 Spring Authorization Server 保护的 Spring AI MCP 服务器。服务器向客户端颁发访问令牌,并在接收请求时验证令牌(来源:Spring 博客)(使用 OAuth2 保护 Spring AI MCP 服务器)。
要部署 Spring MCP 服务器,请将其构建为容器,并使用带有外部入口的 Azure Container Apps。例如,使用 Azure CLI 可以运行:

az containerapp up \
  --name my-mcp-app \
  --resource-group MyResourceGroup \
  --location eastus \
  --environment MyContainerEnv \
  --image myregistry.azurecr.io/my-mcp-server:latest \
  --ingress external \
  --target-port 8080 \
  --query properties.configuration.ingress.fqdn

这将创建一个启用 HTTPS 的公开访问 Container App(Azure 会为默认的 *.azurecontainerapps.io 域颁发免费的 TLS 证书(Azure Container Apps 中的自定义域名和免费托管证书 | Microsoft Learn))。命令输出中包含应用的 FQDN(例如 my-mcp-app.eastus.azurecontainerapps.io),它将作为发行者 URL 的基础。确保启用了 HTTP 入口(如上所述),以便 APIM 能够访问该应用。在测试/开发环境中,使用 --ingress external 选项(或者根据 Microsoft 文档 绑定带 TLS 的自定义域名(Azure Container Apps 中的自定义域名和免费托管证书 | Microsoft Learn))。将任何敏感属性(如 OAuth 客户端密钥)存储在 Container Apps 的 secrets 或 Azure Key Vault 中,并映射为容器的环境变量。

配置 Spring Authorization Server

在你的 Spring Boot 应用代码中,包含 Spring Authorization Server 和 Resource Server 的启动器。配置一个 RegisteredClient(用于开发/测试环境中的 client_credentials 授权类型)和 JWT 密钥源。例如,在 application.properties 中可以设置:

# OAuth2 client (for testing token issuance)
spring.security.oauth2.authorizationserver.client.oidc-client.registration.client-id=mcp-client
spring.security.oauth2.authorizationserver.client.oidc-client.registration.client-secret={noop}secret
spring.security.oauth2.authorizationserver.client.oidc-client.registration.authorization-grant-types=client_credentials
spring.security.oauth2.authorizationserver.client.oidc-client.registration.client-authentication-methods=client_secret_basic

通过定义安全过滤链启用 Authorization Server 和 Resource Server。例如:

@Configuration
@EnableWebSecurity
public class SecurityConfiguration {

    @Bean
    SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        OAuth2AuthorizationServerConfigurer<HttpSecurity> authzServer = OAuth2AuthorizationServerConfigurer.authorizationServer();
        http
            .authorizeHttpRequests(auth -> auth.anyRequest().authenticated())
            // Enable the Authorization Server endpoints
            .apply(authzServer.and())
            // Enable the Resource Server (validate JWT on incoming requests)
            .oauth2ResourceServer(oauth2 -> oauth2.jwt(withDefaults()))
            // Disable CSRF (MCP server is not browser-based)
            .csrf(csrf -> csrf.disable())
            // Allow CORS for client demo tools
            .cors(withDefaults());
        return http.build();
    }

    // Define an in-memory client (RegisteredClient) and a JWK source:
    @Bean
    public RegisteredClientRepository registeredClientRepository() {
        RegisteredClient client = RegisteredClient.withId("1")
            .clientId("mcp-client")
            .clientSecret("{noop}secret")
            .authorizationGrantType(AuthorizationGrantType.CLIENT_CREDENTIALS)
            .scope("mcp.read")
            .clientSettings(ClientSettings.builder().build())
            .tokenSettings(TokenSettings.builder().build())
            .build();
        return new InMemoryRegisteredClientRepository(client);
    }

    @Bean
    public JWKSource<SecurityContext> jwkSource() {
        // Generate an RSA key (for dev/test, generate anew at startup)
        RSAKey rsaKey = new RSAKeyGenerator(2048).keyID("1").generate();
        JWKSet jwkSet = new JWKSet(rsaKey);
        return (selector, context) -> selector.select(jwkSet);
    }
}

此配置将暴露默认的 OAuth2 端点:/oauth2/token 用于获取令牌,/oauth2/jwks 用于 JSON Web Key Set。(默认情况下,Spring 的 AuthorizationServerSettings 会映射 /oauth2/token/oauth2/jwks配置模型 :: Spring Authorization Server)。服务器将颁发由上述 RSA 密钥签名的 JWT 访问令牌,并在 https://<your-app>:/oauth2/jwks 发布其公钥。

启用 OpenID Connect 发现: 为了让 APIM 自动获取发行者和 JWKS,需通过在安全配置中添加 .oidc(Customizer.withDefaults()) 来启用 OIDC 提供者配置端点(配置模型 :: Spring Authorization Server)。例如:

http
  .apply(authzServer.and())
  .securityMatcher(authzServer.getEndpointsMatcher())
  .with(authzServer, authz -> authz
      .oidc(Customizer.withDefaults()));  // <– enables /.well-known/openid-configuration

这将暴露 /.well-known/openid-configuration,APIM 可用其获取元数据。最后,你可能需要自定义 JWT 的 audience 声明,以确保 APIM 的 <audiences> 校验通过。例如,添加一个令牌定制器:

@Bean
public OAuth2TokenCustomizer<OAuth2TokenClaimsContext> tokenCustomizer() {
    return context -> {
        // Set a custom audience (e.g. the client ID or API identifier)
        context.getClaims().audience(Collections.singletonList("mcp-client"));
    };
}

这样令牌中会包含 "aud": ["mcp-client"],与 APIM 期望的客户端 ID 或作用域匹配。

暴露令牌和 JWKS 端点

部署后,你的应用发行者 URL 将是 https://<app-fqdn>,例如 https://my-mcp-app.eastus.azurecontainerapps.io。其 OAuth2 端点包括:

  • 令牌端点: https://<app-fqdn>/oauth2/token – 客户端在此获取令牌(client_credentials 流程)。
  • JWKS 端点: https://<app-fqdn>/oauth2/jwks – 返回 JWK 集(APIM 用于获取签名密钥)。
  • OpenID 配置: https://<app-fqdn>/.well-known/openid-configuration – OIDC 发现 JSON(包含 issuertoken_endpointjwks_uri 等)。

APIM 会指向OpenID 配置 URL,从中发现 jwks_uri。例如,如果你的 Container App FQDN 是 my-mcp-app.eastus.azurecontainerapps.io,则 APIM 的 <openid-config url="..."> 应使用 https://my-mcp-app.eastus.azurecontainerapps.io/.well-known/openid-configuration。(默认情况下,Spring 会将该元数据中的 issuer 设置为相同的基础 URL(配置模型 :: Spring Authorization Server)。)

配置 Azure API Management (validate-jwt)

在 Azure APIM 中,添加一个入站策略,使用 <validate-jwt> 策略根据你的 Spring Authorization Server 验证传入的 JWT。简单配置可以使用 OpenID Connect 元数据 URL。示例策略片段:

<inbound>
  <validate-jwt header-name="Authorization" require-scheme="Bearer">
    <openid-config url="https://my-mcp-app.eastus.azurecontainerapps.io/.well-known/openid-configuration" />
    <audiences>
      <audience>mcp-client</audience>  <!-- Expected audience in the JWT -->
    </audiences>
    <issuers>
      <issuer>https://my-mcp-app.eastus.azurecontainerapps.io</issuer>
    </issuers>
  </validate-jwt>
  <!-- (optional) other policies -->
</inbound>

该策略指示 APIM 从 Spring Auth Server 获取 OpenID 配置,检索其 JWKS,并验证每个令牌是否由受信任的密钥签名且具有正确的受众。(如果省略 <issuers>,APIM 会自动使用元数据中的 issuer 声明。)<audience> 应与令牌中的客户端 ID 或 API 资源标识符匹配(上例中设置为 "mcp-client")。这与微软关于使用 <openid-config>validate-jwt 文档一致(Azure API Management 策略参考 - validate-jwt | Microsoft Learn)。

验证通过后,APIM 会将请求(包括原始的 Authorization 头)转发到后端。由于 Spring 应用也是资源服务器,它会再次验证令牌,但 APIM 已经确保了令牌的有效性。(开发时,你可以只依赖 APIM 的校验并禁用应用中的额外校验,但保持双重校验更安全。)

示例设置

设置示例值说明
Issuerhttps://my-mcp-app.eastus.azurecontainerapps.io你的 Container App 的 URL(基础 URI)
Token endpointhttps://my-mcp-app.eastus.azurecontainerapps.io/oauth2/token默认 Spring 令牌端点(配置模型 :: Spring Authorization Server
JWKS endpointhttps://my-mcp-app.eastus.azurecontainerapps.io/oauth2/jwks默认 JWK 集端点(配置模型 :: Spring Authorization Server
OpenID Confighttps://my-mcp-app.eastus.azurecontainerapps.io/.well-known/openid-configurationOIDC 发现文档(自动生成)
APIM audiencemcp-clientOAuth 客户端 ID 或 API 资源名称
APIM policy<openid-config url="https://.../.well-known/openid-configuration" /><validate-jwt> 使用此 URL([Azure API Management 策略参考 - validate-jwt

常见问题

  • HTTPS/TLS: APIM 网关要求 OpenID/JWKS 端点必须是 HTTPS 且证书有效。默认情况下,Azure Container Apps 会为 Azure 托管域提供受信任的 TLS 证书(Azure Container Apps 中的自定义域名和免费托管证书 | Microsoft Learn)。如果使用自定义域名,务必绑定证书(可以使用 Azure 的免费托管证书功能)(Azure Container Apps 中的自定义域名和免费托管证书 | Microsoft Learn)。如果 APIM 无法信任端点证书,<validate-jwt> 将无法获取元数据。
  • 端点可访问性: 确保 Spring 应用的端点对 APIM 可达。使用 --ingress external(或在门户中启用入口)是最简单的方式。如果选择了内部或 vNet 绑定环境,APIM(默认是公共的)可能无法访问,除非它们在同一 VNet 中。测试环境建议使用公共入口,以便 APIM 能调用 .well-known/jwks URL。
  • 启用 OpenID 发现: 默认情况下,Spring Authorization Server 不会暴露 /.well-known/openid-configuration,除非启用了 OIDC。确保在安全配置中包含 .oidc(Customizer.withDefaults())(见上文),以激活提供者配置端点(配置模型 :: Spring Authorization Server)。否则 APIM 的 <openid-config> 调用会返回 404。
  • Audience 声明: Spring 默认将 aud 声明设置为客户端 ID。如果 APIM 的 <audience> 校验失败,可能需要自定义令牌(如上所示)或调整 APIM 策略。确保 JWT 中的受众与 <audience> 配置匹配。
  • JSON 元数据解析: OpenID 配置的 JSON 必须有效。Spring 默认配置会输出标准的 OIDC 元数据文档。请确认其中包含正确的 issuerjwks_uri。如果 Spring 部署在代理或基于路径的路由后面,请仔细检查元数据中的 URL,APIM 会直接使用这些值。
  • 策略顺序: 在 APIM 策略中,确保 <validate-jwt> 位于 任何路由到后端之前。否则,可能会有请求未经过有效令牌验证就到达应用。同时确保 <validate-jwt> 直接放在 <inbound> 下(不要嵌套在其他条件中),以保证 APIM 正确应用。

按照上述步骤,你可以在 Azure Container Apps 中运行 Spring AI MCP 服务器,并让 Azure API Management 使用最简策略验证传入的 OAuth2 JWT。关键点是:公开暴露带 TLS 的 Spring Auth 端点,启用 OIDC 发现,并将 APIM 的 validate-jwt 指向 OpenID 配置 URL(以便自动获取 JWKS)。此配置适合开发/测试环境;生产环境中请考虑妥善管理密钥、令牌生命周期及 JWKS 的密钥轮换。 参考资料: 请参阅 Spring Authorization Server 文档了解默认端点(Configuration Model :: Spring Authorization Server)和 OIDC 配置(Configuration Model :: Spring Authorization Server);请参阅 Microsoft APIM 文档中的 validate-jwt 示例(Azure API Management policy reference - validate-jwt | Microsoft Learn);以及 Azure Container Apps 文档中的部署和证书(Deploy Java Spring Boot apps to Azure Container Apps - Java on Azure | Microsoft Learn)(Custom domain names and free managed certificates in Azure Container Apps | Microsoft Learn)。

免责声明
本文件使用 AI 翻译服务 Co-op Translator 进行翻译。虽然我们力求准确,但请注意,自动翻译可能包含错误或不准确之处。原始语言的原文应被视为权威来源。对于重要信息,建议使用专业人工翻译。对于因使用本翻译而产生的任何误解或误释,我们不承担任何责任。