在 Deno 中获取远程客户端 IP 地址

Ris*_*shi 4 ip networking http deno

如何在 Deno 中获取客户端的IP 地址?

我已经使用标准http库创建了一个测试服务器,但我无法找到提取客户端 IP 的方法。我需要它作为防止多次提交的安全功能。

在 NodeJS/Express 中,对象ip的一个​​属性request具有相同的作用。 req.ip给出了我在 Express 中想要的东西,但它在 Deno 中的等价物是什么?

我的代码是:

import { serve } from "https://deno.land/std@0.125.0/http/server.ts";

serve(
    (req) => {
        console.log(/* the client IP */);
        return new Response("hello");
    },
    { port: 8080 }
);

Run Code Online (Sandbox Code Playgroud)

是否有其他解决方法可以防止同一设备的多次访问?

谢谢

jse*_*ksn 11

由于键入方式的原因,以类型安全的方式执行此操作有点复杂serve。首先,我将向您展示如何执行此操作的示例,然后我将解释这些类型。

例子

example.ts:

import {
  serve,
  type ConnInfo,
  type Handler,
  type ServeInit,
} from 'https://deno.land/std@0.125.0/http/server.ts';

function assertIsNetAddr (addr: Deno.Addr): asserts addr is Deno.NetAddr {
  if (!['tcp', 'udp'].includes(addr.transport)) {
    throw new Error('Not a network address');
  }
}

function getRemoteAddress (connInfo: ConnInfo): Deno.NetAddr {
  assertIsNetAddr(connInfo.remoteAddr);
  return connInfo.remoteAddr;
}

const handler: Handler = (request, connInfo) => {
  const {hostname, port} = getRemoteAddress(connInfo);
  const message = `You connected from the following address: ${hostname}`;
  return new Response(message);
};

const init: ServeInit = {port: 8080};

serve(handler, init);
console.log(`Listening on port ${init.port}...\nUse ctrl+c to stop`);

Run Code Online (Sandbox Code Playgroud)

类型

查看该serve函数的文档,您可以看到它接受两个参数:类型的回调Handler和一些类型的选项ServeInit

async function serve(handler: Handler, options?: ServeInit): Promise<void>;
Run Code Online (Sandbox Code Playgroud)

回调Handler接受两个参数: aRequest和类型为 的对象ConnInfo

type Handler = (request: Request, connInfo: ConnInfo) => Response | Promise<Response>;
Run Code Online (Sandbox Code Playgroud)

ConnInfo看起来像这样:

interface ConnInfo {
  readonly localAddr: Deno.Addr;
  readonly remoteAddr: Deno.Addr;
}
Run Code Online (Sandbox Code Playgroud)

应该具有远程 IP 地址的部分(从技术上讲,它是远程主机名,但它很可能是一个 IP 地址,除非您在服务器环境中配置了自定义 DNS 设置)是 处的对象connInfo.remoteAddr,其中(您可以在上面看到)类型为Deno.Addr,如下所示:

// in the Deno namespace

type Addr = NetAddr | UnixAddr;
Run Code Online (Sandbox Code Playgroud)

这就是事情变得复杂的地方。 Deno.Addr是和的可区分联合(这意味着它可以是其中之一),并且该属性用于区分两者。Deno.NetAddrDeno.UnixAddrtransport

// in the Deno namespace

interface NetAddr {
  hostname: string;
  port: number;
  transport: "tcp" | "udp";
}

interface UnixAddr {
  path: string;
  transport: "unix" | "unixpacket";
}
Run Code Online (Sandbox Code Playgroud)

网络地址具有hostname属性(其值是 IP 地址)和属性port,而 UNIX 地址具有属性path

内部创建的用于支持服务器的侦听实际上只侦听 TCP,因此我认为可以安全地假设远程地址将是一个网络地址。Handler然而,由于函数中回调参数的类型签名serve并没有明确这一点(尽管它应该如此!),TypeScript 并不知道这一点。

因此,作为程序员,您需要确保该地址实际上是一个网络地址,然后才能以类型安全的方式访问网络地址(而不是 unix 地址)上的属性。这就是类型断言函数 assertIsNetAddr发挥作用的地方。(类型断言执行运行时测试,从而向编译器提供条件的“保证”:如果无法保证条件,则抛出异常。)因为作为程序员,您已经比 TypeScript 编译器了解更多(即该地址位于 TCP 上并且将是一个网络地址),您可以断言该地址确实是一个网络地址。然后编译器将允许您使用该地址作为网络地址。

如果您除了在地址不是网络地址的情况下抛出错误之外还想执行其他操作:您可以在代码中使用类型谓词作为条件,而不是断言函数。

这是 TypeScript Playground 的链接,我在其中使用示例中使用的类型创建了一个 Playground,以便您可以探索/实验。


最后,(这不是类型安全的)如果您只想使用不进行检查的值(因为您已经完成了研究并且确信您永远不会处理非 TCP 连接,您可以简单地使用类型断言:

const handler: Handler = (request, connInfo) => {
  const {hostname, port} = connInfo.remoteAddr as Deno.NetAddr;
  const message = `You connected from the following address: ${hostname}`;
  return new Response(message);
};
Run Code Online (Sandbox Code Playgroud)