1. 概述
注意: 此模块仍处于开发阶段,文档和 API 可能在后续版本中发生变更。
Spring AI MCP Security 模块为 Spring AI 的模型上下文协议(MCP)实现提供了全面的 OAuth 2.0 和 API Key 安全支持。该社区驱动的项目使开发者能够使用行业标准的认证与授权机制来保护 MCP 服务端和客户端。
此模块属于 spring-ai-community/mcp-security 项目,目前仅兼容 Spring AI 1.1.x 分支。它是社区驱动项目,尚未获得 Spring AI 或 MCP 项目的官方背书。
三大核心组件:
| 组件 | 用途 |
|---|---|
MCP Server Security |
为 Spring AI MCP 服务端提供 OAuth 2.0 资源服务器和 API Key 认证 |
MCP Client Security |
为 Spring AI MCP 客户端提供 OAuth 2.0 客户端支持 |
MCP Authorization Server |
基于 Spring Authorization Server 增强,针对 MCP 场景定制 |
核心能力:
- 使用
OAuth 2.0和API Key保护MCP服务端 - 为
MCP客户端配置OAuth 2.0授权流程 - 搭建专为
MCP工作流设计的授权服务器 - 实现对
MCP工具和资源的细粒度访问控制
2. MCP 服务端安全
为 Spring AI MCP 服务端提供 OAuth 2.0 资源服务器能力,同时提供基础的 API Key 认证支持。
注意: 此模块仅兼容
Spring WebMVC服务端。
2.1 依赖
xml
<dependencies>
<dependency>
<groupId>org.springaicommunity</groupId>
<artifactId>mcp-server-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<!-- 可选:OAuth2 支持 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-oauth2-resource-server</artifactId>
</dependency>
</dependencies>
2.2 OAuth 2.0 配置
基础 OAuth 2.0 配置
首先在 application.properties 中启用 MCP 服务端:
properties
spring.ai.mcp.server.name=my-cool-mcp-server
# 支持的协议:STREAMABLE, STATELESS
spring.ai.mcp.server.protocol=STREAMABLE
然后使用 Spring Security 标准 API 结合 MCP 配置器进行安全配置:
java
@Configuration
@EnableWebSecurity
class McpServerConfiguration {
@Value("${spring.security.oauth2.resourceserver.jwt.issuer-uri}")
private String issuerUrl;
@Bean
SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
return http
// 对 EVERY 请求强制要求 token 认证
.authorizeHttpRequests(auth -> auth.anyRequest().authenticated())
// 在 MCP 服务端上配置 OAuth2
.with(
McpServerOAuth2Configurer.mcpServerOAuth2(),
(mcpAuthorization) -> {
// 必填:issuerURI
mcpAuthorization.authorizationServer(issuerUrl);
// 可选:强制校验 JWT 中的 `aud` 声明
// 并非所有授权服务器都支持 resource indicators,因此可能缺失
// 默认值为 false
// 参见 RFC 8707 Resource Indicators for OAuth 2.0
mcpAuthorization.validateAudienceClaim(true);
}
)
.build();
}
}
仅保护工具调用
可以配置服务端仅对工具调用进行安全保护,其他 MCP 操作(如 initialize、tools/list)保持公开:
java
@Configuration
@EnableWebSecurity
@EnableMethodSecurity // 启用注解驱动的安全
class McpServerConfiguration {
@Value("${spring.security.oauth2.resourceserver.jwt.issuer-uri}")
private String issuerUrl;
@Bean
SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
return http
// 开放服务端上的所有请求
.authorizeHttpRequests(auth -> {
auth.requestMatcher("/mcp").permitAll();
auth.anyRequest().authenticated();
})
// 在 MCP 服务端上配置 OAuth2
.with(
McpResourceServerConfigurer.mcpServerOAuth2(),
(mcpAuthorization) -> {
// 必填:issuerURI
mcpAuthorization.authorizationServer(issuerUrl);
}
)
.build();
}
}
然后使用 @PreAuthorize 注解结合方法安全来保护工具调用:
java
@Service
public class MyToolsService {
@PreAuthorize("isAuthenticated()")
@McpTool(name = "greeter", description = "用你选择的语言向你问好")
public String greet(
@ToolParam(description = "问好的语言(例:english、french...)") String language
) {
if (!StringUtils.hasText(language)) {
language = "";
}
return switch (language.toLowerCase()) {
case "english" -> "Hello you!";
case "french" -> "Salut toi!";
default -> "我不懂 \"%s\" 语言,所以只能说 Hello!".formatted(language);
};
}
}
也可以通过 SecurityContextHolder 在工具方法中直接访问当前认证信息:
java
@McpTool(name = "greeter", description = "用指定语言向用户称呼名字问好")
@PreAuthorize("isAuthenticated()")
public String greet(
@ToolParam(description = "问好的语言(例:english、french...)") String language
) {
if (!StringUtils.hasText(language)) {
language = "";
}
var authentication = SecurityContextHolder.getContext().getAuthentication();
var name = authentication.getName();
return switch (language.toLowerCase()) {
case "english" -> "Hello, %s!".formatted(name);
case "french" -> "Salut %s!".formatted(name);
default -> ("我不懂 \"%s\" 语言,所以只能说 Hello %s!").formatted(language, name);
};
}
2.3 API Key 认证
MCP Server Security 模块也支持基于 API Key 的认证。你需要提供自己的 ApiKeyEntityRepository 实现来存储 ApiKeyEntity 对象。
框架提供了 InMemoryApiKeyEntityRepository 示例实现和默认的 ApiKeyEntityImpl:
注意:
InMemoryApiKeyEntityRepository使用bcrypt存储API Key,计算开销较高,不适合高并发生产环境。生产环境请实现自己的ApiKeyEntityRepository。
java
@Configuration
@EnableWebSecurity
class McpServerConfiguration {
@Bean
SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
return http.authorizeHttpRequests(authz -> authz.anyRequest().authenticated())
.with(
mcpServerApiKey(),
(apiKey) -> {
// 必填:API Key 的存储仓库
apiKey.apiKeyRepository(apiKeyRepository());
// 可选:携带 API Key 的请求头名称
// 例如此处,API Key 将通过 "CUSTOM-API-KEY: <value>" 发送
// 替代 .authenticationConverter(...)(见下方)
//
// apiKey.headerName("CUSTOM-API-KEY");
// 可选:自定义转换器,将 HTTP 请求转为认证对象
// 适用于 header 为 "Authorization: Bearer <value>" 的场景
// 替代 .headerName(...)(见上方)
//
// apiKey.authenticationConverter(request -> {
// var key = extractKey(request);
// return ApiKeyAuthenticationToken.unauthenticated(key);
// });
}
)
.build();
}
/**
* 提供 {@link ApiKeyEntity} 的存储仓库
*/
private ApiKeyEntityRepository<ApiKeyEntityImpl> apiKeyRepository() {
var apiKey = ApiKeyEntityImpl.builder()
.name("test api key")
.id("api01")
.secret("mycustomapikey")
.build();
return new InMemoryApiKeyEntityRepository<>(List.of(apiKey));
}
}
配置完成后,可通过请求头 X-API-key: api01.mycustomapikey 调用 MCP 服务端。
2.4 已知限制
| 限制 | 说明 |
|---|---|
SSE 不支持 |
已弃用的 SSE 传输不受支持,请使用 Streamable HTTP 或 Stateless 传输 |
WebFlux 不支持 |
不支持基于 WebFlux 的服务端 |
Opaque Token 不支持 |
请使用 JWT |
3. MCP 客户端安全
为 Spring AI MCP 客户端提供 OAuth 2.0 支持,同时兼容基于 HttpClient 的客户端(spring-ai-starter-mcp-client)和基于 WebClient 的客户端(spring-ai-starter-mcp-client-webflux)。
注意: 此模块仅支持
McpSyncClient。
3.1 依赖
xml
<dependency>
<groupId>org.springaicommunity</groupId>
<artifactId>mcp-client-security</artifactId>
</dependency>
3.2 授权流程
三种 OAuth 2.0 授权流程可供选择:
| 流程 | 适用场景 |
|---|---|
授权码流程 (authorization_code) |
用户级别权限,所有 MCP 请求均在用户请求上下文中发起 |
客户端凭证流程 (client_credentials) |
机器对机器通信,无人工参与 |
| 混合流程(Hybrid) | 部分操作(如 initialize、tools/list)无需用户在场即可执行,但工具调用需要用户级别权限 |
选择建议:当使用
Spring Boot属性配置MCP客户端时,推荐使用混合流程,因为工具发现是在启动时进行的,此时并无用户在场。
3.3 通用配置
所有流程都需要在 application.properties 中激活 Spring Security OAuth2 客户端支持:
yml
# 确保 MCP 客户端为同步模式
spring.ai.mcp.client.type=SYNC
# 授权码流程或混合流程
spring.security.oauth2.client.registration.authserver.client-id=<客户端ID>
spring.security.oauth2.client.registration.authserver.client-secret=<客户端密钥>
spring.security.oauth2.client.registration.authserver.authorization-grant-type=authorization_code
spring.security.oauth2.client.registration.authserver.provider=authserver
# 客户端凭证流程或混合流程
spring.security.oauth2.client.registration.authserver-client-credentials.client-id=<客户端ID>
spring.security.oauth2.client.registration.authserver-client-credentials.client-secret=<客户端密钥>
spring.security.oauth2.client.registration.authserver-client-credentials.authorization-grant-type=client_credentials
spring.security.oauth2.client.registration.authserver-client-credentials.provider=authserver
# 授权服务器配置
spring.security.oauth2.client.provider.authserver.issuer-uri=<授权服务器的ISSUER URI>
然后创建配置类激活 OAuth2 客户端能力:
java
@Configuration
@EnableWebSecurity
class SecurityConfiguration {
@Bean
SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
return http
// 此示例中,客户端应用自身端点无安全保护
.authorizeHttpRequests(auth -> auth.anyRequest().permitAll())
// 开启 OAuth2 支持
.oauth2Client(Customizer.withDefaults())
.build();
}
}
3.4 HttpClient 客户端
使用 spring-ai-starter-mcp-client 时,配置 McpSyncHttpClientRequestCustomizer Bean:
java
@Configuration
class McpConfiguration {
@Bean
McpSyncClientCustomizer syncClientCustomizer() {
return (name, syncSpec) ->
syncSpec.transportContextProvider(
new AuthenticationMcpTransportContextProvider()
);
}
@Bean
McpSyncHttpClientRequestCustomizer requestCustomizer(
OAuth2AuthorizedClientManager clientManager
) {
// clientRegistration 名称 "authserver" 须与 application.properties 中一致
return new OAuth2AuthorizationCodeSyncHttpRequestCustomizer(
clientManager,
"authserver"
);
}
}
可用的 Customizer 实现:
| 类 | 对应流程 |
|---|---|
OAuth2AuthorizationCodeSyncHttpRequestCustomizer |
授权码流程 |
OAuth2ClientCredentialsSyncHttpRequestCustomizer |
客户端凭证流程 |
OAuth2HybridSyncHttpRequestCustomizer |
混合流程 |
3.5 WebClient 客户端
使用 spring-ai-starter-mcp-client-webflux 时,通过 MCP ExchangeFilterFunction 配置 WebClient.Builder:
java
@Configuration
class McpConfiguration {
@Bean
McpSyncClientCustomizer syncClientCustomizer() {
return (name, syncSpec) ->
syncSpec.transportContextProvider(
new AuthenticationMcpTransportContextProvider()
);
}
@Bean
WebClient.Builder mcpWebClientBuilder(OAuth2AuthorizedClientManager clientManager) {
// clientRegistration 名称 "authserver" 须与 application.properties 中一致
return WebClient.builder().filter(
new McpOAuth2AuthorizationCodeExchangeFilterFunction(
clientManager,
"authserver"
)
);
}
}
可用的 Filter 实现:
| 类 | 对应流程 |
|---|---|
McpOAuth2AuthorizationCodeExchangeFilterFunction |
授权码流程 |
McpOAuth2ClientCredentialsExchangeFilterFunction |
客户端凭证流程 |
McpOAuth2HybridExchangeFilterFunction |
混合流程 |
3.6 绕过 Spring AI 自动配置
Spring AI 的自动配置会在启动时初始化 MCP 客户端,这可能导致基于用户认证的场景出问题。以下提供两种绕过方案:
方案一:禁用 @Tool 自动配置
发布一个空的 ToolCallbackResolver Bean 来禁用 Spring AI 的 @Tool 自动配置:
java
@Configuration
public class McpConfiguration {
@Bean
ToolCallbackResolver resolver() {
return new StaticToolCallbackResolver(List.of());
}
}
方案二:编程式客户端配置
以编程方式配置 MCP 客户端,替代 Spring Boot 属性配置。
HttpClient 客户端:
java
@Bean
McpSyncClient client(
ObjectMapper objectMapper,
McpSyncHttpClientRequestCustomizer requestCustomizer,
McpClientCommonProperties commonProps
) {
var transport = HttpClientStreamableHttpTransport.builder(mcpServerUrl)
.clientBuilder(HttpClient.newBuilder())
.jsonMapper(new JacksonMcpJsonMapper(objectMapper))
.httpRequestCustomizer(requestCustomizer)
.build();
var clientInfo = new McpSchema.Implementation("client-name", commonProps.getVersion());
return McpClient.sync(transport)
.clientInfo(clientInfo)
.requestTimeout(commonProps.getRequestTimeout())
.transportContextProvider(new AuthenticationMcpTransportContextProvider())
.build();
}
WebClient 客户端:
java
@Bean
McpSyncClient client(
WebClient.Builder mcpWebClientBuilder,
ObjectMapper objectMapper,
McpClientCommonProperties commonProperties
) {
var builder = mcpWebClientBuilder.baseUrl(mcpServerUrl);
var transport = WebClientStreamableHttpTransport.builder(builder)
.jsonMapper(new JacksonMcpJsonMapper(objectMapper))
.build();
var clientInfo = new McpSchema.Implementation("clientName", commonProperties.getVersion());
return McpClient.sync(transport)
.clientInfo(clientInfo)
.requestTimeout(commonProperties.getRequestTimeout())
.transportContextProvider(new AuthenticationMcpTransportContextProvider())
.build();
}
然后将客户端添加到 ChatClient 中:
java
var chatResponse = chatClient.prompt("提示 LLM 执行操作")
.toolCallbacks(new SyncMcpToolCallbackProvider(mcpClient1, mcpClient2, mcpClient3))
.call()
.content();
3.7 已知限制
| 限制 | 说明 |
|---|---|
WebFlux 服务端不支持 |
不支持 Spring WebFlux 服务端 |
| 启动时自动初始化 | Spring AI 自动配置在应用启动时初始化 MCP 客户端,基于用户认证的场景需要绕过方案 |
SSE 兼容 |
不同于服务端模块,客户端实现支持 SSE 传输(HttpClient 和 WebClient 均可) |
4. MCP 授权服务器
MCP Authorization Server 模块在 Spring Security OAuth 2.0 Authorization Server 基础上进行了增强,新增了动态客户端注册(Dynamic Client Registration)和资源指示符(Resource Indicators)等 MCP 授权规范相关特性。
4.1 依赖
xml
<dependency>
<groupId>org.springaicommunity</groupId>
<artifactId>mcp-authorization-server</artifactId>
</dependency>
4.2 配置
在 application.yml 中配置授权服务器:
yaml
spring:
application:
name: sample-authorization-server
security:
oauth2:
authorizationserver:
client:
default-client:
token:
access-token-time-to-live: 1h
registration:
client-id: "default-client"
client-secret: "{noop}default-secret"
client-authentication-methods:
- "client_secret_basic"
- "none"
authorization-grant-types:
- "authorization_code"
- "client_credentials"
redirect-uris:
- "http://127.0.0.1:8080/authorize/oauth2/code/authserver"
- "http://localhost:8080/authorize/oauth2/code/authserver"
# mcp-inspector
- "http://localhost:6274/oauth/callback"
# Claude Code
- "https://claude.ai/api/mcp/auth_callback"
user:
# 单用户,用户名为 "user"
name: user
password: password
server:
servlet:
session:
cookie:
# 覆盖默认 cookie 名称(JSESSIONID)
# 允许多个 Spring 应用在 localhost 上运行且各自拥有独立的 cookie
# 否则,由于 cookie 不考虑端口号,会产生冲突
name: MCP_AUTHORIZATION_SERVER_SESSIONID
然后通过安全过滤器链激活授权服务器能力:
java
@Bean
SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
return http
// 所有请求均需认证
.authorizeHttpRequests(auth -> auth.anyRequest().authenticated())
// 启用授权服务器自定义
.with(McpAuthorizationServerConfigurer.mcpAuthorizationServer(), withDefaults())
// 启用表单登录,用户 "user" / "password"
.formLogin(withDefaults())
.build();
}
4.3 已知限制
| 限制 | 说明 |
|---|---|
WebFlux 不支持 |
不支持 Spring WebFlux 服务端 |
| 资源标识符 | 所有客户端支持所有资源标识符(resource identifiers) |
5. 示例与集成
samples 目录包含所有模块的工作示例,包括集成测试。
通过 mcp-server-security 和配套的 mcp-authorization-server,可与以下工具集成:
- Cursor
- Claude Desktop
- MCP Inspector
使用
MCP Inspector时,可能需要禁用CSRF和CORS保护。