查看者请求上的Cloudfront Lambda @ edge设置cookie

时间:2019-03-12 18:41:36

标签: amazon-web-services aws-lambda amazon-cloudfront aws-lambda-edge

更新:更好地收集了我的想法

我正在为查看器请求Lambda中的每个用户生成一个唯一标识符(UUID),然后根据该UUID选择要返回的缓存页面。这可行。

理想情况下,此用户将始终具有相同的UUID。

我必须在查看器请求中生成该UUID ,如果该UUID不存在于该查看器请求的Cookie中。我还需要将UUID设置为cookie,这当然发生在响应中而不是请求中。

没有缓存,我的服务器仅处理获取自定义标头并在响应标头中创建Set-Cookie。

如果要缓存页面,我没有找到处理此问题的方法。我可以忽略用于缓存的请求标头,并提供正确的缓存页面,但是由于没有将Cookie设置为在下一个请求中使用,因此用户不会坚持使用UUID。

有人能做到这样吗?

我正在尝试的事情

我正在处理一些问题,但尚未开始工作:

  1. 我没有意识到Cloudfront中的某些设置,该设置会处理从Viewer Request到Viewer Response的标头或其他数据传递,这些设置可能会在Cloudfront中的第二个lambda中使用。

    < / li>
  2. 在查看器请求中抢先修改响应对象标头。我认为这是不可能的,因为还没有创建返回标头,除非我缺少一些内置的Cloudfront方法。

  3. 一个现有的某种传递标头,我什至不知道那是不是真的,因为我对请求-响应处理的这一方面并不十分熟悉,但是值得一试。

  4. 可能(虽然尚未尝试),我可以在Client Request lambda中创建整个响应对象,并以某种方式从那里提供缓存的页面,修改响应标头,然后将其传递给回调方法。

Tobin's答案确实有效,但不是一个可靠的解决方案。如果用户不存储或提供他们的cookie,它将成为一个无限循环,此外,如果可以避免的话,我宁愿不要在所有页面前抛出重定向


一些可行的概念

  1. 当Cookie中不存在UUID时,查看器请求Lambda会生成UUID
  2. 查看器请求Lambda在请求对象的标头上的cookie中设置UUID。通过传入的更新请求对象进行回调
  3. UUID cookie破坏了Cloudfront缓存
  4. 使用UUID触发原始请求Lambda
  5. 来源请求Lambda通过设置了UUID cookie的http.get再次调用原始请求URL(限制为40KB,这使得在“查看器请求Lambda”中这样做不切实际)
  6. 查看器请求Lambda的第二种情况,看到现在存在UUID,剥离UUID cookie,然后正常继续请求
  7. 第二个原始请求(如果尚未缓存)-缓存的响应(如果已缓存),因为不存在缓存无效的UUID-将实际页面HTML返回到第一个原始请求
  8. 第一来源请求收到来自http.get的包含HTML
  9. 的响应
  10. 第一个原始请求会创建自定义响应对象,该对象包含http.get中的响应正文和设置有我们原始UUID的Set-Cookie标头

已经设置了UUID的后续调用将从cookie中剥离UUID(以防止缓存破坏),并直接跳到Viewer Request Lambda中的第二种情况,后者将直接加载页面的缓存版本。

我说“有点”是因为当我尝试击中端点时,我下载了一个二进制文件。

编辑

这是因为我没有设置content-type标头。我现在只有一个302重定向问题...如果克服了这个问题,我将发布完整答案。


原始问题

我在查看器请求上有一个功能,可以选择一个选项并在从缓存或服务器中检索请求之前设置请求中的某些内容。

那行得通,但我希望它记住将来用户的选择。想法是简单地设置一个cookie,以便下次用户访问时可以阅读。因为这是在查看器请求上,而不是在查看器响应上,所以我还没有弄清楚如何做到这一点,或者甚至通过Lambda本身是否可能实现。

Viewer Request -> 
  Lambda picks options (needs to set cookie) -> 
    gets corresponding content -> 
      returns to Viewer with set-cookie header intact

我有seen the examples,并且能够通过Lambda在“查看器响应”中成功设置Cookie。这对我没有多大帮助,因为需要根据请求做出决定。毫不奇怪,将此代码添加到查看器请求中不会在响应中显示任何内容。

2 个答案:

答案 0 :(得分:1)

我认为设置不存在的cookie的真正正确方法是使用Set-Cookie将302重定向返回到相同的URI,然后让浏览器重做请求。这可能不会有太大影响,因为浏览器可以重用相同的连接来“跟随”重定向。

但是,如果您坚持不这样做,则可以使用Viewer Request触发器将cookie注入请求中,然后在Viewer Response触发器中发出具有相同值的Set-Cookie

在查看器 response 事件中的 request 对象可以在原始 request 事件中的同一位置找到, event.Records[0].cf.request

在观众响应触发器中,结构的这一部分包含"request that CloudFront received from the viewer and that might have been modified by the Lambda function that was triggered by a viewer request event."

请谨慎使用,以确保您正确处理cookie标头。 Cookie request header需要仔细且准确的操作,因为存在多个Cookie的浏览器can use multiple formats

从前,有时需要将cookie作为单个请求标头发送。

Cookie: foo=bar; buzz=fizz

通过在;后跟<space>上拆分值来解析它们。

但是浏览器可能还会用多个标题将它们拆分,如下所示:

Cookie: foo=bar
Cookie: buzz=fizz

在后一种情况下,数组event.Records[0].cf.request.headers.cookie将包含多个成员。您需要检查该数组中每个对象的value属性,检查每个对象中是否有多个值,并适应以下事实:如果不存在cookie,则该数组将完全未定义(不为空)。


奖金:这是我写的一个函数,我相信它可以正确处理所有情况,包括没有cookie的情况。它将提取您要查找的名称的cookie。 Cookie names are case-sensitive

// extract a cookie value from request headers, by cookie name
// const my_cookie_value = extract_cookie(event.Records[0].cf.request.headers,'MYCOOKIENAME');
// returns null if the cookie can't be found
// https://stackoverflow.com/a/55436033/1695906

function extract_cookie(headers, cname) {

    const cookies = headers['cookie'];
    if(!cookies)
    {
        console.log("extract_cookie(): no 'Cookie:' headers in request");
        return null;
    }

    // iterate through each Cookie header in the request, last to first

    for (var n = cookies.length; n--;)
    {
        // examine all values within each header value, last to first

        const cval = cookies[n].value.split(/;\ /);
        const vlen = cval.length;

        for (var m = vlen; m--;)
        {
            const cookie_kv = cval[m].split('=');
            if(cookie_kv[0] === cname)
            {
                return cookie_kv[1];
            }            
        } // for m (each value)    
    } // for n (each header)

    // we have no match if we reach this point
    console.log('extract_cookie(): cookies were found, but the specified cookie is absent');
    return null;

}

答案 1 :(得分:0)

该问题发布已经一年多了。希望您找到了解决方案,并可以与我们分享!

我正面临着同样的问题,我也在考虑无限循环……那又如何呢?

  • 查看器请求事件发送回一个带有cookie集的302响应,例如uuid=whatever 和GET参数添加到了位置标头中的网址,例如_uuid_set_=1

  • 在下一个设置了GET参数_uuid_set_(等于1,但这不是必需的)的查看器请求中,将有两个选项:

    • 未设置Cookie uuid,在这种情况下,您可以发送回响应500来中断循环,或者根据需要返回

    • 或设置了cookie,在这种情况下,您将另一个302发送回去,并且删除了参数_uuid_set_,因此最终用户永远不会看到它,并且无法复制粘贴和共享它,我们所有人都可以在晚上睡觉。