你的内部文档正在裸奔。部署在S3上的Docusaurus站点——那些API规范、运维手册、新人指南——任何人拿到URL就能直接访问。S3加CloudFront确实给了HTTPS、缓存和全球分发,但偏偏没给登录页。
多数团队的选择只有两个:要么把文档搬到Notion、Confluence这类平台然后放弃控制权,要么一股脑塞进VPN里完事。两种方案都能用,但烦人的 trade-off 很快就会找上门。
我想要第三条路:静态站点保持原样(我用的是Docusaurus,换别的也一样),继续放在S3+CloudFront上(便宜、快、零维护),同时在前面加一层真正的认证,而且不动站点代码、不动构建流水线。
最终产物是 docusaurus-cognito-auth ——一套基于Lambda@Edge和AWS Cognito的纯无服务器认证层。本文是架构拆解、决策思路,以及踩过的坑。
技术栈极简:四个AWS服务各干各的。S3存静态文件,私有桶,不开网站托管,部署靠aws s3 sync单独走。CloudFront挡在前面,管TLS、缓存、压缩、全球边缘节点,通过OAC访问S3,桶完全私有,只有CloudFront能读。
认证逻辑全在Lambda@Edge。两个函数,都是CloudFront的viewer-request触发器:一个查JWT cookie,一个处理OAuth回调。Cognito当身份提供商,User Pool带Hosted UI,注册登录、密码重置、邮件验证全包。Lambda跟它的token端点通信,用JWKS验签。
核心约束:认证层和静态站点彻底解耦。换站点(Docusaurus、Next.js导出、纯HTML)不用重部署认证;改认证不用重建站点。两个独立 concern,两个独立发布周期。
请求流程其实就两种情况。有合法cookie时:浏览器→CloudFront→auth-check Lambda→验签通过→S3→返回页面。函数提取auth_token,用RS256验JWT签名、查过期时间、issuer、audience,全过就原样返回请求对象,CloudFront继续去S3拿页面。用户无感知,边缘执行约1毫秒。
无cookie或验签失败时:auth-check把请求重定向到Cognito Hosted UI,带好回调URL和PKCE参数。用户登录后,Cognito跳回第二个Lambda函数,它用授权码换token,设好cookie,再302回原始页面。全程无后端服务器。
几个关键决策。JWT放cookie而非localStorage,是为了让浏览器自动带上,Lambda@Edge能直接读。RS256而非HS256,因为验签只需公钥,Lambda不用存Cognito客户端密钥。OAC而非 OAI,OAC是AWS推荐的新方案,支持更多特性。Private S3 bucket而非静态网站托管,彻底杜绝桶策略泄露风险。
踩过的坑。Lambda@Edge有诸多限制:必须us-east-1部署,viewer-request有128KB请求体上限,不支持环境变量(配置硬编码或从SSM拉),日志分散到各个边缘区域CloudWatch,调试极痛苦。Cognito Hosted UI的回调URL必须精确匹配,多一个斜杠都失败。Cookie的Domain和Path设置要仔细,跨子域场景容易翻车。
成本方面,个人或小团队几乎免费。Lambda@Edge按调用次数和运行时间计费,认证检查1毫秒级别,月调用百万次约几美元。Cognito User Pool前5万用户免费。CloudFront和S3按原有流量走,无额外开销。
这套方案的代价是AWS锁定。Lambda@Edge和Cognito都是AWS专属,迁移成本不低。但对于已经深度使用AWS的团队,这是用最小侵入性给静态资产加认证的务实选择——不用写一行后端代码,不用改站点构建,部署一次,长期受益。
热门跟贴