本项目演示了如何使用 Spring Security 来保护您的 Agent-to-Agent (A2A) 和 Model Context Protocol (MCP) 服务器。它提供了 A2A Java 的代理框架与 Spring 强大安全机制之间的无缝集成。
A2A Java 是一个用于构建基于代理系统的强大框架,而 MCP(模型上下文协议)则允许与各种AI模型进行交互。本项目展示了如何实现以下功能:

@Log
@Service
@Agent(groupName = "car booking", groupDescription = "与汽车预订相关的操作")
public class CarBookingAgent {
@PreAuthorize("hasRole('USER')")
@Action(description = "根据给定详情预订汽车")
public String bookCar(String carType, String pickupLocation, String dropLocation) {
log.info("正在预订类型为:" + carType +
",取车地点:" + pickupLocation +
",还车地点:" + dropLocation);
return "已预订类型为 " + carType + " 的汽车,从 " + pickupLocation + " 到 " + dropLocation;
}
@PreAuthorize("hasRole('ADMIN')")
@Action(description = "取消汽车预订")
public String cancelCarBooking(String bookingId) {
log.info("正在取消预订ID为:" + bookingId + " 的汽车预订");
return "预订ID为 " + bookingId + " 的汽车预订已被取消";
}
// 此操作是公开的,不需要任何角色
@Action(description = "获取预订状态")
public String getBookingStatus(String bookingId) {
return "预订ID为 " + bookingId + " 的状态已确认";
}
}
让我们测试代理卡是如何根据其角色暴露给客户端的。
使用用户角色进行测试
curl -u user:password http://localhost:7860/.well-known/agent.json
这将只返回这些操作
{"name":"Car Booking Agent","description":"此代理提供汽车预订的能力,允许用户预订汽车、检查预订状态、查看可用汽车类型并获取定价信息。","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":"允许用户从可用选项中预订汽车。","tags":["booking","car rental","transportation"],"examples":["预订明天的轿车","周末预订SUV"],"inputModes":["Text","Voice"],"outputModes":["Text","Voice"]},{"id":"getBookingStatus","name":"Get Booking Status","description":"允许用户检查他们的汽车预订状态。","tags":["status","booking"],"examples":["我的预订状态是什么?","检查今天的预订状态"],"inputModes":["Text","Voice"],"outputModes":["Text","Voice"]},{"id":"listCarTypes","name":"List Car Types","description":"提供可用于预订的汽车类型列表。","tags":["list","car types","available cars"],"examples":["有哪些可用的汽车类型?","显示汽车列表"],"inputModes":["Text","Voice"],"outputModes":["Text","Voice"]},{"id":"getCarPricing","name":"Get Car Pricing","description":"提供不同汽车类型的定价信息。","tags":["pricing","cost","car rental"],"examples":["轿车的价格是多少?","租用SUV需要多少钱?"],"inputModes":["Text","Voice"],"outputModes":["Text","Voice"]}]}
不使用角色进行测试
curl http://localhost:7860/.well-known/agent.json
{"name":"Car Booking Agent","description":"此代理提供与汽车预订相关的能力,包括检查预订状态、列出汽车类型和检索汽车定价。","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":"允许用户预订汽车、检查预订状态并检索定价信息。","tags":["car","booking","transportation"],"examples":["从机场预订一辆轿车","检查我的预订状态","列出可用的SUV","获取紧凑型汽车的定价"],"inputModes":["text","voice"],"outputModes":["text","voice"]}]}
使用管理员角色进行测试
curl -u admin:admin http://localhost:7860/.well-known/agent.json
{"name":"Car Booking Agent","description":"此代理提供与汽车预订相关的能力,包括预订管理和状态跟踪。","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":"管理汽车预订,包括创建、取消和状态检查。","tags":["booking","car","management"],"examples":["预订明天的汽车","取消我的预订","我的预订状态是什么?"],"inputModes":["text","voice"],"outputModes":["text","voice"]},{"id":"getBookingStatus","name":"Get Booking Status","description":"检查汽车预订的当前状态。","tags":["status","booking"],"examples":["我的预订状态是什么?","我的汽车准备好取了吗?"],"inputModes":["text","voice"],"outputModes":["text","voice"]},{"id":"listCarTypes","name":"List Car Types","description":"检索可用于预订的汽车类型列表。","tags":["car types","listing"],"examples":["你们有什么类型的汽车?","列出今天可用的汽车。"],"inputModes":["text","voice"],"outputModes":["text","voice"]},{"id":"getCarPricing","name":"Get Car Pricing","description":"获取不同汽车类型的定价信息。","tags":["pricing","car"],"examples":["轿车的价格是多少?","租用SUV需要多少钱?"],"inputModes":["text","voice"],"outputModes":["text","voice"]},{"id":"cancelCarBooking","name":"Cancel Car Booking","description":"取消现有的汽车预订。","tags":["cancellation","booking"],"examples":["取消我明天的预订。","我想取消我的预订。"],"inputModes":["text","voice"],"outputModes":["text","voice"]},{"id":"blockCarForMaintenance","name":"Block Car for Maintenance","description":"阻止汽车预订以进行维护。","tags":["maintenance","blocking"],"examples":["阻止汽车进行维护。","将这辆汽车暂停服务。"],"inputModes":["text","voice"],"outputModes":["text","voice"]},{"id":"generateBookingReport","name":"Generate Booking Report","description":"生成预订报告以供分析。","tags":["reporting","booking"],"examples":["生成本周所有预订的报告。","显示这个月的预订报告。"],"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": "预订一辆马鲁蒂汽车,从托诺特到万克尔,日期为2025年5月22日",
"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-10d0-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":"预订一辆马鲁蒂汽车,从托诺特到万克尔,日期为2025年5月22日"}],"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":"预订一辆马鲁蒂汽车,从托诺特到万克尔,日期为2025年5月22日"}],"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":"处理失败:访问被拒绝"}],"metadata":null},"timestamp":"2025-05-23T21:27:06.172634900Z"},"history":[{"role":"user","parts":[{"type":"text","type":"text","metadata":null,"text":"预订一辆马鲁蒂汽车,从托诺特到万克尔,日期为2025年5月22日"}],"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":"预订一辆马鲁蒂汽车,从托诺特到万克尔,日期为2025年5月22日"}],"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}
不使用角色进行测试
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":"获取汽车预订状态","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":"查看可用汽车类型","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":"获取汽车类型的定价","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":"根据给定详情预订汽车","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":"获取汽车预订状态","name":"getBookingStatus","type":null},{"parameters":null,"inputSchema":{"type":"object","properties":{"provideAllValuesInPlainEnglish":{"type":"string","description":"{\n \"parameters\": []\n}","additionalProperties":{},"items":false}},"required":["provideAllValuesInPlainEnglish"]