如何在AWS自定义授权器(Custom Authorizer)中实现访客模式:无Authorization头触发Lambda,有授权头验证JWT
你遇到的这个问题确实是API Gateway Custom Authorizer的一个默认限制——当你指定的identitySource(比如Authorization头)不存在时,API Gateway会直接返回401 Unauthorized,根本不会触发授权器Lambda,自然没法绕过它去调用你的业务函数。不过除了你提到的在业务Lambda里写自定义授权逻辑之外,还有一个更贴合你需求的方案,我来详细拆解:
方案一:修改授权器配置,让它始终被触发
API Gateway的identitySource支持指定多个参数来源,只要其中任意一个存在,就会触发授权器。我们可以利用这一点,在原有Authorization头的基础上,添加一个始终存在的请求上下文参数(比如context.requestId,每个请求都会生成唯一的requestId)。这样一来,哪怕请求里没有Authorization头,授权器也会被调用,我们就能在授权器里主动处理访客请求了。
Serverless框架配置调整
把你的authorizer配置里的identitySource改成下面这样,同时指定授权器类型为request(因为我们需要访问请求上下文参数):
functions: hello: handler: handler.hello events: - http: path: /hello method: get private: true authorizer: identitySource: method.request.header.Authorization,context.requestId name: custom-authorizer type: request # 必须指定为request类型才能访问上下文参数 custom-authorizer: handler: authorizer.handler
授权器Lambda逻辑修改
在授权器里,我们只需要判断Authorization头是否存在:
- 如果不存在,直接返回Allow策略,让API Gateway放行请求到业务Lambda
- 如果存在,就正常验证JWT,返回Allow或Deny策略
示例代码(Node.js):
exports.handler = async (event) => { const authHeader = event.headers?.Authorization; // 访客模式:没有Authorization头,直接允许访问 if (!authHeader) { return generatePolicy('visitor', event.methodArn, 'Allow'); } // 有授权头,执行JWT验证逻辑 try { // 替换成你的JWT验证逻辑,比如用jsonwebtoken库解码验证 const decodedToken = verifyYourJwtToken(authHeader); // 验证通过,允许访问 return generatePolicy(decodedToken.userId, event.methodArn, 'Allow'); } catch (error) { // 验证失败,拒绝访问 return generatePolicy('unauthorized', event.methodArn, 'Deny'); } }; // 生成IAM策略的辅助函数 function generatePolicy(principalId, resource, effect) { return { principalId: principalId, policyDocument: { Version: '2012-10-17', Statement: [ { Action: 'execute-api:Invoke', Effect: effect, Resource: resource } ] } }; }
这个方案的好处是保持了授权逻辑和业务逻辑的分离,符合Custom Authorizer的设计初衷,同时完美实现了你的访客模式需求。
方案二:在业务Lambda中统一处理授权(你提到的变通方案)
如果觉得维护单独的授权器Lambda太繁琐,也可以直接移除Custom Authorizer,把授权逻辑嵌入到业务Lambda的handler里:
示例代码(Node.js):
exports.hello = async (event) => { const authHeader = event.headers?.Authorization; // 有授权头时验证JWT if (authHeader) { try { const decodedToken = verifyYourJwtToken(authHeader); // 这里可以把解码后的用户信息传递给后续业务逻辑 } catch (error) { return { statusCode: 401, body: JSON.stringify({ message: 'Invalid or expired token' }) }; } } // 访客模式或授权通过,执行业务逻辑 return { statusCode: 200, body: JSON.stringify({ message: 'Hello from Lambda!' }) }; };
这个方案配置简单,但缺点是授权逻辑和业务逻辑耦合在一起,后续如果要修改授权规则或者扩展其他API,维护成本会更高。
一些注意事项
- 方案一中,一定要把授权器类型指定为
request,否则API Gateway不会传递上下文参数,授权器还是不会被触发。 - 验证JWT时,务必使用正确的密钥和签名算法,避免出现安全漏洞。
- 如果你的API有多个资源路径,方案一的授权器可以复用,而方案二则需要在每个业务Lambda里重复编写授权逻辑。
内容的提问来源于stack exchange,提问作者Arek Jaworski

