PHP CloudFront 代理/通过 API 转发视频流请求

Posted

技术标签:

【中文标题】PHP CloudFront 代理/通过 API 转发视频流请求【英文标题】:PHP CloudFront Proxy / Forward Requests For Video Streams Via API 【发布时间】:2020-10-18 13:25:30 【问题描述】:

我正在尝试转发客户端对流的请求,它使最初从视频播放器发出的请求保持不变:

Content-Type: Keep-Alive;
Range: 0-
...

我在用什么:

前端:Web - ReactJS 后端:php REST API CDN:AWS CloudFront 存储:AWS S3

建筑图形


原因:

我需要能够通过 REST 使用我们自己的 JWT 中间件对用户进行身份验证,以验证他们是否可以访问该文件。


约束:

不能使用nginx 转发请求,除非还有办法通过 PHP 中间件对其进行身份验证。


我的调查结果:

aws php sdk

我查看了 AWS PHP,但似乎缺少有关此特定功能的文档。

guzzle + php curl

恐怕我缺乏关于我需要传递到 CloudFront 以使其工作的知识。

cloudfront signed url/signature

除非我弄错了,否则这不会有帮助,因为访问的视频过期时间将由 AWS 设置,而不是由应用程序的 REST API 设置,因此如果他们刷新 JWT,它将不会使用签名进行更新。

why not s3 directly?

S3 不支持像 Range: 0-100 bytes 这样的块的标头。


任何帮助或建议都将不胜感激,即使这意味着建议购买一些预先构建的东西来看看他们是如何实施的。

======= 更新:2020 年 6 月 29 日 =======

在@ChrisWilliams 的推荐下,我最终在 AWS Lambda@Edge 上创建了一个脚本,配置如下:

触发器: CloudFront - viewer request

viewer request 的原因是因为它是从用户原始请求中获取 GET 查询参数的唯一方法。

功能代码:

(请原谅非常粗糙的代码以使事情正常运行)

文件: index.js

// IMPORTS
const zlib = require('zlib');
const https = require('https');

// html ERROR TEMPLATE
const content = `
<\!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <title>Unauthorized Page</title>
  </head>
  <body>
    <p>Unauthorized!</p>
  </body>
</html>
`;

// TRIGGER FUNCTION
exports.handler = async (event, context, callback) => 
    // Getting request and response
    const originalResponse = event.Records[0].cf.response;
    const request = event.Records[0].cf.request;

    // Setup for html page (cont content)
    const buffer = zlib.gzipSync(content); 
    const base64EncodedBody = buffer.toString('base64');
    
    // Response Templates
    var response401 = 
        headers: 
            'content-type': [key:'Content-Type', value: 'text/html; charset=utf-8'],
            'content-encoding' : [key:'Content-Encoding', value: 'gzip']
         ,
        body: base64EncodedBody,
        bodyEncoding: 'base64',
        status: '401',
        statusDescription: "OK"
     ;
     
    var response500 = 
        headers: 
            'content-type': [key:'Content-Type', value: 'text/html; charset=utf-8'],
            'content-encoding' : [key:'Content-Encoding', value: 'gzip']
         ,
        body: base64EncodedBody,
        bodyEncoding: 'base64',
        status: '500',
        statusDescription: "OK"
     ;
     
    // Perform Http Request
    const response = await new Promise((resolve, reject) => 
      
        // Expected ?token=ey...
        const req = https.get(`https://myauthserver.com/?$(request && request.querystring) || ''`, function(res) 
          if (res.statusCode !== 200) 
            return reject(response401);
          

          return resolve(
            status: '200'
          );
        );
        
        req.on('error', (e) => 
          reject(response500);
        );
    ).catch(error => error);
    
    // Get results from promise
    const results = await response;
    if (results.status === '200') 
        // Successful request - continue with the rest of the process
        callback(null, request);  
    

    // Not successful, show the errors results (401 or 500)
    callback(null, results);
;

注意:您必须尝试几次,以防因缓存而出现任何拼写错误或语法错误。我还建议尝试使用不同的 IP 地址来验证对内容的访问。更不用说,如果返回的请求的格式不正确,base64EncodedBody 会出现502 的情况。

双重注意:这是在查看了来自 AWS 的无效或过时的教程,并查看了多个开发人员的 cmets 无法正常工作之后。

【问题讨论】:

为什么不能使用 Labda@Edge 函数来验证 JWT 令牌? 这将如何工作?身份验证中间件位于单独的服务器上,因此 CloudFront 是否有一个特殊的钩子/中间件检查外部身份验证,如果是,它是否专门与 Lambda 绑定? 您可以拥有一个处理 JWT 标头的 Lambda@Edge 函数,可以在函数本身内进行验证,也可以调用您必须验证的外部端点。如果它无效,它将拒绝阻止它被转发的请求。这是亚马逊发表的一篇文章:aws.amazon.com/blogs/networking-and-content-delivery/… 到目前为止你有没有尝试过解决这个问题?通常要解决此问题,您只需编写一些类似代理的 PHP 代码,该代码接受请求并在验证后将其转换为适当的请求,然后将转换后的请求发送到上游并对响应执行相同操作(但以另一种方式)。有一个 PHP proxy 库可以作为起点,或者您可以按照 AWS 指南了解如何打开可搜索的 S3 流 here。 @apokryfos 我提到我尝试过,aws php ask、guzzle、cloudfront 和 aws php sdk for s3。 PHP 代理看起来很有希望,我会检查一下。谢谢你。 【参考方案1】:

我建议使用Lambda@Edge 函数,而不是在您的 CloudFront 前面添加第三阶段。

在您的 CloudFront 前添加代理可能会导致调试问题,并允许有人绕过代理到达您的 CloudFront 源而不将其锁定。

使用 Lambda@Edge 函数可确保解决方案验证 JWT 令牌的真实性,它可以配置为直接使用 Lambda 函数验证 JWT 令牌或让 Lambda 调用您构建的端点进行验证。如果 JWT 无效,它可以拒绝请求。

亚马逊有一个很棒的 article 带有一个演示堆栈,演示了如何使用它。

【讨论】:

只是想说声谢谢。这是正确的方法。尽管使用 CloudFront 和 Lamda@Edge 进行缓存有点烦人,而且将日志记录存储在不同的边缘位置这一事实非常具有挑战性,但我还是成功了。

以上是关于PHP CloudFront 代理/通过 API 转发视频流请求的主要内容,如果未能解决你的问题,请参考以下文章

通过 API 将域添加到 AWS Cloudfront 分发

CloudFront + EC2 + Apache + PHP

API 网关缓存与 CloudFront

AWS SOC 2 API 网关 / CloudFront

CloudFront 使用路径前缀重定向所有请求

cloudfront的默认缓存策略是啥(当常规lambda运行由api网关调用时)