Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
432 views
in Technique[技术] by (71.8m points)

symfony - Pass cookie to CloudFront origin but prevent from caching

I am using CloudFront as cache in front of my Symfony web application. To get a cache based on a user's role (admin, customer,...) I generate a user role based hash in a Lambda@Edge Viewer Request trigger. I pass that hash on as a request header to my origin as X-User-Context-Hash.

My problem is now that I need to pass the PHPSESSID cookie on to my origin to get the right response for caching, but I do not want to base the cache on the value of PHPSESSID. I do only need my cached response to be based on the value of X-User-Context-Hash but not on my session cookie.

The image below should explain my problem in detail

Is there any possibility to accomplish that?

Would appreciate any help.

enter image description here

Here's my Lambda@Edge Viewer Request trigger:

'use strict';

function parseCookies(headers) {
    const parsedCookie = {};
    if (headers.cookie) {

        console.log(`${headers.cookie[0].value}`);

        headers.cookie[0].value.split(';').forEach((cookie) => {
            if (cookie) {
                const parts = cookie.split('=');
                parsedCookie[parts[0].trim()] = parts[1].trim();
            }
        });
    }
    return parsedCookie;
}

exports.handler = (event, context, callback) => {
    const request = event.Records[0].cf.request;
    const headers = request.headers;

    const https = require('https');

    // Read session cookie
    const parsedCookies = parseCookies(headers);
    let cookie = '';
    if (parsedCookies) {
        if(parsedCookies['PHPSESSID']) {
            cookie = `PHPSESSID=${parsedCookies['PHPSESSID']}`;
        }
    }

    console.log(`Cookie: ${cookie}`);

    // Send request to origin host at /_fos_user_context_hash
    // passing the original session cookie
    const options = {
        hostname: `${request.headers.host[0].value}`,
        port: 443,
        path: '/_fos_user_context_hash',
        method: 'HEAD',
        headers: {
            'Cookie': cookie, 
            'Accept': 'application/vnd.fos.user-context-hash',
            'Vary' : 'Cookie'
        }
    };

    const req = https.request(options, (res) => {
      console.log('statusCode:', res.statusCode);
      console.log('headers:', res.headers);

      // Read the X-User-Context-Hash from the hash endpoint
      const headerName = 'X-User-Context-Hash';
      let hash = 'anonymous';

      if (res.headers[headerName.toLowerCase()]) {
        hash = res.headers[headerName.toLowerCase()];
      }

      // Append X-User-Context-Hash before passing request on to CF
      request.headers[headerName.toLowerCase()] = [{ key: headerName, value: hash }];  

      callback(null, request);

    }).on('error', (e) => {
      console.error(e);
      // Forward request anyway
      callback(null, request);
    });

    req.end();
}


;
See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

Here's how I finally solved my problem:

CloudFront behavior

I configured the behavior not to forward any cookies to the origin, but only cache based on the headers Host and X-User-Context-Hash (see screenshot).

Screenshot CloudFront behavior

The following image explains my lambda@edge process: lambda@edge process

  1. In the "Viewer Request" trigger I read the user-based cookies named PHPSESSID and REMEMBERME and pass those values via the X-Session-Cookies header on.
  2. If the there's a match for my request url and the given Host and X-User-Context-Hash headers, Cloud-Front returns the cached item and stops here.
  3. If there's no match the "Origin Request" trigger is fired. When that event fires the custom header X-Session-Cookies is available. So I take the value from the X-Session-Cookies header and set the value of request.headers.cookie to that value. This step ensures that the PHPSESSID and REMEMBERME cookie are both passed to the origin before the page gets cached.

My Lambda@Edge functions:

The Viewer Request trigger:

'use strict';

function parseCookies(headers) {
    const parsedCookie = {};
    if (headers.cookie) {

        console.log(`${headers.cookie[0].value}`);

        headers.cookie[0].value.split(';').forEach((cookie) => {
            if (cookie) {
                const parts = cookie.split('=');
                parsedCookie[parts[0].trim()] = parts[1].trim();
            }
        });
    }
    return parsedCookie;
}

exports.handler = (event, context, callback) => {
    const request = event.Records[0].cf.request;
    const headers = request.headers;

    const https = require('https');

    let sessionId = '';

    // Read session cookie
    const parsedCookies = parseCookies(headers);
    let cookie = '';
    if (parsedCookies) {
        if(parsedCookies['PHPSESSID']) {
            cookie = `PHPSESSID=${parsedCookies['PHPSESSID']}`;
        }
        if(parsedCookies['REMEMBERME']) {
            if (cookie.length > 0) {
                cookie += ';';
            }
            cookie += `REMEMBERME=${parsedCookies['REMEMBERME']}`;
        }
    }

    console.log(`Cookie: ${cookie}`);

    // Send request to origin host at /_fos_user_context_hash
    // passing the original session cookie
    const options = {
        hostname: `${request.headers.host[0].value}`,
        port: 443,
        path: '/_fos_user_context_hash',
        method: 'HEAD',
        headers: {
            'Cookie': cookie, 
            'Accept': 'application/vnd.fos.user-context-hash',
            'Vary' : 'Cookie'
        }
    };

    const req = https.request(options, (res) => {
      console.log('statusCode:', res.statusCode);
      console.log('headers:', res.headers);

      // Read the X-User-Context-Hash from the hash endpoint
      const headerName = 'X-User-Context-Hash';
      let hash = 'anonymous';

      if (res.headers[headerName.toLowerCase()]) {
        hash = res.headers[headerName.toLowerCase()];
      }

      // Append X-User-Context-Hash before passing request on to CF
      request.headers[headerName.toLowerCase()] = [{ key: headerName, value: hash }];

      const sessionHeaderName = 'X-Session-Cookies';
      request.headers[sessionHeaderName.toLowerCase()] = [{ key: sessionHeaderName, value: cookie }];  

      callback(null, request);

    }).on('error', (e) => {
      console.error(e);
      // Forward request anyway
      callback(null, request);
    });

    req.end();
}


;

The Origin Request trigger:

exports.handler = (event, context, callback) => {
    const request = event.Records[0].cf.request;

    const sessionHeaderName = 'X-Session-Cookies';

    let cookie = '';
    if (request.headers[sessionHeaderName.toLowerCase()]) {
        console.log(request.headers[sessionHeaderName.toLowerCase()]);
        cookie = request.headers[sessionHeaderName.toLowerCase()][0].value;
    }

    request.headers.cookie = [{ key : 'Cookie', value : cookie }];

    callback(null, request);
};

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...