Spring AI框架提供了对Model Context Protocol(简称MCP)的全面支持,使AI模型能够以标准化方式与外部工具和资源进行安全交互。借助Spring AI,开发者仅需少量代码即可构建功能完备的MCP服务器,为AI模型提供丰富的功能扩展。
MCP 中的授权和安全
MCP服务器默认支持通过STDIO传输在本地环境中运行。当需要将服务公开至网络环境时,则必须通过HTTP端点提供服务。虽然私有部署场景下可能无需严格的身份验证机制,但在企业级应用中,必须实施完善的安全防护和权限管理体系。2025年3月26日发布的最新MCP规范版本(2025-03-26)针对这一需求,基于业界广泛采用的OAuth2框架,为客户端与服务器间的安全通信建立了标准规范。
在深入实现细节前,让我们简要回顾OAuth2的核心概念。根据规范草案,MCP服务器需要同时承担资源服务器和授权服务器双重角色:
作为 资源服务器,它通过验证请求头中的Authorization字段执行访问控制。该字段必须包含有效的OAuth2访问令牌(access_token),这个令牌可以是自包含的JSON Web Token(JWT),也可以是需验证的不透明字符串。当令牌缺失或无效(如格式错误、过期或接收方不匹配)时,服务器将拒绝请求。典型的安全请求示例如下:
复制curl https://mcp.example.com/sse -H "Authorization: Bearer <a valid access token>"
作为授权服务器,MCP服务还需安全地颁发访问令牌。在令牌发放前,服务器会验证客户端凭证,某些场景下还需确认终端用户身份。授权服务器同时负责定义令牌属性,包括有效期、作用域(scope)和目标受众(audience)等关键参数。
借助Spring Security和Spring Authorization Server,我们可以为现有Spring MCP服务添加这两类安全能力。
image.png
为 Spring MCP 服务器添加 OAuth2
本示例基于Spring AI开发的一个图书的Mcp Server,代码如下:
复制@Service @Slf4j publicclass BookService { /** * 查询图书信息 * @param bookName 图书名称 * @return 图书信息 */ @Tool(description = "查询图书信息") @SneakyThrows public BookInfo getBookInfo(@ToolParam(description = "图书名称") String bookName,@ToolParam(description = "图书ID") Long bookId) { // 构建一个静态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安全支持,暂不涉及客户端交互细节。
第一步:添加依赖配置
在项目的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 配置类中配置基础客户端信息,用于后续的令牌获取测试:
复制@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。
第三步:实现安全配置
创建安全配置类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"开头。现在我们可以使用这个访问令牌发出请求,它们应该能成功。例如使用 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"字段中。即可建立安全连接。
image.png
进阶安全方案展望
目前来看,SpringAi Mcp的oauth2集成方案只能通过客户端模式与SSE进行安全连接,无法对单个tool进行精细化权限控制。接下来主要有两个方向:
- 客户端认证升级:增强MCP客户端功能,使其支持"授权码模式"(Authorization Code Grant)。该模式允许终端用户使用个人凭证登录,获取用户绑定的访问令牌,为实现基于角色的访问控制(RBAC)等精细化权限管理奠定基础。
- 分布式认证架构:探索集成外部专业OAuth2授权服务器的方案,使MCP服务器仅需专注于资源服务器功能的实现。