A2A和MCP与Spring的安全集成
本项目演示了如何使用Spring Security保护您的代理到代理(A2A)和模型上下文协议(MCP)服务器。它提供了A2A Java的代理框架和Spring的强大安全机制之间的无缝集成。
概述
A2A Java 是构建基于代理的系统的强大框架,而MCP(模型上下文协议)支持与各种AI模型的交互。该项目展示了如何:
- 使用基于角色的访问控制保护代理操作
- 将Spring Security与A2A Java注释集成
- 处理人类用户和AI代理的身份验证
特性
- 基于角色的访问控制(USER和ADMIN角色)
- 基于注释的安全性(@PreAuthorize)
- 与现有A2A Java代理无缝集成
- 支持多种客户端实现(Python、Java)
- 服务器启动于7860,您可以在application.properties文件中将其更改为8080
@日志 @售后服务 @代理(groupName=“汽车预订”,groupDescription=“与汽车预订相关的操作”) 公共类汽车预订代理{
@PreAuthorize("hasRole('USER')")
@Action(description = "Book a car for the given details")
public String bookCar(String carType, String pickupLocation, String dropLocation) {
log.info("Booking car of type: " + carType+
", pickup location: " + pickupLocation +
", drop location: " + dropLocation);
return "Car of type " + carType + " has been booked from " + pickupLocation + " to " + dropLocation;
}
@PreAuthorize("hasRole('ADMIN')")
@Action(description = "Cancel a car booking")
public String cancelCarBooking(String bookingId) {
log.info("Cancelling car booking with ID: " + bookingId);
return "Car booking with ID " + bookingId + " has been cancelled";
}
//this action is public and does not require any role
@Action(description = "get status of booking")
public String getBookingStatus(String bookingId) {
return "The status of booking ID " + bookingId + " is confirmed";
}}
测试A2A客户端
让我们测试一下代理卡是如何根据其角色暴露给客户端的。
使用用户角色进行测试
curl -u user:password http://localhost:7860/.well-known/agent.json 只会给你这些动作
{"name":"Car Booking Agent","description":"This agent provides capabilities for car booking, allowing users to book cars, check booking status, view available car types, and get pricing information.","url":"http://vishal:7860","provider":{"organization":"Car Rentals Inc.","url":"https://example.com"},"version":"1.0.0","documentationUrl":"https://example.com/docs/car-booking-agent","capabilities":{"streaming":true,"pushNotifications":true,"stateTransitionHistory":true},"authentication":{"schemes":["ApiKey"],"credentials":"Basic","valid":true,"bearerAuth":false,"basicAuth":false},"defaultInputModes":["Text","Voice"],"defaultOutputModes":["Text","Voice"],"skills":[{"id":"bookCar","name":"Book Car","description":"Allows users to book a car from the available options.","tags":["booking","car rental","transportation"],"examples":["Book a sedan for tomorrow","Reserve an SUV for the weekend"],"inputModes":["Text","Voice"],"outputModes":["Text","Voice"]},{"id":"getBookingStatus","name":"Get Booking Status","description":"Allows users to check the status of their car bookings.","tags":["status","booking"],"examples":["What is the status of my booking?","Check booking status for today"],"inputModes":["Text","Voice"],"outputModes":["Text","Voice"]},{"id":"listCarTypes","name":"List Car Types","description":"Provides a list of available car types for booking.","tags":["list","car types","available cars"],"examples":["What car types are available?","Show me the list of cars"],"inputModes":["Text","Voice"],"outputModes":["Text","Voice"]},{"id":"getCarPricing","name":"Get Car Pricing","description":"Provides pricing information for different car types.","tags":["pricing","cost","car rental"],"examples":["What is the price for a sedan?","How much does it cost to rent an SUV?"],"inputModes":["Text","Voice"],"outputModes":["Text","Voice"]}]}
无角色测试
curl http://localhost:7860/.well-known/agent.json {"name":"Car Booking Agent","description":"This agent provides capabilities related to booking cars, checking booking status, listing car types, and retrieving car pricing.","url":"http://vishal:7860","provider":{"organization":"Car Rentals Inc.","url":"https://example.com"},"version":"1.0.0","documentationUrl":"https://example.com/docs/car-booking-agent","capabilities":{"streaming":true,"pushNotifications":true,"stateTransitionHistory":false},"authentication":{"schemes":["ApiKey"],"credentials":"Basic","valid":true,"bearerAuth":false,"basicAuth":false},"defaultInputModes":["text","voice"],"defaultOutputModes":["text","voice"],"skills":[{"id":"car_booking","name":"Car Booking","description":"Allows users to book cars, check booking status, and retrieve pricing information.","tags":["car","booking","transportation"],"examples":["Book a sedan from the airport","Check the status of my booking","List available SUVs","Get pricing for a compact car"],"inputModes":["text","voice"],"outputModes":["text","voice"]}]}使用管理员角色进行测试
curl -u admin:admin http://localhost:7860/.well-known/agent.json {"name":"Car Booking Agent","description":"This agent provides capabilities related to car booking, including booking management and status tracking.","url":"http://vishal:7860","provider":{"organization":"Car Rentals Inc.","url":"https://example.com"},"version":"1.0.0","documentationUrl":"https://example.com/documentation","capabilities":{"streaming":true,"pushNotifications":true,"stateTransitionHistory":true},"authentication":{"schemes":["ApiKey"],"credentials":"Basic","valid":true,"bearerAuth":false,"basicAuth":false},"defaultInputModes":["text","voice"],"defaultOutputModes":["text","voice"],"skills":[{"id":"carBooking","name":"Car Booking Management","description":"Manage car bookings including creation, cancellation, and status checks.","tags":["booking","car","management"],"examples":["Book a car for tomorrow","Cancel my booking","What is the status of my booking?"],"inputModes":["text","voice"],"outputModes":["text","voice"]},{"id":"getBookingStatus","name":"Get Booking Status","description":"Check the current status of a car booking.","tags":["status","booking"],"examples":["What is the status of my booking?","Is my car ready for pickup?"],"inputModes":["text","voice"],"outputModes":["text","voice"]},{"id":"listCarTypes","name":"List Car Types","description":"Retrieve a list of available car types for booking.","tags":["car types","listing"],"examples":["What car types do you have?","List the available cars for today."],"inputModes":["text","voice"],"outputModes":["text","voice"]},{"id":"getCarPricing","name":"Get Car Pricing","description":"Fetch the pricing information for different car types.","tags":["pricing","car"],"examples":["What is the price for a sedan?","How much does it cost to rent an SUV?"],"inputModes":["text","voice"],"outputModes":["text","voice"]},{"id":"cancelCarBooking","name":"Cancel Car Booking","description":"Cancel an existing car booking.","tags":["cancellation","booking"],"examples":["Cancel my booking for tomorrow.","I want to cancel my reservation."],"inputModes":["text","voice"],"outputModes":["text","voice"]},{"id":"blockCarForMaintenance","name":"Block Car for Maintenance","description":"Block a car for maintenance to prevent bookings.","tags":["maintenance","blocking"],"examples":["Block a car for maintenance.","Put this car on hold for servicing."],"inputModes":["text","voice"],"outputModes":["text","voice"]},{"id":"generateBookingReport","name":"Generate Booking Report","description":"Generate a report of bookings for analysis.","tags":["reporting","booking"],"examples":["Generate a report of all bookings for the week.","Show me the booking report for this month."],"inputModes":["text","voice"],"outputModes":["text","voice"]}]}来自A2a客户端的呼叫
curl -v -u user:password \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"method": "tasks/send",
"params": {
"id": "0e2fc442-180f-4cd2-b316-08b384bb236f",
"sessionId": "fbe485db-c295-40d0-bb9c-bb16367df091",
"message": {
"role": "user",
"parts": [
{
"type": "text",
"text": "book a maruti car from tornot to vanquer on 22 may 2025",
"metadata": null
}
],
"metadata": {
"conversation_id": "fbe485db-c295-40d0-bb9c-bb16367df091",
"conversation_name": "",
"message_id": "e3749782-bc89-4691-b6cc-77239fef1ad0",
"last_message_id": "122350f4-6ce7-436f-8ca4-0557b75b15ea"
}
},
"acceptedOutputModes": ["text", "text/plain", "image/png"],
"pushNotification": null,
"historyLength": null,
"metadata": {
"conversation_id": "fbe485db-c295-40d0-bb9c-bb16367df091"
}
},
"id": "d0ed3aa440f64d63a3a73d2bc7eb6358"
}' \
http://localhost:7860/结果
{"jsonrpc":"2.0","id":"0e2fc442-180f-4cd2-b316-08b384bb236f","result":{"id":"0e2fc442-180f-4cd2-b316-08b384bb236f","sessionId":"fbe485db-c295-40d0-bb9c-bb16367df091","status":{"state":"submitted","message":null,"timestamp":"2025-05-23T21:07:55.790141400Z"},"history":[{"role":"user","parts":[{"type":"text","type":"text","metadata":null,"text":"book a maruti car from tornot to vanquer on 22 may 2025"}],"metadata":{"conversation_id":"fbe485db-c295-40d0-bb9c-bb16367df091","conversation_name":"","message_id":"e3749782-bc89-4691-b6cc-77239fef1ad0","last_message_id":"122350f4-6ce7-436f-8ca4-0557b75b15ea"}},{"role":"user","parts":[{"type":"text","type":"text","metadata":null,"text":"book a maruti car from tornot to vanquer on 22 may 2025"}],"metadata":{"conversation_id":"fbe485db-c295-40d0-bb9c-bb16367df091","conversation_name":"","message_id":"e3749782-bc89-4691-b6cc-77239fef1ad0","last_message_id":"122350f4-6ce7-436f-8ca4-0557b75b15ea"}}],"artifacts":null,"metadata":null,"pushNotificationConfig":null,"pushNotificationUrl":null,"subscribed":false,"subscriptionDateNow":null,"cancelled":false},"error":null}但是,当您使用不同的用户名和密码拨打电话时,您将被拒绝访问
{"jsonrpc":"2.0","id":"0e2fc442-180f-4cd2-b316-08b384bb236f","result":{"id":"0e2fc442-180f-4cd2-b316-08b384bb236f","sessionId":"fbe485db-c295-40d0-bb9c-bb16367df091","status":{"state":"failed","message":{"role":"agent","parts":[{"type":"text","type":"text","metadata":{},"text":"Processing failed: Access Denied"}],"metadata":null},"timestamp":"2025-05-23T21:27:06.172634900Z"},"history":[{"role":"user","parts":[{"type":"text","type":"text","metadata":null,"text":"book a maruti car from tornot to vanquer on 22 may 2025"}],"metadata":{"conversation_id":"fbe485db-c295-40d0-bb9c-bb16367df091","conversation_name":"","message_id":"e3749782-bc89-4691-b6cc-77239fef1ad0","last_message_id":"122350f4-6ce7-436f-8ca4-0557b75b15ea"}},{"role":"user","parts":[{"type":"text","type":"text","metadata":null,"text":"book a maruti car from tornot to vanquer on 22 may 2025"}],"metadata":{"conversation_id":"fbe485db-c295-40d0-bb9c-bb16367df091","conversation_name":"","message_id":"e3749782-bc89-4691-b6cc-77239fef1ad0","last_message_id":"122350f4-6ce7-436f-8ca4-0557b75b15ea"}}],"artifacts":null,"metadata":null,"pushNotificationConfig":null,"pushNotificationUrl":null,"subscribed":false,"subscriptionDateNow":null,"cancelled":false},"error":null}
测试MCP客户端
无角色测试
curl -H "Content-Type: application/json" -d '{"jsonrpc":"2.0","method":"tools/list","params":{},"id":9}' http://localhost:7860/
{"result":{"_meta":{},"tools":[{"parameters":null,"inputSchema":{"type":"object","properties":{"provideAllValuesInPlainEnglish":{"type":"string","description":"{\n \"arg0\": \"\"\n}","additionalProperties":{},"items":false}},"required":["provideAllValuesInPlainEnglish"]},"annotations":null,"description":"Get booking status of a car","name":"getBookingStatus","type":null},{"parameters":null,"inputSchema":{"type":"object","properties":{"provideAllValuesInPlainEnglish":{"type":"string","description":"{\n \"methodName\": \"listCarTypes\",\n \"parameters\": []\n}","additionalProperties":{},"items":false}},"required":["provideAllValuesInPlainEnglish"]},"annotations":null,"description":"View available car types","name":"listCarTypes","type":null},{"parameters":null,"inputSchema":{"type":"object","properties":{"provideAllValuesInPlainEnglish":{"type":"string","description":"{\n \"arg0\": \"\"\n}","additionalProperties":{},"items":false}},"required":["provideAllValuesInPlainEnglish"]},"annotations":null,"description":"Get pricing for car type","name":"getCarPricing","type":null}]},"id":9,"jsonrpc":"2.0"}使用用户角色进行测试
curl -u user:password -H "Content-Type: application/json" -d '{"jsonrpc":"2.0","method":"tools/list","params":{},"id":9}' http://localhost:7860/
你得到
{"result":{"_meta":{},"tools":[{"parameters":null,"inputSchema":{"type":"object","properties":{"provideAllValuesInPlainEnglish":{"type":"string","description":"{\n \"parameters\": {\n \"arg0\": {\n \"type\": \"String\",\n \"fieldValue\": \"\"\n },\n \"arg1\": {\n \"type\": \"String\",\n \"fieldValue\": \"\"\n },\n \"arg2\": {\n \"type\": \"String\",\n \"fieldValue\": \"\"\n }\n }\n}","additionalProperties":{},"items":false}},"required":["provideAllValuesInPlainEnglish"]},"annotations":null,"description":"Book a car for the given details","name":"bookCar","type":null},{"parameters":null,"inputSchema":{"type":"object","properties":{"provideAllValuesInPlainEnglish":{"type":"string","description":"{\n \"arg0\": \"\"\n}","additionalProperties":{},"items":false}},"required":["provideAllValuesInPlainEnglish"]},"annotations":null,"description":"Get booking status of a car","name":"getBookingStatus","type":null},{"parameters":null,"inputSchema":{"type":"object","properties":{"provideAllValuesInPlainEnglish":{"type":"string","description":"{\n \"parameters\": []\n}","additionalProperties":{},"items":false}},"required":["provideAllValuesInPlainEnglish"]},"annotations":null,"description":"View available car types","name":"listCarTypes","type":null},{"parameters":null,"inputSchema":{"type":"object","properties":{"provideAllValuesInPlainEnglish":{"type":"string","description":"{\n \"arg0\": \"\"\n}","additionalProperties":{},"items":false}},"required":["provideAllValuesInPlainEnglish"]},"annotations":null,"description":"Get pricing for car type","name":"getCarPricing","type":null}]},"id":9,"jsonrpc":"2.0"}使用管理员角色进行测试
curl -u admin:admin -H "Content-Type: application/json" -d '{"jsonrpc":"2.0","method":"tools/list","params":{},"id":9}' http://localhost:7860/
你会得到
{"result":{"_meta":{},"tools":[{"parameters":null,"inputSchema":{"type":"object","properties":{"provideAllValuesInPlainEnglish":{"type":"string","description":"{\n \"arg0\": \"\"\n}","additionalProperties":{},"items":false}},"required":["provideAllValuesInPlainEnglish"]},"annotations":null,"description":"Cancel a car booking","name":"cancelCarBooking","type":null},{"parameters":null,"inputSchema":{"type":"object","properties":{"provideAllValuesInPlainEnglish":{"type":"string","description":"{\n \"arg0\": \"\"\n}","additionalProperties":{},"items":false}},"required":["provideAllValuesInPlainEnglish"]},"annotations":null,"description":"Get booking status of a car","name":"getBookingStatus","type":null},{"parameters":null,"inputSchema":{"type":"object","properties":{"provideAllValuesInPlainEnglish":{"type":"string","description":"{\n \"parameters\": []\n}","additionalProperties":{},"items":false}},"required":["provideAllValuesInPlainEnglish"]},"annotations":null,"description":"View available car types","name":"listCarTypes","type":null},{"parameters":null,"inputSchema":{"type":"object","properties":{"provideAllValuesInPlainEnglish":{"type":"string","description":"{\n \"parameters\": [\n {\n \"name\": \"arg0\",\n \"type\": \"String\",\n \"fieldValue\": \"\"\n }\n ]\n}","additionalProperties":{},"items":false}},"required":["provideAllValuesInPlainEnglish"]},"annotations":null,"description":"Get pricing for car type","name":"getCarPricing","type":null},{"parameters":null,"inputSchema":{"type":"object","properties":{"provideAllValuesInPlainEnglish":{"type":"string","description":"{\n \"parameters\": {\n \"arg0\": {\n \"type\": \"String\",\n \"fieldValue\": \"\"\n },\n \"arg1\": {\n \"type\": \"String\",\n \"fieldValue\": \"\"\n }\n }\n}","additionalProperties":{},"items":false}},"required":["provideAllValuesInPlainEnglish"]},"annotations":null,"description":"Block a car for maintenance","name":"blockCarForMaintenance","type":null},{"parameters":null,"inputSchema":{"type":"object","properties":{"provideAllValuesInPlainEnglish":{"type":"string","description":"{\n \"arg0\": {\n \"type\": \"String\",\n \"fieldValue\": \"\"\n }\n}","additionalProperties":{},"items":false}},"required":["provideAllValuesInPlainEnglish"]},"annotations":null,"description":"Generate booking report","name":"generateBookingReport","type":null}]},"id":9,"jsonrpc":"2.0"} 要调用工具,请发送以下内容
curl -u admin:admin \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"method": "tools/call",
"params": {
"name": "cancelCarBooking",
"arguments": {
"provideAllValuesInPlainEnglish": {
"arg0": "2233"
}
}
},
"id": 25
}' \
http://localhost:7860/你的结果将是
{"result":{"content":[{"annotations":null,"text":"Car booking with ID 2233 has been cancelled","type":"text"}]},"id":25,"jsonrpc":"2.0"}
但如果另一个用户试图调用它
curl -u user:password \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"method": "tools/call",
"params": {
"name": "cancelCarBooking",
"arguments": {
"provideAllValuesInPlainEnglish": {
"arg0": "2233"
}
}
},
"id": 25
}' \
http://localhost:7860/结果将是
{"result":{"content":[{"annotations":null,"text":"access denied, you are not authorized to use this tool","type":"text"}]},"id":25,"jsonrpc":"2.0"}
示例实现
这 CarBookingAgent 演示如何在A2A Java代理中实现基于角色的安全性:
- 用户角色操作:用户可以使用
bookCar方法 - ADMIN角色操作:管理员可以使用取消预订
cancelCarBooking方法 - 公共行动:The
getBookingStatus所有用户都可以访问该方法
安全实施
安全实现是通过Spring security处理的,对客户端实现完全透明。虽然为了简单起见,此演示使用了基本身份验证,但在生产环境中,您应该:
- 对凭据使用适当的加密
- 实现基于安全令牌的身份验证
- 将凭据存储在安全位置
客户端集成
Python客户端(谷歌a2a客户端)
async def _send_request(self, request: JSONRPCRequest) -> dict[str, Any]:
async with httpx.AsyncClient(auth=("user", "password")) as client:
response = await client.post(
self.url,
json=request.model_dump(),
timeout=self.timeout
)Java MCP连接器
该项目使用一个简单的Java直通服务器 mcp连接器 克劳德整合。
配置示例:
{
"yardshare": {
"command": "java",
"args": [
"-jar",
"/work/a2a-mcp-bridge/target/mcp-connector-full.jar",
"http://localhost:7860",
"user",
"password"
],
"timeout": 30000
}
}演示截图
A2A Security Demo *成功通过USER角色的身份验证*
Claude Integration *与Claude安全集成MCP*
Access Denied *未经授权的请求被拒绝访问*
安全上下文
安全上下文在同一会话中跨请求进行维护。初始成功的身份验证根据用户的角色授予访问权限,而随后未经授权的访问尝试则被Spring Security阻止。
