0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

MCPの認証をSpringAI MCPの基本機能で実現する!!

0
Last updated at Posted at 2026-05-31

SpringAIのMCP(Model Context Protocol)で開発が進められている「認証・セキュリティ」の仕組みを整理しました。

通常、MCPは標準入出力(Stdio)で接続することが多いですが、HTTP(SSEなど)経由でMCPサーバーを公開する場合、「いかにして安全に接続するか(認証・認可)」が課題になります。今回は、Spring AIが用意しているセキュリティ機能の中から「APIキー認証」の動作検証を行ったので、実装コードと合わせて解説します。

Spring AIのMCPセキュリティとは

2026年5月現在、Spring AIのMCPコンポーネントではセキュリティ関連の機能開発が進められています。
主な認証メカニズムとして、以下の2つが準備されています。

  • APIキーでの認証 (API Key Authentication)
  • OAuth2での認証 (OAuth2 Authentication)

公式の詳しいリファレンスは以下を参照してください。

実装サンプル

実装のサンプルコードは以下のリポジトリに公開しています。

こちらのコードは、SpringAIのページを参考にAPIキーでの認証を行うMCPサーバを参考に作成したものです。

ソースコード解説

実装については「src/main/java/com/example」のディレクトリの中の3ファイルとなります。

DemoApplication.java

SpringBootを起動のためのファイルになります。

package com.example.demo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class DemoApplication {

	public static void main(String[] args) {
		SpringApplication.run(DemoApplication.class, args);
	}

}

McpServerConfiguration.java

認証を行うフィルターを行うソースコードになります。

package com.example.demo;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.web.SecurityFilterChain;
import org.springaicommunity.mcp.security.server.apikey.memory.ApiKeyEntityImpl;
import org.springaicommunity.mcp.security.server.apikey.ApiKeyEntityRepository;
import org.springaicommunity.mcp.security.server.apikey.memory.InMemoryApiKeyEntityRepository;

import java.util.List;

import static org.springaicommunity.mcp.security.server.config.McpApiKeyConfigurer.mcpServerApiKey;

@Configuration
@EnableWebSecurity
@EnableMethodSecurity
public class McpServerConfiguration {

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        return http
                .authorizeHttpRequests(authz -> authz
                        .anyRequest().authenticated()
                )
                .csrf(csrf -> csrf.disable()) // disable CSRF for API endpoints
                .with(
                        mcpServerApiKey(),
                        (apiKey) -> {
                            // REQUIRED: the repo for API keys
                            apiKey.apiKeyRepository(apiKeyRepository());
                        }
                )
                .build();
    }

    /**
     * Provide a repository of {@link org.springaicommunity.mcp.server.security.apikey.ApiKeyEntity}.
     */
    @Bean
    public ApiKeyEntityRepository<ApiKeyEntityImpl> apiKeyRepository() {
        var apiKey = ApiKeyEntityImpl.builder()
                .name("test api key")
                .id("api01")
                .secret("mycustomapikey")
                .build();
        return new InMemoryApiKeyEntityRepository<>(List.of(apiKey));
    }

    @com.fasterxml.jackson.annotation.JsonIgnoreProperties(ignoreUnknown = true)
    interface McpRecordMixIn {}

    @Bean
    @org.springframework.context.annotation.Primary
    public com.fasterxml.jackson.databind.ObjectMapper mcpObjectMapper() {
        var objectMapper = new com.fasterxml.jackson.databind.ObjectMapper();
        objectMapper.configure(com.fasterxml.jackson.databind.DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
        objectMapper.addMixIn(io.modelcontextprotocol.spec.McpSchema.InitializeRequest.class, McpRecordMixIn.class);
        objectMapper.addMixIn(io.modelcontextprotocol.spec.McpSchema.ClientCapabilities.class, McpRecordMixIn.class);
        objectMapper.addMixIn(io.modelcontextprotocol.spec.McpSchema.ClientCapabilities.Elicitation.class, McpRecordMixIn.class);
        objectMapper.addMixIn(io.modelcontextprotocol.spec.McpSchema.ClientCapabilities.RootCapabilities.class, McpRecordMixIn.class);
        objectMapper.addMixIn(io.modelcontextprotocol.spec.McpSchema.ClientCapabilities.Sampling.class, McpRecordMixIn.class);
        return objectMapper;
    }

    @Bean
    public org.springframework.ai.tool.ToolCallbackProvider myToolsCallbackProvider(MyToolsService myToolsService) {
        return org.springframework.ai.tool.method.MethodToolCallbackProvider.builder()
                .toolObjects(myToolsService)
                .build();
    }

}

各メソッドの役割

securityFilterChain メソッド

全ての要求に対して認証を必須(.anyRequest().authenticated())とし、Spring AI提供の mcpServerApiKey() カスタマイザーを組み込んで認証処理を行うフィルターチェーンを構築しています。

apiKeyRepository メソッド

APIキーの具体的な値をメモリ上に定義しています。今回は検証用のサンプルなのでハードコードしていますが、実運用ではデータベースやSecrets Managerなどの安全な外部領域から取得する実装に変更可能です。

mcpObjectMapper メソッド

MCPの仕様上、リクエスト内に未知のパラメータが含まれていた場合にデシリアライズエラーを回避するための設定(MixInの適用など)を行っています。

myToolsCallbackProvider メソッド

後述する、MCPツールの実処理を持つコンポーネント(MyToolsService)をSpring AIに登録するための定義です。

MyToolsService.java

package com.example.demo;

import org.springframework.stereotype.Service;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.util.StringUtils;
import org.springframework.ai.tool.annotation.Tool;
import org.springframework.ai.tool.annotation.ToolParam;

@Service
public class MyToolsService {

    @Tool(name = "greeter", description = "A tool that greets the user by name, in the selected language")
    @PreAuthorize("isAuthenticated()")
    public String greet(
            @ToolParam(description = "The language for the greeting (example: english, french, ...)") String language
    ) {
        if (!StringUtils.hasText(language)) {
            language = "english";
        }

        var authentication = SecurityContextHolder.getContext().getAuthentication();
        var name = (authentication != null && authentication.getName() != null) ? authentication.getName() : "Guest";

        return switch (language.toLowerCase()) {
            case "english" -> "Hello, %s!".formatted(name);
            case "french" -> "Salut %s!".formatted(name);
            default -> ("I don't understand language \"%s\". " +
                    "So I'm just going to say Hello %s!").formatted(language, name);
        };
    }
}

今回のSpringAIのMCPのセキュリティはSpringSecurityを基準に作成されています。
PreAuthorize("isAuthenticated()")はそのための定義で、該当のメソッドの実行の前に認証がされているかどうかを判定されているかどうかを確認することを動かすための定義となります。

さらに実装されているコードにありますが、nameを取得することもできるので誰がアクセスしてきたかを明確に記録を残すことができます。

実行結果

MCPの設定ファイルに以下の設定を行って実行してみます。
今回はローカル環境にHTTP接続できる環境を作りました。

"spring-mcp-server": {
      "command": "npx",
      "args": [
        "-y",
        "mcp-remote",
        "http://localhost:8080/sse",
        "--header",
        "X-API-key: api01.mycustomapikey",
        "--transport",
        "sse-only"
      ],
      "disabled": false,
      "disabledTools": []
    }
MCPのspring-mcp-serverを実行してください
MCPサーバー spring-mcp-server の greeter ツールを実行した結果、以下のレスポンスが返却されました。

実行結果
呼び出し引数: {"language": "english"}
返却値: "Hello, api01!"
0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?