使用OAuth2保护Spring AI MCP服务!

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?sessionId=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服务器仅需专注于资源服务器功能的实现。

相关推荐
阿常111 小时前
解决Maven项目中报错“java不支持版本6即更高的版本 7”
java·开发语言·maven
汤姆大聪明1 小时前
Spring MVC入门
java·spring·mvc
Stay Passion2 小时前
Java 实用工具类:Apache Commons IO 的 IOUtils
java·开发语言·apache
今天也是元气满满的一天呢3 小时前
java学习之数据结构:一、数组
java·数据结构·学习
heyCHEEMS4 小时前
最大子段和 Java
java·开发语言·算法
-曾牛4 小时前
探索 Spring AI 的 ChatClient API:构建智能对话应用的利器
java·人工智能·spring boot·后端·spring·springai·ai指南
magic 2454 小时前
Spring 命名空间注入:p、c 与 .util 的深度解析
java·前端·spring
橘猫云计算机设计5 小时前
基于springboot的金院银行厅预约系统的设计及实现(源码+lw+部署文档+讲解),源码可白嫖!
java·数据库·spring boot·后端·爬虫·spring·毕业设计
Aurora_NeAr5 小时前
Spring IoC容器的设计与实现
java·后端·spring