使用 OAuth2 保護 Spring AI MCP 伺服器

工程 | Daniel Garnier-Moiroux | 2025 年 4 月 2 日 | ...

Spring AI 提供對模型上下文協議(Model Context Protocol)(簡稱 MCP)的支援,該協議允許 AI 模型以結構化的方式與外部工具和資源進行互動和訪問。藉助 Spring AI,開發者只需幾行程式碼即可建立自己的 MCP 伺服器,並向 AI 模型公開其功能。

MCP 中的授權與安全

MCP 伺服器可以使用 STDIO 傳輸在本地執行。要將 MCP 伺服器公開到外部,它必須暴露一些標準的 HTTP 端點。雖然私下使用的 MCP 伺服器可能不需要嚴格的認證,但企業部署需要對暴露的端點進行健壯的安全和許可權管理。最新版本的 MCP 規範(2025-03-26)(上週釋出)解決了這一挑戰。它為保護客戶端和伺服器之間的通訊奠定了基礎,並利用了廣泛使用的 OAuth2 框架

雖然我們不會在這篇博文中全面回顧 OAuth2,但快速複習一下可能會很有用。在規範草案中,MCP 伺服器既是資源伺服器(Resource Server),也是授權伺服器(Authorization Server)。

作為資源伺服器,它透過檢查 Authorization 頭來對傳入請求執行授權檢查。該頭必須包含一個 OAuth2 access_token,這是一個表示客戶端“許可權”的字串。該令牌可以是 JSON Web Token (JWT),也可以是一個本身不攜帶資訊的 opaque 字串。如果令牌丟失或無效(格式錯誤、過期、接收者錯誤等),資源伺服器將拒絕該請求。使用這些令牌,典型的請求可能看起來像這樣

curl https://mcp.example.com/sse
    -H "Authorization: Bearer <a valid access token>"

作為授權伺服器,MCP 伺服器還必須能夠以安全的方式為客戶端頒發 access_token。在頒發令牌之前,伺服器會驗證客戶端的憑據,在某些情況下還會驗證嘗試訪問伺服器的使用者身份。授權伺服器將決定令牌的特性:其過期時間、範圍、目標受眾等。

使用 Spring Security 和 Spring Authorization Server,我們可以輕鬆地為現有的 Spring MCP 伺服器新增這兩個功能。

OAuth2 diagram with MCP Client and MCP Server

為 Spring MCP 伺服器新增 OAuth2 支援

在本例中,我們將向示例 MCP 伺服器(來自 Spring AI 示例倉庫中的 “Weather” MCP 工具)新增 OAuth 2 支援。我們不會探討互動的客戶端部分,只確保我們的伺服器能夠頒發令牌並對其進行驗證。

首先,在 pom.xml 中匯入所需的 Boot Starter

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-oauth2-resource-server</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-oauth2-authorization-server</artifactId>
</dependency>

然後,在我們的 application.properties 中配置一個 OAuth2 客戶端,以便我們可以請求訪問令牌

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.client-authentication-methods=client_secret_basic
spring.security.oauth2.authorizationserver.client.oidc-client.registration.authorization-grant-types=client_credentials

這是最簡單的客戶端。我們可以透過直接傳送 POST 請求與授權伺服器進行互動,無需瀏覽器,並使用硬編碼的憑據 mcp-client / secret

最後一步是啟用授權伺服器和資源伺服器功能。為此,我們建立一個安全功能的配置類,例如 SecurityConfiguration,並在其中暴露一個 SecurityFilterChain bean

import static org.springframework.security.oauth2.server.authorization.config.annotation.web.configurers.OAuth2AuthorizationServerConfigurer.authorizationServer;

@Configuration
@EnableWebSecurity
class SecurityConfiguration {

	@Bean
	SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
		return http.authorizeHttpRequests(auth -> auth.anyRequest().authenticated())
			.with(authorizationServer(), Customizer.withDefaults())
			.oauth2ResourceServer(resource -> resource.jwt(Customizer.withDefaults()))
			.csrf(CsrfConfigurer::disable)
			.cors(Customizer.withDefaults())
			.build();
	}

}

這個過濾器鏈將執行多項任務

  • 確保每個請求都經過認證。有了這個,我們的 MCP 伺服器將只允許帶有 access_token 的請求。
  • 啟用 Spring Authorization Server 和 Spring Resource Server。
  • 關閉 CSRF(跨站請求偽造)。MCP 伺服器並非為基於瀏覽器的互動而設計,因此不需要 CSRF。
  • 開啟 CORS(跨域資源共享)支援,這樣我們可以使用 MCP inspector 來演示伺服器。

至此,我們的應用程式已受到保護,並且只接受帶有訪問令牌的請求。否則,請求將被拒絕,並返回 HTTP 401 Unauthorized 錯誤。例如

curl https://:8080/sse --fail-with-body
#
# Response:
#
# curl: (22) The requested URL returned error: 401

要使用我們的 MCP 伺服器,我們首先需要獲取一個訪問令牌。我們使用 client_credentials OAuth2 授權型別,這用於“機器到機器”或“服務賬戶”場景

curl -XPOST https://:8080/oauth2/token --data grant_type=client_credentials --user mcp-client:secret
#
# Response:
#
# {"access_token":"<YOUR-ACCESS-TOKEN>","token_type":"Bearer","expires_in":299}%

複製 access_token 的值。它以字母 "ey" 開頭。現在我們可以使用此訪問令牌傳送請求,並且請求應該成功。例如,使用 curl,您可以將 YOUR_ACCESS_TOKEN 替換為您上面複製的值

curl https://:8080/sse -H"Authorization: Bearer YOUR_ACCESS_TOKEN"
#
# Response:
#
# id:918d5ebe-9ae5-4b04-aae8-c1ff8cdbb6e0
# event:endpoint
# data:/mcp/message?sessionId=918d5ebe-9ae5-4b04-aae8-c1ff8cdbb6e0

從版本 0.6.0 開始,也可以直接在 MCP inspector 中使用訪問令牌。只需啟動 inspector,並將訪問令牌貼上到左側選單中的 "Authentication > Bearer" 欄位。然後點選 Connect:您應該能夠進行 MCP 呼叫。

MCP inspector screenshot

如果您想自己執行此示例,可以檢視 spring-ai-examples 倉庫中的示例程式碼

下一步是什麼?

在本例中,我們在 MCP 伺服器中實現了基礎的 OAuth2 功能。

顯而易見的下一步是更新 MCP 客戶端,使其能夠與伺服器進行認證,並使用“授權碼”(authorization code)OAuth2 授權型別。透過這種流程,使用者可以使用自己的憑據登入,並獲取與使用者繫結的令牌,從而實現更細粒度的許可權控制,例如基於角色的訪問控制(RBAC)。

我們還將探索使用外部 OAuth2 授權伺服器來頒發令牌,並且只在我們的 MCP 伺服器中實現資源伺服器的功能。

訂閱 Spring 新聞通訊

隨時關注 Spring 新聞通訊

訂閱

搶佔先機

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

瞭解更多

獲取支援

Tanzu Spring 透過一個簡單的訂閱提供對 OpenJDK™、Spring 和 Apache Tomcat® 的支援和二進位制檔案。

瞭解更多

近期活動

檢視 Spring 社群的所有近期活動。

檢視全部