使用 Spring AI 保護 MCP 伺服器

工程 | Daniel Garnier-Moiroux | 2025年9月30日 | ...

模型上下文協議(Model Context Protocol),簡稱MCP,已經席捲了整個AI世界。如果您一直關注我們的部落格,您可能已經閱讀了該主題的介紹文章,即將您的AI連線到一切:Spring AI的MCP Boot Starters。MCP的安全性方面發展迅速,最新版本的規範得到了生態系統越來越多的支援。為了滿足Spring使用者的需求,我們已經在Github上孵化了一個專門的專案:spring-ai-community/mcp-security。本週,我們釋出了首個版本,您現在可以將其新增到基於Spring AI 1.1.x的應用程式中。在這篇文章中,我們將探討

使用OAuth 2保護MCP伺服器

根據MCP規範的授權部分,透過HTTP暴露的MCP伺服器必須使用OAuth 2訪問令牌進行保護。對MCP伺服器的任何呼叫都必須包含一個頭部Authorization: Bearer <access_token>,其中訪問令牌是從授權伺服器(例如:Okta、Github等)代表使用者獲取的。MCP伺服器還必須明確宣告它信任的授權伺服器,以便MCP客戶端可以動態發現它們,向授權伺服器註冊自己並獲取令牌。我們稍後會討論授權伺服器,但目前我們假設您已經配置並運行了一個授權伺服器,地址為<AUTH_SERVER_URL>,我們將把我們的MCP伺服器連線到它。如果您需要設定授權伺服器,請參見下一節

首先,將所需的依賴項新增到您的專案中

Maven


<dependencies>

    <!-- Spring AI MCP starter -->
    <dependency>
        <groupId>org.springframework.ai</groupId>
        <artifactId>spring-ai-starter-mcp-server-webmvc</artifactId>
    </dependency>
    <!-- MCP Security -->
    <dependency>
        <groupId>org.springaicommunity</groupId>
        <artifactId>mcp-server-security</artifactId>
        <version>0.0.3</version>
    </dependency>
    <!-- MCP Security dependencies -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-oauth2-resource-server</artifactId>
    </dependency>

</dependencies>

Gradle

implementation("org.springframework.ai:spring-ai-starter-mcp-server-webmvc")
implementation("org.springaicommunity:mcp-server-security:0.0.3")
implementation("org.springframework.boot:spring-boot-starter-oauth2-resource-server")

確保在您的application.properties中啟用了MCP伺服器,並注入您的授權伺服器URL

spring.ai.mcp.server.name=my-cool-mcp-server
# Supported protocols: STREAMABLE, STATELESS
spring.ai.mcp.server.protocol=STREAMABLE
# Choose any property name you'd like
# You MAY use the usual Spring well-known "spring.security.oauth2.resourceserver.jwt.issuer-uri".
authorization.server.url=<AUTH_SERVER_URL>

我們將新增一個簡單的MCP工具,它根據輸入語言(“英語”、“法語”等)和使用者姓名向用戶打招呼。


@Service
public class MyToolsService {

    @McpTool(name = "greeter", description = "A tool that greets you, in the selected language")
    public String greet(
            @ToolParam(description = "The language for the greeting (example: english, french, ...)") String language
    ) {
        if (!StringUtils.hasText(language)) {
            language = "";
        }
        var authentication = SecurityContextHolder.getContext().getAuthentication();
        var name = authentication.getName();
        return switch (language.toLowerCase()) {
            case "english" -> "Hello, %s!".formatted(name);
            case "french" -> "Salut %s!".formatted(name);
            default -> ("I don't understand language \"%s\". " +
                        "So I'm just going to say Hello %s!").formatted(language, name);
        };
    }

}

在此示例中,該工具將從SecurityContext中查詢使用者的姓名,並建立個性化問候語。使用者的姓名將是用於驗證請求的JWT訪問令牌中的sub宣告。

最後但同樣重要的是,我們新增一個用於安全配置的類,例如McpServerSecurityConfiguration


@Configuration
@EnableWebSecurity
class McpServerSecurityConfiguration {

    @Value("${authorization.server.url}")
    private String authServerUrl;

    @Bean
    SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        return http
                // Enforce authentication with token on EVERY request
                .authorizeHttpRequests(auth -> auth.anyRequest().authenticated())
                // Configure OAuth2 on the MCP server
                .with(
                        McpServerOAuth2Configurer.mcpServerOAuth2(),
                        (mcpAuthorization) -> {
                            // REQUIRED: the authserver's issuer URI
                            mcpAuthorization.authorizationServer(this.authServerUrl);
                            // OPTIONAL: enforce the `aud` claim in the JWT token.
                            mcpAuthorization.validateAudienceClaim(true);
                        }
                )
                .build();
    }

}

使用./mvnw spring-boot:run./gradlew bootRun執行應用程式。它應該在8080埠啟動。如果您嘗試訪問https://:8080/mcp上的MCP伺服器,您將收到一個WWW-authenticate,指示OAuth2資源元資料URL

curl -XPOST  -w '%{http_code}\n%header{www-authenticate}' https://:8080/mcp
#
# Will print out:
#
# 401
# Bearer resource_metadata=https://:8080/.well-known/oauth-protected-resource/mcp

元資料URL本身將向潛在客戶端指示授權伺服器的位置

curl https://:8080/.well-known/oauth-protected-resource/mcp
#
# Will print out:
#
# {
#   "resource": "https://:8080/mcp",
#   "authorization_servers": [
#     "<AUTH_SERVER_URL>"
#   ],
#   "resource_name": "Spring MCP Resource Server",
#   "bearer_methods_supported": [
#     "header"
#   ]
# }

這對人類來說用處不大,但它有助於其他程式找到您的MCP伺服器的身份驗證入口點。每個基於AI的應用程式都有自己獨特的方式新增MCP伺服器,但除錯伺服器的一個好工具是MCP檢查器。您可以輕鬆地執行它

npx @modelcontextprotocol/[email protected]

在使用者介面中,您必須設定伺服器的URL,然後點選“開啟身份驗證設定”

MCP inspector screenshot

在身份驗證設定中,選擇“快速OAuth流程”。

MCP inspector screenshot: Authentication Settings page

這將重定向您到授權伺服器。登入後,您將被重定向回MCP檢查器,它將顯示一個成功訊息和訪問令牌的前幾個字元。從那裡,您應該能夠連線並最終呼叫我們的“問候器”工具

MCP inspector screenshot: Call tool page

在上面的截圖中,按順序您可以執行以下操作

  1. 選擇工具選項卡
  2. 點選列出工具
  3. 選擇greeter工具
  4. 填寫引數並呼叫工具

這樣,您就擁有了第一個符合規範並受OAuth2保護的MCP伺服器。此實現存在變體,例如MCP伺服器上的所有內容都可公開訪問(例如“列出工具”),但工具呼叫本身除外。這不符合規範,但滿足某些特定需求。您可以在mcp-security文件的專用部分中瞭解更多資訊。

當然,為了讓使用者登入,您必須將您的MCP伺服器連線到一個符合MCP所需規範的授權伺服器,例如動態客戶端註冊。雖然有許多SaaS選項可用,您也可以使用Spring Authorization Server自行編寫。

MCP相容的Spring授權伺服器

要使用Spring建立一個MCP相容的授權伺服器,請建立一個新的Spring專案,包含Spring Authorization Server,並新增MCP特有的

Maven


<dependency>
    <groupId>org.springaicommunity</groupId>
    <artifactId>mcp-authorization-server</artifactId>
    <version>0.0.3</version>
</dependency>

Gradle

implementation("org.springaicommunity:mcp-authorization-server:0.0.3")

您可以按照通常的方式配置授權伺服器(參見參考文件)。以下是一個示例application.yml,用於註冊一個預設客戶端和一個預設使用者

spring:
  application:
    name: sample-authorization-server
  security:
    oauth2:
      authorizationserver:
        client:
          default-client:
            token:
              access-token-time-to-live: 1h
            registration:
              client-id: "default-client-id"
              client-secret: "{noop}default-client-secret"
              client-authentication-methods:
                - "client_secret_basic"
                - "none"
              authorization-grant-types:
                - "authorization_code"
                - "client_credentials"
              redirect-uris:
                - "http://127.0.0.1:8080/authorize/oauth2/code/authserver"
                - "https://:8080/authorize/oauth2/code/authserver"
                # mcp-inspector
                - "https://:6274/oauth/callback"
    user:
      # A single user, named "user"
      name: user
      password: password

server:
  port: 9000
  servlet:
    session:
      cookie:
        # Override the default cookie name (JSESSIONID).
        # This allows running multiple Spring apps on localhost, and they'll each have their own cookie.
        # Otherwise, since the cookies do not take the port into account, they are confused.
        name: MCP_AUTHORIZATION_SERVER_SESSIONID

這只是一個例子,您可能需要編寫自己的配置。透過此配置,將註冊一個使用者(使用者名稱:user,密碼:password)。還將有一個OAuth2客戶端(default-client-id / default-client-secret)。然後,您可以使用通常的Spring Security API,即安全過濾器鏈,啟用所有授權伺服器功能。


@Bean
SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
    return http
            // all requests must be authenticated
            .authorizeHttpRequests(auth -> auth.anyRequest().authenticated())
            // enable authorization server customizations
            .with(McpAuthorizationServerConfigurer.mcpAuthorizationServer(), withDefaults())
            // enable form-based login, for user "user"/"password"
            .formLogin(withDefaults())
            .build();
}

這樣,您的Spring授權伺服器將支援OAuth 2動態客戶端註冊以及OAuth 2資源指示符。將您的MCP伺服器連線到此授權伺服器與大多數AI工具(如Claude Desktop、Cursor或MCP檢查器)相容。

超越OAuth 2:API金鑰

雖然MCP規範強制要求使用OAuth2進行安全認證,但許多環境不具備支援此用例的基礎設施。為了在缺乏OAuth 2的環境中使用,許多客戶端,包括MCP檢查器本身,允許您在發出請求時傳遞自定義請求頭。這為替代認證流程打開了大門,包括基於API金鑰的安全認證。MCP安全專案支援API金鑰,我們將在下面進行展示。

首先,將依賴項新增到您的專案中


<dependencies>

    <!-- Spring AI MCP starter -->
    <dependency>
        <groupId>org.springframework.ai</groupId>
        <artifactId>spring-ai-starter-mcp-server-webmvc</artifactId>
    </dependency>
    <!-- MCP Security -->
    <dependency>
        <groupId>org.springaicommunity</groupId>
        <artifactId>mcp-server-security</artifactId>
        <version>0.0.3</version>
    </dependency>
    <!-- MCP Security dependencies -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-security</artifactId>
    </dependency>

</dependencies>

Gradle

implementation("org.springframework.ai:spring-ai-starter-mcp-server-webmvc")
implementation("org.springaicommunity:mcp-server-security:0.0.3")
implementation("org.springframework.boot:spring-boot-starter-security")

確保在您的application.properties中啟用了MCP伺服器

spring.ai.mcp.server.name=my-cool-mcp-server
# Supported protocols: STREAMABLE, STATELESS
spring.ai.mcp.server.protocol=STREAMABLE

透過API金鑰進行身份驗證的“實體”,例如使用者或服務賬戶,由ApiKeyEntity表示。MCP伺服器會檢查特定的請求頭以獲取API金鑰,載入實體,並驗證金鑰。您可以提供自己的實體實現和實體倉庫,以進行特定的安全驗證。

有了這些,您就可以按照通常的Spring Security方式配置專案的安全性了


@Configuration
@EnableWebSecurity
class McpServerConfiguration {

    @Bean
    SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        return http.authorizeHttpRequests(authz -> authz.anyRequest().authenticated())
                .with(
                        McpApiKeyConfigurer.mcpServerApiKey(),
                        (apiKey) -> apiKey.apiKeyRepository(apiKeyRepository())
                )
                .build();
    }

    private ApiKeyEntityRepository<ApiKeyEntityImpl> apiKeyRepository() {
        var apiKey = ApiKeyEntityImpl.builder()
                .name("test api key")
                .id("api01")
                .secret("mycustomapikey")
                .build();

        return new InMemoryApiKeyEntityRepository<>(List.of(apiKey));
    }

}

這裡我們使用一個儲存簡單金鑰的API Key儲存庫。然後您應該能夠使用請求頭X-API-key: api01.mycustomapikey呼叫您的MCP伺服器。X-API-key是傳遞API金鑰的預設請求頭名稱,後跟請求頭值{id}.{secret}。金鑰以bcrypt雜湊形式儲存在伺服器端。mcpServerApiKey()配置器提供了更改請求頭名稱的選項,甚至提供了專門的API來從傳入的HTTP請求中提取API金鑰。

改進MCP安全性

如果您想了解更多資訊,請訪問spring-ai-community/mcp-security專案,獲取文件和示例。您還將找到對使用Spring AI和Spring Security進行客戶端MCP安全性的支援。在您自己的專案和應用程式中嘗試一下,與生態系統的其餘部分進行測試,並幫助我們改進它!我們歡迎貢獻,包括反饋和問題。

在另一篇博文中,我們將介紹如何使用org.springaiframework:mcp-client-security模組在客戶端實現OAuth 2。

獲取 Spring 新聞通訊

透過 Spring 新聞通訊保持聯絡

訂閱

領先一步

VMware 提供培訓和認證,助您加速進步。

瞭解更多

獲得支援

Tanzu Spring 提供 OpenJDK™、Spring 和 Apache Tomcat® 的支援和二進位制檔案,只需一份簡單的訂閱。

瞭解更多

即將舉行的活動

檢視 Spring 社群所有即將舉行的活動。

檢視所有