任何知道代理 URL 的人都可以向其发送请求。请在请求到达 Durable Object 之前,在 Worker 中检查调用者身份。对象只会看到你已允许的请求。
routeAgentRequest() 在转发 WebSocket 升级请求前运行 onBeforeConnect,在转发 HTTP 请求前运行 onBeforeRequest。两个钩子都在 Worker 中运行,并且都可以:
- 返回
Response以拒绝请求。 - 返回新的
Request来替换原请求,例如添加对象信任的请求头。 - 不返回值,将请求原样转发。
route 参数包含匹配的绑定名称 className,以及对象的 name:
import { routeAgentRequest } from "agents/routing";
async function authenticate(request, env) {
// Verify a session cookie, a JWT, or an API key here.
const token = request.headers.get("Authorization");
return token ? { id: "user-123" } : null;
}
export default {
async fetch(request, env) {
const check = async (req, route) => {
const user = await authenticate(req, env);
if (!user) {
return new Response("Unauthorized", { status: 401 });
}
// Each user can only reach the agent named after them.
if (route.name !== user.id) {
return new Response("Forbidden", { status: 403 });
}
const forwarded = new Request(req);
forwarded.headers.set("x-user-id", user.id);
return forwarded;
};
return (
(await routeAgentRequest(request, env, {
onBeforeConnect: check,
onBeforeRequest: check,
})) ?? new Response("Not found", { status: 404 })
);
},
};import { routeAgentRequest } from "agents/routing";
type User = { id: string };
async function authenticate(request: Request, env: Env): Promise<User | null> {
// Verify a session cookie, a JWT, or an API key here.
const token = request.headers.get("Authorization");
return token ? { id: "user-123" } : null;
}
export default {
async fetch(request: Request, env: Env): Promise<Response> {
const check = async (req: Request, route: { name: string }) => {
const user = await authenticate(req, env);
if (!user) {
return new Response("Unauthorized", { status: 401 });
}
// Each user can only reach the agent named after them.
if (route.name !== user.id) {
return new Response("Forbidden", { status: 403 });
}
const forwarded = new Request(req);
forwarded.headers.set("x-user-id", user.id);
return forwarded;
};
return (
(await routeAgentRequest(request, env, {
onBeforeConnect: check,
onBeforeRequest: check,
})) ?? new Response("Not found", { status: 404 })
);
},
} satisfies ExportedHandler<Env>;浏览器无法在 WebSocket 升级请求中设置 Authorization 请求头。对于 WebSocket 客户端,请在 onBeforeConnect 中从 Cookie 或查询参数读取令牌。useAgent 和 AgentClient 都接受 query 选项。
只有客户端无法通过其他方式访问对象时,你在钩子中添加的请求头才可信。所有发往对象的请求都必须经过相同的检查。
也可以在调用 routeAgentRequest() 之前检查请求。此方法适用于任何路由,包括 routeAgentRequest() 不处理的路由:
import { routeAgentRequest } from "agents/routing";
async function authenticate(request) {
const token = request.headers.get("Authorization");
return token ? { id: "user-123" } : null;
}
export default {
async fetch(request, env) {
const url = new URL(request.url);
if (url.pathname.startsWith("/agents/")) {
const user = await authenticate(request);
if (!user) {
return new Response("Unauthorized", { status: 401 });
}
// /agents/:binding/:name — the name is the fourth segment.
const name = url.pathname.split("/")[3];
if (name !== user.id) {
return new Response("Forbidden", { status: 403 });
}
}
return (
(await routeAgentRequest(request, env)) ??
new Response("Not found", { status: 404 })
);
},
};import { routeAgentRequest } from "agents/routing";
async function authenticate(request: Request): Promise<{ id: string } | null> {
const token = request.headers.get("Authorization");
return token ? { id: "user-123" } : null;
}
export default {
async fetch(request: Request, env: Env): Promise<Response> {
const url = new URL(request.url);
if (url.pathname.startsWith("/agents/")) {
const user = await authenticate(request);
if (!user) {
return new Response("Unauthorized", { status: 401 });
}
// /agents/:binding/:name — the name is the fourth segment.
const name = url.pathname.split("/")[3];
if (name !== user.id) {
return new Response("Forbidden", { status: 403 });
}
}
return (
(await routeAgentRequest(request, env)) ??
new Response("Not found", { status: 404 })
);
},
} satisfies ExportedHandler<Env>;你可以从会话中自行选择名称,而不是检查客户端发送的名称。这样客户端就不会命名对象。更多信息,请参阅自定义路由。
身份验证决定谁可以访问对象。在对象内部,WebSockets 能力仍可限制每个连接。例如,将查看者的连接设为只读,使其只能接收状态而不能修改状态。