You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

CloudFront+S3架构下基于User-Agent的访问控制实现及缓存、Lambda@Edge权限问题解决方案

解决方案:CloudFront + S3 基于User-Agent的访问控制(解决缓存与Lambda权限问题)

嘿,我来帮你一步步拆解问题并解决。你现在遇到两个核心问题:一是CloudFront的缓存绕过了S3桶策略的访问控制,二是Lambda@Edge在Viewer Request事件上触发时出现503权限错误。咱们逐个解决:

一、为什么S3桶策略的控制会被缓存绕过?

CloudFront的核心作用是缓存源站内容到边缘节点。当第一个带有合法User-Agent: example的请求到达时,CloudFront会从S3拉取内容并缓存。后续不管请求的User-Agent是否合法,只要缓存键匹配,就直接返回缓存内容——完全不会再去请求S3,自然也就绕过了S3桶策略的检查。

所以要解决这个问题,要么让CloudFront的缓存区分合法/非法请求的User-Agent,要么在缓存层(边缘节点)直接拦截非法请求。

二、方案1:调整CloudFront缓存策略(配合S3桶策略)

如果你想继续用S3桶策略控制访问,必须让CloudFront根据User-Agent生成不同的缓存条目:

  • 进入CloudFront控制台,找到你的分发,切换到「行为」标签,编辑目标行为
  • 在「缓存策略」部分,选择「创建新缓存策略」
  • 在缓存键设置中,添加User-Agent作为自定义头部(只添加这一个即可,避免缓存条目过多膨胀)
  • 保存策略并关联到你的分发行为

这样CloudFront会为不同的User-Agent缓存不同内容:合法请求的内容会被缓存,非法请求的403响应也会被缓存,后续同User-Agent的请求直接命中对应缓存,不会绕过控制。

但这个方案的缺点是:非法请求仍会回源一次(直到缓存了403响应),不如在边缘直接拦截高效。

三、方案2:用Lambda@Edge在Viewer Request阶段拦截(推荐)

Viewer Request事件是在CloudFront边缘节点接收请求后、检查缓存前触发的,能直接拦截非法请求,完全不会回源,性能更好。之前的503错误是因为Lambda的权限配置不对,按以下步骤修复:

1. 配置Lambda执行角色的信任策略

Lambda@Edge的Viewer Request函数需要允许CloudFront服务角色调用它:

  • 打开IAM控制台,找到Lambda的执行角色
  • 修改信任策略,添加CloudFront的服务主体:
    {
      "Version": "2012-10-17",
      "Statement": [
        {
          "Effect": "Allow",
          "Principal": {
            "Service": "lambda.amazonaws.com"
          },
          "Action": "sts:AssumeRole"
        },
        {
          "Effect": "Allow",
          "Principal": {
            "Service": "cloudfront.amazonaws.com"
          },
          "Action": "sts:AssumeRole",
          "Condition": {
            "StringEquals": {
              "AWS:SourceArn": "arn:aws:cloudfront::你的账号ID:distribution/你的分发ID"
            }
          }
        }
      ]
    }
    

2. 正确发布Lambda到边缘

  • Lambda@Edge的Viewer Request函数必须在**us-east-1(弗吉尼亚北部)**区域创建
  • 编写完函数后,必须发布一个正式版本(不能用$LATEST版本,CloudFront不支持)
  • 关联到CloudFront的Viewer Request事件时,选择刚才发布的版本,而非$LATEST

3. Lambda函数示例代码

这个函数会检查请求的User-Agent是否为example,不符合则直接返回403:

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

  // 检查User-Agent是否存在且严格等于"example"
  if (!userAgentHeader || userAgentHeader[0].value !== 'example') {
    const forbiddenResponse = {
      status: '403',
      statusDescription: 'Forbidden',
      body: 'Access Denied: Invalid User-Agent',
      headers: {
        'content-type': [{ key: 'Content-Type', value: 'text/plain' }]
      }
    };
    callback(null, forbiddenResponse);
    return;
  }

  // 符合条件,继续处理请求(检查缓存或回源)
  callback(null, request);
};

4. 简化S3桶策略

当用Viewer Request拦截后,非法请求根本不会到达S3,所以可以移除桶策略里的AWS:UserAgent条件,只保留允许CloudFront访问的规则:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "AllowCloudFrontServicePrincipalReadOnly",
      "Effect": "Allow",
      "Principal": {
        "Service": "cloudfront.amazonaws.com"
      },
      "Action": "s3:GetObject",
      "Resource": "arn:aws:s3:::<bucket-name>/*",
      "Condition": {
        "StringEquals": {
          "AWS:SourceArn": "arn:aws:cloudfront::account-number:distribution/EXAMPLEDISTO"
        }
      }
    }
  ]
}

四、为什么Origin Request能正常运行?

Origin Request事件是在CloudFront准备回源时触发的,这个阶段的Lambda权限要求更低,因为它是在你的AWS区域(而非边缘节点)执行的,所以不会出现503错误。但缺点是:非法请求仍会到达S3,浪费源站资源,而且还是需要调整缓存策略避免缓存绕过,不如Viewer Request高效。

总结

优先选择方案2(Lambda@Edge Viewer Request),既解决了缓存绕过问题,又能在边缘拦截非法请求,性能最优。只要配置好Lambda的信任策略和发布版本,就能解决之前的503错误。

内容的提问来源于stack exchange,提问作者Yahav

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.27 15:17:33