跳转到内容
本站为社区维护的非官方中文翻译,与 Cloudflare, Inc. 无关联。本站不提供账户登录,请勿在任何非官方站点输入您的 Cloudflare 账户信息。官方文档:developers.cloudflare.com

身份验证

最后更新 查看 MarkdownAgent 设置

任何知道代理 URL 的人都可以向其发送请求。请在请求到达 Durable Object 之前,在 Worker 中检查调用者身份。对象只会看到你已允许的请求。

使用路由钩子

routeAgentRequest() 在转发 WebSocket 升级请求前运行 onBeforeConnect,在转发 HTTP 请求前运行 onBeforeRequest。两个钩子都在 Worker 中运行,并且都可以:

  • 返回 Response 以拒绝请求。
  • 返回新的 Request 来替换原请求,例如添加对象信任的请求头。
  • 不返回值,将请求原样转发。

route 参数包含匹配的绑定名称 className,以及对象的 name:

src/index.jsjs
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 })
		);
	},
};
src/index.tsts
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() 不处理的路由:

src/index.jsjs
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 })
		);
	},
};
src/index.tsts
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 能力仍可限制每个连接。例如,将查看者的连接设为只读,使其只能接收状态而不能修改状态。

这篇文档对您有帮助吗?