Angular SSR 中如何获取请求的原始来源

举报
汪子熙 发表于 2025/04/03 23:29:42 2025/04/03
【摘要】 这段代码是一个用于获取请求来源 (origin) 的函数,在 Node.js 中使用 Express 框架来处理 HTTP 请求时特别有用。通过理解这段代码,可以深入了解如何处理代理请求头,以及如何确保生成的请求源 (origin) 是可信且正确的。 主要目的这段代码的主要目的是获取当前请求的来源 (origin),即完整的协议和主机名 (protocol + hostname)。这个 or...

这段代码是一个用于获取请求来源 (origin) 的函数,在 Node.js 中使用 Express 框架来处理 HTTP 请求时特别有用。通过理解这段代码,可以深入了解如何处理代理请求头,以及如何确保生成的请求源 (origin) 是可信且正确的。

主要目的

这段代码的主要目的是获取当前请求的来源 (origin),即完整的协议和主机名 (protocol + hostname)。这个 origin 通常用在生成重定向链接、跨域资源共享 (CORS) 的情况下,确保正确地构建 URL。尤其在系统需要通过代理服务器 (reverse proxy) 处理请求时,函数需要获取正确的 host 信息以便使用。

这段代码结合了请求的协议 (req.protocol)、HTTP 请求头中的 HostX-Forwarded-Host,来生成请求的 origin。通过这种方式,能够确保函数获取到正确的主机地址,无论请求是通过直接访问,还是通过多个代理转发来的。

具体代码解释

trust proxy fn

const trustProxyFn = req.app.get('trust proxy fn');

trustProxyFn 是 Express 应用程序的一个内部函数,用于判断一个特定的请求是否来自受信任的代理服务器。req.app.get('trust proxy fn') 通过从 Express 应用实例中获取到当前的 trust proxy 配置。如果应用程序配置了 trust proxy,那么这个函数将被用来判断传入请求的远程地址 (req.connection.remoteAddress) 是否属于受信任的代理。

X-Forwarded-Host 的处理

let forwardedHost = req.get('X-Forwarded-Host');

X-Forwarded-Host 是一种常见的 HTTP 请求头,它通常被代理服务器添加,用来告诉最终的 Web 服务器,原始请求的主机 (host) 信息是什么。因为请求可能经过多个代理服务器转发,因此在某些应用场景中,必须获取到正确的 host,这也是函数检查 X-Forwarded-Host 的原因。

判断受信任代理与多主机处理

if (forwardedHost && trustProxyFn(req.connection.remoteAddress, 0)) {
  if (forwardedHost.indexOf(',') !== -1) {
    forwardedHost = forwardedHost
      .substring(0, forwardedHost.indexOf(','))
      .trimRight();
  }
  return `${req.protocol}://${forwardedHost}`;
}

这里的逻辑是为了确保只有在请求是从可信任的代理服务器发来的时候,才会使用 X-Forwarded-Host 头的值。如果 trustProxyFn(req.connection.remoteAddress, 0) 返回 true,表示该请求是来自于受信任的代理。

另外,如果 X-Forwarded-Host 中包含逗号,表明可能存在多个主机名,这是因为多个代理服务器可能会将其自己添加到请求头里。代码通过调用 .substring() 获取逗号前面的第一个主机名,使用 trimRight() 去除掉多余的空格,从而确保 forwardedHost 是一个单一的、有效的主机名。

返回请求的 origin

return `${req.protocol}://${forwardedHost}`;

代码在确认受信任代理的情况下返回 forwardedHost,与请求的协议组合成完整的 origin,例如 https://example.com

默认处理

else {
  return `${req.protocol}://${req.get('host')}`;
}

如果没有找到 X-Forwarded-Host,或者请求不是来自受信任的代理,那么返回默认的请求源信息,即使用请求的协议加上 Host 请求头中的内容,确保生成一个合理的 origin

举例说明

场景一:普通请求

假设你有一个正在运行的应用,它没有使用代理服务器。客户端直接向服务器发送请求。在这种情况下,req.get('X-Forwarded-Host') 将返回 undefined,因为没有代理服务器插入该请求头。

假设请求使用的是 HTTPS 协议,并且主机名为 myapp.com,则 req.protocolhttpsreq.get('host')myapp.com

那么函数 getRequestOrigin(req) 的返回结果将是:

https://myapp.com

场景二:通过代理请求

假设应用程序通过反向代理服务器部署,代理服务器在请求头中添加了 X-Forwarded-Host。这个请求头通常表示最初的主机名。例如,客户端请求的 URL 为 https://client.com,代理服务器将请求转发到你的应用,并且添加 X-Forwarded-Host: client.com

在这种情况下,假设应用配置了信任代理,且代理服务器的 IP 地址被列入受信任列表中:

  • req.get('X-Forwarded-Host') 的值为 client.com
  • trustProxyFn(req.connection.remoteAddress, 0) 返回 true,表示代理服务器是可信的
  • req.protocolhttps

函数返回结果将是:

https://client.com

场景三:多层代理的情况

在复杂的架构中,请求可能经过多个代理服务器转发,这时 X-Forwarded-Host 头部中可能包含多个值,以逗号分隔。例如,假设请求头为:

X-Forwarded-Host: proxy1.com, proxy2.com, client.com

在这种情况下,代码通过以下步骤处理:

  • 使用 .indexOf(',') 找到第一个逗号的位置
  • 使用 .substring(0, forwardedHost.indexOf(',')) 提取出第一个主机名,即 proxy1.com
  • 使用 .trimRight() 去除可能存在的右侧空格

最后,函数返回的结果将是:

https://proxy1.com

场景四:没有信任代理配置

如果应用没有设置信任代理 (trust proxy),或者请求不是从受信任的代理服务器发来的,即 trustProxyFn(req.connection.remoteAddress, 0) 返回 false,即便存在 X-Forwarded-Host 头部,函数也不会使用这个值,而是默认采用 req.get('host')

假设:

  • req.protocolhttp
  • req.get('host')backend.com

那么返回结果为:

http://backend.com

安全性与可靠性考量

在这段代码中,安全性与可靠性是重要的考虑因素。

受信任的代理

trust proxy 是一个至关重要的安全配置。只有当请求来自受信任的代理时,代码才会使用 X-Forwarded-Host 的值。这是因为 X-Forwarded-Host 可以由任意客户端伪造,信任未验证的 X-Forwarded-Host 可能导致开放重定向漏洞,或其他类型的安全威胁。

多值处理

代码中考虑了 X-Forwarded-Host 可能存在多个值的情况。这在请求经过多个代理服务器时非常有用。多个值以逗号分隔,而代码确保只使用第一个值,这样可以更精确地反映原始请求的主机信息,避免混淆。

总结与注意事项

通过分析,我们可以看出这段代码的主要作用是从 HTTP 请求中准确提取出请求的 origin,特别是考虑到代理服务器的存在情况。

  • trust proxy 的重要性:确保只信任来自受信任代理的 X-Forwarded-Host
  • 处理多重代理:考虑到 X-Forwarded-Host 包含多个值的情况,通过提取第一个值来保证安全和准确性。
  • 协议与主机的组合:结合协议和主机来生成正确的 origin,确保返回结果可以用于跨域资源共享、安全重定向等功能。

在实际应用中,理解代理服务器的行为和正确处理代理请求头对于构建安全和可靠的后端系统至关重要。这段代码展示了如何在面对复杂的代理环境时有效管理请求源。

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。