使用OAuth2保護Spring AI MCP服務!
Spring AI框架提供了對Model Context Protocol(簡稱MCP)的全面支持,使AI模型能夠以標準化方式與外部工具和資源進行安全交互。借助Spring AI,開發(fā)者僅需少量代碼即可構建功能完備的MCP服務器,為AI模型提供豐富的功能擴展。
MCP 中的授權和安全
MCP服務器默認支持通過STDIO傳輸在本地環(huán)境中運行。當需要將服務公開至網絡環(huán)境時,則必須通過HTTP端點提供服務。雖然私有部署場景下可能無需嚴格的身份驗證機制,但在企業(yè)級應用中,必須實施完善的安全防護和權限管理體系。2025年3月26日發(fā)布的最新MCP規(guī)范版本(2025-03-26)針對這一需求,基于業(yè)界廣泛采用的OAuth2框架,為客戶端與服務器間的安全通信建立了標準規(guī)范。
在深入實現細節(jié)前,讓我們簡要回顧OAuth2的核心概念。根據規(guī)范草案,MCP服務器需要同時承擔資源服務器和授權服務器雙重角色:
作為 資源服務器,它通過驗證請求頭中的Authorization字段執(zhí)行訪問控制。該字段必須包含有效的OAuth2訪問令牌(access_token),這個令牌可以是自包含的JSON Web Token(JWT),也可以是需驗證的不透明字符串。當令牌缺失或無效(如格式錯誤、過期或接收方不匹配)時,服務器將拒絕請求。典型的安全請求示例如下:
curl https://mcp.example.com/sse -H "Authorization: Bearer <a valid access token>"
作為授權服務器,MCP服務還需安全地頒發(fā)訪問令牌。在令牌發(fā)放前,服務器會驗證客戶端憑證,某些場景下還需確認終端用戶身份。授權服務器同時負責定義令牌屬性,包括有效期、作用域(scope)和目標受眾(audience)等關鍵參數。
借助Spring Security和Spring Authorization Server,我們可以為現有Spring MCP服務添加這兩類安全能力。
image.png
為 Spring MCP 服務器添加 OAuth2
本示例基于Spring AI開發(fā)的一個圖書的Mcp Server,代碼如下:
@Service
@Slf4j
publicclass BookService {
/**
* 查詢圖書信息
* @param bookName 圖書名稱
* @return 圖書信息
*/
@Tool(description = "查詢圖書信息")
@SneakyThrows
public BookInfo getBookInfo(@ToolParam(description = "圖書名稱") String bookName,@ToolParam(description = "圖書ID") Long bookId) {
// 構建一個靜態(tài)BookInfo對象,只有bookName是根據參數傳入的
return BookInfo.builder()
.xxxx()
.build();
}
/**
* 獲取圖書列表
* @param limit 返回數量限制
* @return 圖書列表
*/
@Tool(description = "獲取圖書列表")
@SneakyThrows
public List<BookInfo> getBookList(@ToolParam(description = "返回數量限制,默認10") Integer limit) {
......
return bookList;
}
}
BookService中暴露了兩個Mcp Tool,一個用于獲取圖書的詳細信息,一個用于獲取圖書的列表。本文重點在于演示如何為其添加OAuth2安全支持,暫不涉及客戶端交互細節(jié)。
第一步:添加依賴配置
在項目的pom.xml中引入必要的Spring 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>
第二步:配置OAuth2客戶端
在OAuth2ClientConfig
配置類中配置基礎客戶端信息,用于后續(xù)的令牌獲取測試:
@Bean
public RegisteredClientRepository registeredClientRepository() {
RegisteredClient mcpClient = RegisteredClient.withId(UUID.randomUUID().toString())
.clientId("mcp-client")
.clientSecret("{noop}secret")
.clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC)
.authorizationGrantType(AuthorizationGrantType.CLIENT_CREDENTIALS)
.tokenSettings(TokenSettings.builder()
.accessTokenTimeToLive(Duration.ofHours(1))
.build())
.clientSettings(ClientSettings.builder()
.requireAuthorizationConsent(false)
.build())
.scope("LIST")
.build();
returnnew InMemoryRegisteredClientRepository(mcpClient);
}
此配置定義了一個使用客戶端憑證模式(client_credentials)的基礎客戶端,采用HTTP Basic認證方式,憑證硬編碼為mcp-client/secret。
第三步:實現安全配置
創(chuàng)建安全配置類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();
}
}
該配置實現了以下安全策略:
- 強制所有請求必須經過認證
- 同時啟用授權服務器和資源服務器功能
- 禁用CSRF防護(適用于非瀏覽器交互場景)
- 啟用CORS支持(便于使用MCP檢查器測試)
服務驗證測試
完成配置后,未經認證的請求將被拒絕,并顯示 HTTP 401 Unauthorized
錯誤
curl http://localhost:8080/sse --fail-with-body
#
# Response:
#
# curl: (22) The requested URL returned error: 401
要使用MCP 服務器,我們首先需要獲取一個訪問令牌。我們使用 client_credentials
OAuth2 授權類型,這用于"機器對機器"或"服務賬戶"場景:
curl -XPOST http://localhost:8080/oauth2/token --data grant_type=client_credentials --user mcp-client:secret
#
# Response:
#
# {"access_token":"<YOUR-ACCESS-TOKEN>","token_type":"Bearer","expires_in":3599}%
復制 access_token
的值,它以字母"ey"開頭?,F在我們可以使用這個訪問令牌發(fā)出請求,它們應該能成功。例如使用 curl
,您可以將 YOUR_ACCESS_TOKEN
替換為您上面復制的值:
curl http://localhost:8080/sse -H"Authorization: Bearer YOUR_ACCESS_TOKEN"
#
# Response:
#
# id:918d5ebe-9ae5-4b04-aae8-c1ff8cdbb6e0
# event:endpoint
# data:/mcp/message?sessinotallow=918d5ebe-9ae5-4b04-aae8-c1ff8cdbb6e0
從版本 0.6.0
開始,也可以直接在 mcp inspector
中使用訪問令牌。只需啟動檢查器,并將訪問令牌粘貼到左側菜單中的"Authentication > Bearer"字段中。即可建立安全連接。
進階安全方案展望
目前來看,SpringAi Mcp的oauth2集成方案只能通過客戶端模式與SSE進行安全連接,無法對單個tool進行精細化權限控制。接下來主要有兩個方向:
- 客戶端認證升級:增強MCP客戶端功能,使其支持"授權碼模式"(Authorization Code Grant)。該模式允許終端用戶使用個人憑證登錄,獲取用戶綁定的訪問令牌,為實現基于角色的訪問控制(RBAC)等精細化權限管理奠定基礎。
- 分布式認證架構:探索集成外部專業(yè)OAuth2授權服務器的方案,使MCP服務器僅需專注于資源服務器功能的實現。