使用 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 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":299}%

复制 access_token 的值。它以字母 "ey" 开头。现在我们可以使用此访问令牌发送请求,并且请求应该成功。例如,使用 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?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 社区的所有近期活动。

查看全部