AWS Lambda授权器添加自定义Header后Spring服务未接收问题排查
问题背景(翻译后)
我的服务基于AWS云基础设施构建,客户端发起请求时,通过API Gateway的Node.js版Lambda授权器校验JWT Token。Token验证通过后,解析Token获取用户ID并将其添加至请求Header中。目前授权逻辑已实现成功,但我在Lambda中添加event.headers["userId"] = userId;代码后,VPC Link后的Spring服务始终无法接收到该Header。我尝试过创建集成请求映射模板,但未达到预期效果,配置后请求甚至无法到达Spring服务,API Gateway直接返回错误:{"message": "internal server error"}。请求链路为:client -> api gateway -> lambda authorizer(授权并添加userId header) -> my vpc-link server
核心问题说明
Lambda授权器里直接修改event.headers完全没用,因为API Gateway不会自动同步授权器内的event修改到后端请求。正确路径是:通过授权器返回结果注入自定义上下文变量,再通过集成请求映射模板将变量转为请求头。
步骤1:修改Lambda授权器返回值
删掉修改event.headers的代码,改成在授权器返回结构中添加context字段存放userId:
// Node.js Lambda授权器示例代码 exports.handler = async (event) => { // 此处省略你的JWT验证逻辑 const userId = 解析JWT得到的用户ID; // 必须返回标准授权结构,context用于传递自定义变量 return { principalId: userId, // 可填任意合法用户标识,建议直接用userId policyDocument: { Version: '2012-10-17', Statement: [ { Action: 'execute-api:Invoke', Effect: 'Allow', Resource: event.methodArn // 必须与当前请求的资源ARN一致 } ] }, context: { userId: userId.toString() // 强制转字符串:API Gateway不支持非字符串类型的上下文变量 } }; };
步骤2:配置集成请求映射模板(解决500错误的关键)
之前配置模板报错500,大概率是模板语法错误或清空了后端需要的原有请求头。按以下步骤操作:
- 进入API Gateway控制台,找到目标API的对应资源和方法
- 切换到集成请求标签,找到映射模板区域
- 点击添加映射模板,输入你的请求内容类型(如
application/json,表单请求则填application/x-www-form-urlencoded),勾选「当没有模板匹配请求内容类型时通过」 - 在模板编辑框中输入以下内容(以JSON请求为例):
{ "body": $input.json('$'), "headers": { #foreach($header in $input.params().header.keySet()) "$header": "$util.escapeJavaScript($input.params().header.get($header))" #if($foreach.hasNext),#end #end, "userId": "$context.authorizer.userId" } }
注意:必须保留原有请求头的遍历逻辑,否则会丢失客户端传来的Authorization、Content-Type等关键头,这是导致500错误的最常见原因。
- 保存配置后,必须重新部署API到对应阶段才会生效
步骤3:Spring服务接收Header
在Spring控制器中直接通过注解接收:
@GetMapping("/xxx") public ResponseEntity<?> test(@RequestHeader("userId") String userId) { // 业务逻辑 return ResponseEntity.ok(userId); }
常见坑点排查
- Lambda授权器的context变量必须是字符串:数字类型会被API Gateway自动丢弃,务必转成toString()
- 映射模板语法错误:检查逗号、引号是否遗漏,
$context.authorizer.userId是否拼写正确 - 未部署API:修改授权器或映射模板后,必须重新部署到目标阶段
- 大小写问题:API Gateway会将自定义头转为小写?不,模板里写的"userId"就是后端接收的头名,,保持一致即可
内容的提问来源于stack exchange,提问作者leechan

