[Перевод] Как обезопасить Spring AI MCP сервер с помощью OAuth2

Команда Spring АйО перевела статью о том, как правильно настраивать безопасность на MCP серверах с использованием возможностей OAuth2 в свете новейшей спецификации MCP, вышедшей в свет 26-го марта 2025-го года, то есть совсем недавно.

Spring AI предлагает поддержку для Model Context Protocol или, сокращенно, MCP, что позволяет ИИ моделям взаимодействовать и получать доступ к внешним инструментам и ресурсам структурированно. С помощью Spring AI разработчики могут создавать свои собственные MCP сервера и давать ИИ моделям доступ к их возможностям всего в несколько строчек кода.

Авторизация и безопасность в MCP

MCP сервера могут работать локально, используя передачу по STDIO. Чтобы сделать MCP сервер доступным для внешнего мира, он должен предоставлять несколько стандартных HTTP эндпоинтов. В то время как MCP сервера, используемые локально и с целями тестирования, могут не требовать строгой аутентификации, продакшен деплойменты нуждаются в строжайшей безопасности и эффективном управлении permission-ами для доступных внешнему миру эндпоинтов. Эта проблема была решена в последней версии спецификации MCP (от 26-го марта 2025), которую выпустили на прошлой неделе. Она закладывает фундамент для безопасных коммуникаций между клиентами и серверами с использованием популярного протокола OAuth2.

Мы не собираемся делать полный обзор OAuth2 в рамках этой статьи, но немного освежить наши знания о нем будет полезно. В черновике спецификации MCP сервер описывается как одновременно и сервер ресурсов (он же OAuth2 Resource Server), и сервер авторизации (он же OAuth2 Authorization Server).

Будучи сервером ресурсов он выполняет проверки для авторизации входящих запросов, проверяя заголовок Authorization. Заголовок должен содержать OAuth2 access_token, который представляет из себя строку, описывающую «разрешения» (permissions-ы) клиента. Этот токен может быть JSON Web Token (JWT) или opaque token, который сам по себе не несет никакой информации. Если токен отсутствует или невалиден, (неправильно сформирован, истек, неверный получатель и т.д.), MCP сервер, будучи сервером ресурсов, отклоняет запрос. При использовании таких токенов типичный запрос может выглядеть вот так:

curl https://mcp.example.com/sse
    -H "Authorization: Bearer "

В качестве сервера авторизации, MCP сервер должен также обладать способностью выпускать access_token`ы для клиентов безопасным образом. Прежде чем выпустить токен, сервер должен проверить креды клиента и, в некоторых случаях, identity клиента, который пытается получить доступ к серверу. Сервер авторизации принимает решения по характеристикам токена, таким как дата его истечения, scope-ы и т.д. 

Используя Spring Security и Spring Authorization Server, мы можем легко добавить обе эти возможности к существующему Spring MCP серверу.

6e37f0c04cab7baa36f54cdf22ab052e.png

Добавление OAuth2 к вашему Spring MCP серверу

В приведенном примере мы добавим поддержку OAuth 2 к тестовому MCP серверу, который предоставляет дополнительные  «погодные» данные (температура, влажность и т.д.) для LLM.  Исходные коды MCP сервера можно найти в репозитории с примерами для Spring AI. Мы не будем заострять внимание на действиях клиентской стороны, только убедимся в том, что наш сервер может выпускать токены и валидировать их.

Прежде всего, импортируем необходимые нам Boot стартеры в pom.xml:


    org.springframework.boot
    spring-boot-starter-oauth2-resource-server


    org.springframework.boot
    spring-boot-starter-oauth2-authorization-server

Далее, сконфигурируем OAuth2 клиент в нашем файле application.properties, чтобы мы могли запрашивать токены доступа:

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:

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 (Cross-Site Request Forgery). MCP сервер не предназначен для взаимодействия с браузерами, поэтому CSRF-защита здесь не нужна.

  • Включает поддержку CORS (Cross-Origin Resource Sharing), чтобы мы могли продемонстрировать сервер при помощи 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":"","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

Можно также использовать токен доступа напрямую в MCP инспекторе, начиная с версии 0.6.0. Просто запустите инспектор и вставьте токен доступа в поле «Authentication > Bearer» в меню слева. Затем нажмите «Connect»: теперь у вас должна появиться возможность отправлять MCP вызовы.

eafa5fcaca3bb8a7c952160235064213.png

Если вы хотите запустить эти программы самостоятельно, вы можете взять код примера из репозитория spring-ai-examples.

Что дальше?

В нашем примере мы реализовали базовые возможности OAuth2 в MCP сервере.

Очевидный следующий шаг — обновить MCP клиент и позволить ему аутентифицироваться на сервере с использованием OAuth2 authorization_code grant-а вместо client_credentials. При таком сценарии пользователи смогут залогиниться со своими собственными кредами и получить свои пользовательские токены, что позволяет более точно назначать пермишены, например с помощью Roles-Based Access Control (RBAC).

В будущем, мы также посмотрим на внешний сервер авторизации OAuth2 и выпуск токенов с его помощью, используя только возможности сервера ресурсов (OAuth2 Resource Server-а) внутри нашего MCP сервера.

25848492d4cd2dcd1df595e475569a9a.png

Присоединяйтесь к русскоязычному сообществу разработчиков на Spring Boot в телеграм — Spring АйО, чтобы быть в курсе последних новостей из мира разработки на Spring Boot и всего, что с ним связано.

© Habrahabr.ru