将 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。
(使用 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 domain (Custom domain names and free managed certificates in Azure Container Apps | Microsoft Learn)). The command output includes the app’s FQDN (e.g. my-mcp-app.eastus.azurecontainerapps.io), which becomes the issuer URL base. Ensure HTTP ingress is enabled (as above) so APIM can reach the app. In a test/dev setup, use the --ingress external option (or bind a custom domain with TLS per Microsoft docs (Custom domain names and free managed certificates in Azure Container Apps | Microsoft Learn)). Store any sensitive properties (like OAuth client secrets) in Container Apps secrets or Azure Key Vault, and map them into the container as environment variables.
In your Spring Boot app’s code, include the Spring Authorization Server and Resource Server starters. Configure a RegisteredClient (for the client_credentials grant in dev/test) and a JWT key source. For example, in 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 for tokens and /oauth2/jwks for the JSON Web Key Set. (By default Spring’s AuthorizationServerSettings maps /oauth2/token and /oauth2/jwks (Configuration Model :: Spring Authorization Server).) The server will issue JWT access tokens signed by the RSA key above, and publish its public key at https://<your-app>:/oauth2/jwks.
Enable OpenID Connect discovery: To let APIM automatically retrieve the issuer and JWKS, enable the OIDC provider configuration endpoint by adding .oidc(Customizer.withDefaults()),在你的安全配置中使用(配置模型 :: 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, which APIM can use for metadata. Finally, you may want to customize the JWT audience claim so that APIM’s <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"], matching the client ID or scope expected by APIM.
After deploying, your app’s issuer URL will be https://<app-fqdn>, e.g. https://my-mcp-app.eastus.azurecontainerapps.io. Its OAuth2 endpoints are:
https://<app-fqdn>/oauth2/token – clients obtain tokens here (client_credentials flow).https://<app-fqdn>/oauth2/jwks – returns the JWK set (used by APIM to get signing keys).https://<app-fqdn>/.well-known/openid-configuration – OIDC discovery JSON (contains issuer, token_endpoint, jwks_uri, etc.).APIM will point to the OpenID configuration URL, from which it discovers the jwks_uri. For example, if your Container App FQDN is my-mcp-app.eastus.azurecontainerapps.io, then APIM’s <openid-config url="..."> should use https://my-mcp-app.eastus.azurecontainerapps.io/.well-known/openid-configuration. (By default Spring will set the issuer in that metadata to the same base URL (Configuration Model :: Spring Authorization Server).)
validate-jwt)In Azure APIM, add an inbound policy that uses the <validate-jwt> 策略,用于验证传入的 JWT 是否来自你的 Spring Authorization Server。对于简单配置,可以使用 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 will use the issuer claim from the metadata automatically.) The <audience> should match your client ID or API resource identifier in the token (in the example above, we set it to "mcp-client"). This is consistent with Microsoft’s documentation on using validate-jwt with <openid-config> (Azure API Management policy reference - validate-jwt | Microsoft Learn).
After validation, APIM will forward the request (including the original Authorization header) to the backend. Since the Spring app is also a resource server, it will re-validate the token, but APIM has already ensured its validity. (For development, you can rely on APIM’s check and disable additional checks in the app if desired, but it’s safer to keep both.)
| Setting | Example Value | Notes |
|---|---|---|
| Issuer | https://my-mcp-app.eastus.azurecontainerapps.io |
Your Container App’s URL (base URI) |
| Token endpoint | https://my-mcp-app.eastus.azurecontainerapps.io/oauth2/token |
Default Spring token endpoint (Configuration Model :: Spring Authorization Server) |
| JWKS endpoint | https://my-mcp-app.eastus.azurecontainerapps.io/oauth2/jwks |
Default JWK Set endpoint (Configuration Model :: Spring Authorization Server) |
| OpenID Config | https://my-mcp-app.eastus.azurecontainerapps.io/.well-known/openid-configuration |
OIDC discovery document (auto-generated) |
| APIM audience | mcp-client |
OAuth client ID or API resource name |
| APIM policy | <openid-config url="https://.../.well-known/openid-configuration" /> |
<validate-jwt> uses this URL ([Azure API Management policy reference - validate-jwt |
HTTPS/TLS: The APIM gateway requires that the OpenID/JWKS endpoint be HTTPS with a valid certificate. By default, Azure Container Apps provides a trusted TLS cert for the Azure-managed domain (Custom domain names and free managed certificates in Azure Container Apps | Microsoft Learn). If you use a custom domain, be sure to bind a certificate (you can use Azure’s free managed cert feature) (Custom domain names and free managed certificates in Azure Container Apps | Microsoft Learn). If APIM cannot trust the endpoint’s certificate, <validate-jwt> will fail to fetch the metadata.
Endpoint Accessibility: Ensure the Spring app’s endpoints are reachable from APIM. Using --ingress external (or enabling ingress in the portal) is simplest. If you chose an internal or vNet-bound environment, APIM (by default public) might not reach it unless placed in the same VNet. In a test setup, prefer public ingress so APIM can call the .well-known and /jwks URLs.
OpenID Discovery Enabled: By default, Spring Authorization Server does not expose the /.well-known/openid-configuration unless OIDC is enabled. Make sure to include .oidc(Customizer.withDefaults()) in your security config (see above) so that the provider configuration endpoint is active (Configuration Model :: Spring Authorization Server). Otherwise APIM’s <openid-config> call will 404.
Audience Claim: Spring’s default behavior is to set the aud claim to the client ID. If APIM’s <audience> check fails, you may need to customize the token (as shown above) or adjust the APIM policy. Ensure the audience in your JWT matches what you configure in <audience>.
JSON Metadata Parsing: The OpenID configuration JSON must be valid. Spring’s default config will emit a standard OIDC metadata document. Verify that it contains the correct issuer and jwks_uri. If you host Spring behind a proxy or path-based route, double-check the URLs in this metadata. APIM will use these values as-is.
Policy Ordering: In the APIM policy, place <validate-jwt> before any routing to the backend. Otherwise, calls might reach your app without a valid token. Also ensure <validate-jwt> appears immediately under <inbound> (not nested inside another condition) so that APIM applies it.
By following the above steps, you can run your Spring AI MCP server in Azure Container Apps and have Azure API Management validate incoming OAuth2 JWTs with a minimal policy. The key points are: expose the Spring Auth endpoints publicly with TLS, enable OIDC discovery, and point APIM’s validate-jwt at the OpenID config URL (so it can fetch the JWKS automatically). This setup is suitable for a dev/test environment; for production, consider proper secret management, token lifetimes, and rotating keys in JWKS as needed.
References: See Spring Authorization Server docs for default endpoints (Configuration Model :: Spring Authorization Server) and OIDC configuration (Configuration Model :: Spring Authorization Server); see Microsoft APIM docs for validate-jwt 示例(Azure API Management 策略参考 - validate-jwt | Microsoft Learn);以及 Azure Container Apps 的部署和证书文档(将 Java Spring Boot 应用部署到 Azure Container Apps - Java on Azure | Microsoft Learn)(Azure Container Apps 中的自定义域名和免费托管证书 | Microsoft Learn)。
免责声明:
本文件使用AI翻译服务Co-op Translator进行翻译。虽然我们力求准确,但请注意自动翻译可能存在错误或不准确之处。原始语言的文件应被视为权威来源。对于重要信息,建议采用专业人工翻译。对于因使用本翻译而产生的任何误解或误释,我们不承担任何责任。