diff --git a/docs/en/API_REFERENCE.md b/docs/en/API_REFERENCE.md index 7486c16..663fef6 100644 --- a/docs/en/API_REFERENCE.md +++ b/docs/en/API_REFERENCE.md @@ -731,11 +731,39 @@ Auto-configures the following beans (all `@ConditionalOnMissingBean`, so you can | `pushStore` | `PushNotificationConfigStore` | Push notification config storage | | `agentExecutorPool` | `ExecutorService` | Thread pool for agent execution (8 threads, daemon) | | `eventBusProcessor` | `MainEventBusProcessor` | Event bus processor | -| `requestHandler` | `RequestHandler` | Default request handler | +| `requestHandler` | `RequestHandler` | Default request handler; operation-level authorization is enabled automatically when a `TaskAuthorizationProvider` bean exists | | `restHandler` | `RestHandler` | REST protocol handler | | `a2aController` | `A2AController` | Spring MVC controller for message and task endpoints | | `a2aSlashActionAliasController` | `A2ASlashActionAliasController` | Optional public controller for slash-style action aliases | +### Server-side request authorization (TaskAuthorizationProvider) + +Declare a `TaskAuthorizationProvider` bean (`@Component` or `@Bean`) to inject business validation +(caller identity, request legality, ...) into every A2A operation **before** the agent executor runs: + +```java +@Component +public class WorkbenchRequestGuard implements TaskAuthorizationProvider { + @Override + public boolean checkCreate(ServerCallContext ctx, TaskOperation op) throws A2AError { + // message:send / message:stream (new task creation) arrive here + return ctx.getUser().isAuthenticated(); // false or an A2AError rejects the request + } + @Override public boolean checkRead(ServerCallContext ctx, String taskId, TaskOperation op) { ... } + @Override public boolean checkWrite(ServerCallContext ctx, String taskId, TaskOperation op) { ... } + @Override public boolean isTaskRecorded(String taskId) { ... } + @Override public void recordOwnership(ServerCallContext ctx, String taskId, TaskOperation op) { ... } +} +``` + +- `checkCreate` guards `message:send` / `message:stream`; `checkRead` guards task query/list; + `checkWrite` guards task cancel; `recordOwnership`/`isTaskRecorded` track task ownership. +- `ServerCallContext` exposes `getUser()` (authenticated user), `getRequestedExtensions()` + (the A2A-Extensions request header), and `getState()` (transport state). +- Rejections return a standard A2A error envelope to the caller (`checkCreate` returning false maps + to a task-not-found error; throwing a more specific `A2AError` subclass carries a custom reason). +- Without such a bean the server behaves exactly as before (no authorization decorator is wired). + ### A2AController Spring MVC controller that exposes A2A REST endpoints: diff --git a/docs/zh/API_REFERENCE.md b/docs/zh/API_REFERENCE.md index be4b1cb..9a6a2e7 100644 --- a/docs/zh/API_REFERENCE.md +++ b/docs/zh/API_REFERENCE.md @@ -684,11 +684,39 @@ a2at: | `pushStore` | `PushNotificationConfigStore` | 推送通知配置存储 | | `agentExecutorPool` | `ExecutorService` | 智能体执行线程池(8 线程,守护线程) | | `eventBusProcessor` | `MainEventBusProcessor` | 事件总线处理器 | -| `requestHandler` | `RequestHandler` | 默认请求处理器 | +| `requestHandler` | `RequestHandler` | 默认请求处理器;存在 `TaskAuthorizationProvider` Bean 时自动启用操作级鉴权 | | `restHandler` | `RestHandler` | REST 协议处理器 | | `a2aController` | `A2AController` | 消息和任务端点的 Spring MVC 控制器 | | `a2aSlashActionAliasController` | `A2ASlashActionAliasController` | 可选的斜杠动作别名公共控制器 | +### 服务端请求鉴权(TaskAuthorizationProvider) + +宿主声明一个 `TaskAuthorizationProvider` Bean(`@Component` 或 `@Bean`),即可在每个 A2A 操作进入 +智能体执行器**之前**注入业务校验(调用方身份、请求合法性等): + +```java +@Component +public class WorkbenchRequestGuard implements TaskAuthorizationProvider { + @Override + public boolean checkCreate(ServerCallContext ctx, TaskOperation op) throws A2AError { + // message:send / message:stream(新任务创建)走这里 + return ctx.getUser().isAuthenticated(); // 返回 false 或抛 A2AError 即拒绝 + } + @Override public boolean checkRead(ServerCallContext ctx, String taskId, TaskOperation op) { ... } + @Override public boolean checkWrite(ServerCallContext ctx, String taskId, TaskOperation op) { ... } + @Override public boolean isTaskRecorded(String taskId) { ... } + @Override public void recordOwnership(ServerCallContext ctx, String taskId, TaskOperation op) { ... } +} +``` + +- `checkCreate` 守卫 `message:send` / `message:stream`;`checkRead` 守卫任务查询/列表; + `checkWrite` 守卫任务取消;`recordOwnership`/`isTaskRecorded` 用于任务归属跟踪。 +- `ServerCallContext` 提供 `getUser()`(认证用户)、`getRequestedExtensions()`(A2A-Extensions 请求头) + 和 `getState()`(传输状态)。 +- 拒绝以标准 A2A 错误信封返回调用方(`checkCreate` 返回 false 时为任务不存在错误; + 抛出更具体的 `A2AError` 子类可携带自定义原因)。 +- 未声明该 Bean 时行为与之前完全一致(不装配鉴权装饰器)。 + ### A2AController 暴露 A2A REST 端点的 Spring MVC 控制器: diff --git a/spring-boot-starter/src/main/java/dev/openan/workflow/engine/spring/A2AAutoConfiguration.java b/spring-boot-starter/src/main/java/dev/openan/workflow/engine/spring/A2AAutoConfiguration.java index c67a6aa..8041024 100644 --- a/spring-boot-starter/src/main/java/dev/openan/workflow/engine/spring/A2AAutoConfiguration.java +++ b/spring-boot-starter/src/main/java/dev/openan/workflow/engine/spring/A2AAutoConfiguration.java @@ -29,10 +29,12 @@ import java.util.concurrent.TimeUnit; import org.a2aproject.sdk.server.AgentCardCacheMetadata; import org.a2aproject.sdk.server.agentexecution.AgentExecutor; +import org.a2aproject.sdk.server.auth.TaskAuthorizationProvider; import org.a2aproject.sdk.server.config.A2AConfigProvider; import org.a2aproject.sdk.server.events.InMemoryQueueManager; import org.a2aproject.sdk.server.events.MainEventBus; import org.a2aproject.sdk.server.events.MainEventBusProcessor; +import org.a2aproject.sdk.server.requesthandlers.AuthorizationRequestHandlerDecorator; import org.a2aproject.sdk.server.requesthandlers.DefaultRequestHandler; import org.a2aproject.sdk.server.requesthandlers.RequestHandler; import org.a2aproject.sdk.server.tasks.BasePushNotificationSender; @@ -44,6 +46,7 @@ import org.a2aproject.sdk.transport.rest.handler.RestHandler; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.ObjectProvider; import org.springframework.boot.autoconfigure.AutoConfiguration; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; @@ -70,6 +73,12 @@ *
Optional host-side request validation: declare a {@link TaskAuthorizationProvider} bean and + * every A2A operation is offered to it before the agent executor runs. {@code checkCreate} guards + * message send/stream (new task creation), {@code checkRead}/{@code checkWrite} guard task + * query/cancel, and rejection returns a standard A2A error envelope to the caller. Without such a + * bean the server behaves exactly as before. + * *
The partner only needs to provide an {@link AgentExecutor} implementation as a
* {@code @Component} or {@code @Bean}. Set {@code a2at.server.enabled=true} to enable the
* server-side autoconfiguration.
@@ -189,7 +198,8 @@ public RequestHandler requestHandler(
InMemoryQueueManager qm,
PushNotificationConfigStore pushStore,
MainEventBusProcessor proc,
- ExecutorService pool) {
+ ExecutorService pool,
+ ObjectProvider