【问题标题】:Get remote client IP address in Deno在 Deno 中获取远程客户端 IP 地址
【发布时间】:2022-02-07 04:18:19
【问题描述】:

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

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

在 NodeJS/Express 中,ip 对象的 ip 属性可以做同样的事情。 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 }
);

是否有任何其他解决方法可以防止从同一设备进行多次访问?

谢谢

【问题讨论】:

    标签: http networking ip deno


    【解决方案1】:

    由于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`);
    
    

    类型

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

    async function serve(handler: Handler, options?: ServeInit): Promise<void>;
    

    Handler 回调接受两个参数:RequestConnInfo 类型的对象:

    type Handler = (request: Request, connInfo: ConnInfo) => Response | Promise<Response>;
    

    ConnInfo 看起来像这样:

    interface ConnInfo {
      readonly localAddr: Deno.Addr;
      readonly remoteAddr: Deno.Addr;
    }
    

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

    // in the Deno namespace
    
    type Addr = NetAddr | UnixAddr;
    

    这就是它变得复杂的地方。 Deno.AddrDeno.NetAddrDeno.UnixAddr 中的discriminated union(这意味着它可以是任何一个),而属性transport用于区分两者。

    // in the Deno namespace
    
    interface NetAddr {
      hostname: string;
      port: number;
      transport: "tcp" | "udp";
    }
    
    interface UnixAddr {
      path: string;
      transport: "unix" | "unixpacket";
    }
    

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

    listener 在内部创建以支持服务器is actually only listening on TCP,因此我认为可以安全地假设远程地址将是网络地址。但是,由于 serve 函数中 Handler 回调参数的类型签名并没有明确说明(尽管它应该!),TypeScript 不知道。

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

    如果您想在地址不是网络地址的情况下抛出错误之外的其他操作:您可以在代码中使用type predicate 作为条件,而不是断言函数。

    Here is a link 到 TypeScript Playground,在那里我使用示例中使用的类型创建了一个 Playground,因此您可以探索/实验。


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

    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);
    };
    

    【讨论】:

    • 非常感谢您提供如此详细的解释!现在我对 deno 和 TS 有了更好的理解:D 再次感谢。
    • @Rishi 很高兴您在阅读答案后有这种感觉! (顺便说一句——如果你想继续的话——我在 repo 中打开了an issue for this。)
    猜你喜欢
    • 2018-04-30
    • 2019-01-14
    • 1970-01-01
    • 1970-01-01
    • 2012-05-10
    • 1970-01-01
    • 2013-07-07
    • 2011-05-26
    • 2019-11-13
    相关资源
    最近更新 更多