0

0

在Java中通过IAM权限访问AWS API Gateway:签名V4认证教程

聖光之護

聖光之護

发布时间:2025-12-03 19:50:00

|

185人浏览过

|

来源于php中文网

原创

在java中通过iam权限访问aws api gateway:签名v4认证教程

本教程旨在解决在Java应用中,使用核心HTTP客户端调用受IAM权限保护的AWS API Gateway时遇到的认证问题。文章详细阐述了AWS Signature Version 4(SigV4)认证机制,解释了手动实现SigV4的复杂性,并提供了使用AWS SDK for Java中的`AwsV4Signer`来签署`java.net.http.HttpRequest`的实用示例,确保请求能够成功通过IAM认证,从而安全地访问API Gateway。

1. 引言:API Gateway IAM认证挑战

当尝试使用Java的java.net.http.HttpClient库访问由AWS API Gateway托管并受IAM权限保护的API时,常见的错误是收到403 Forbidden响应,并伴随{"message":"Missing Authentication Token"}的错误信息。这表明请求未能提供有效的身份验证凭证。直接在HTTP请求头中简单地传递AWS访问密钥(Access Key ID)和秘密访问密钥(Secret Access Key)是无效的,因为AWS服务(包括API Gateway)要求通过AWS Signature Version 4(SigV4)过程来对请求进行签名以验证其合法性。

以下是尝试直接发送未签名请求的示例代码及其产生的错误:

import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpResponse;
import java.net.http.HttpRequest;

public class ApiGatewayClientExample {

  public static void main(String[] args) throws Exception {
    HttpClient client = HttpClient.newHttpClient();

    // 构建一个未签名的请求
    HttpRequest request = HttpRequest.newBuilder()
      .uri(URI.create("https://abc123.execute-api.us-east-1.amazonaws.com/prod/controlplane/model/a1"))
      .header("accept", "application/json")
      .GET() // 明确指定GET方法
      .build();

    HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());

    System.out.println("Response Status: " + response.statusCode());
    System.out.println("Response Body: " + response.body());
  }
}

运行上述代码,将得到403状态码和{"message":"Missing Authentication Token"}的响应体,这正是由于缺少SigV4签名。

立即学习Java免费学习笔记(深入)”;

2. AWS Signature Version 4 认证机制

AWS Signature Version 4 (SigV4) 是AWS用于验证所有对其服务的API请求的标准协议。它通过结合您的AWS访问密钥、秘密访问密钥、请求内容(包括HTTP方法、URI、请求头、请求体)以及请求时间戳来生成一个唯一的数字签名。这个签名随后作为Authorization头的一部分随请求发送。AWS服务在收到请求后,会使用相同的过程重新计算签名,并与请求中提供的签名进行比对,以验证请求的真实性和完整性。

SigV4的核心作用包括:

  • 身份验证: 证明请求是由拥有相应AWS凭证的实体发出的。
  • 数据完整性: 确保请求在传输过程中未被篡改。
  • 防止重放攻击: 通过时间戳和签名有效期限制,防止恶意方截获并重放请求。

3. 手动实现SigV4的复杂性与考量

SigV4的实现过程涉及多个复杂步骤,包括:

公文宝
公文宝

AI公文写作神器,一键生成合规材料

下载
  1. 创建规范请求 (Canonical Request): 将HTTP方法、规范URI、规范查询字符串、规范头部(按字母顺序排序并包含主机、内容类型、日期等)、规范签名的头部列表以及请求体哈希值组合成一个字符串。
  2. 创建待签名字符串 (String to Sign): 结合签名算法、请求日期、凭证范围(包括区域和AWS服务)、规范请求的哈希值。
  3. 计算签名密钥 (Signing Key): 通过一系列HMAC-SHA256操作,从秘密访问密钥、日期、区域和服务名称派生出最终的签名密钥。
  4. 计算最终签名 (Signature Calculation): 使用HMAC-SHA256算法,以签名密钥作为键,待签名字符串作为消息,计算出最终的签名。
  5. 构建授权头 (Authorization Header): 将签名、凭证范围、签名的头部列表等信息格式化为Authorization HTTP头。

手动实现这些步骤不仅工作量大,而且极易出错,任何微小的偏差都可能导致签名验证失败。因此,强烈建议利用AWS提供的SDK或相关库来处理SigV4签名。

4. 推荐方案:使用AWS SDK for Java

AWS SDK for Java(v2)提供了强大的工具和抽象层,可以自动处理复杂的SigV4签名过程。对于调用AWS服务,包括自定义的API Gateway端点,SDK是首选且最可靠的方法。虽然API Gateway没有一个直接的“Invoke Client”来调用自定义端点,但AWS SDK提供了一个AwsV4Signer,可以用来签署任何software.amazon.awssdk.http.SdkHttpRequest,甚至可以桥接签署java.net.http.HttpRequest。

本教程将演示如何使用AWS SDK的AwsV4Signer来签署java.net.http.HttpRequest,从而在保持使用核心HttpClient的同时,利用SDK的签名能力。

5. 示例代码:使用AWS SDK的AwsV4Signer签署请求

为了使用AwsV4Signer,您需要将AWS SDK v2的auth和regions模块添加到您的项目中。

Maven 依赖:

<dependencies>
    <dependency>
        <groupId>software.amazon.awssdk</groupId>
        <artifactId>auth</artifactId>
        <version>2.20.100</version> <!-- 使用最新稳定版本 -->
    </dependency>
    <dependency>
        <groupId>software.amazon.awssdk</groupId>
        <artifactId>regions</artifactId>
        <version>2.20.100</version> <!-- 使用最新稳定版本 -->
    </dependency>
    <dependency>
        <groupId>software.amazon.awssdk</groupId>
        <artifactId>http-client-spi</artifactId>
        <version>2.20.100</version>
    </dependency>
</dependencies>

Java 代码示例:

import software.amazon.awssdk.auth.credentials.AwsBasicCredentials;
import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider;
import software.amazon.awssdk.core.signer.Signer;
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.http.SdkHttpFullRequest;
import software.amazon.awssdk.http.SdkHttpMethod;
import software.amazon.awssdk.http.SdkHttpRequest;
import software.amazon.awssdk.core.signer.AwsV4Signer;
import software.amazon.awssdk.utils.BinaryUtils;

import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.time.Instant;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

public class SignedApiGatewayClient {

    public static void main(String[] args) throws Exception {
        // 1. 配置AWS凭证
        // 建议从环境变量或AWS凭证链中获取,此处为示例硬编码
        String accessKey = System.getenv("AWS_ACCESS_KEY_ID");
        String secretKey = System.getenv("AWS_SECRET_ACCESS_KEY");
        if (accessKey == null || secretKey == null) {
            System.err.println("Error: AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY environment variables must be set.");
            return;
        }

        AwsBasicCredentials credentials = AwsBasicCredentials.create(accessKey, secretKey);
        StaticCredentialsProvider credentialsProvider = StaticCredentialsProvider.create(credentials);

        // 2. 定义API Gateway端点和区域
        String endpoint = "https://abc123.execute-api.us-east-1.amazonaws.com/prod/controlplane/model/a1";
        Region region = Region.US_EAST_1;
        String serviceName = "execute-api"; // API Gateway的签名服务名称

        // 3. 构建原始的 java.net.http.HttpRequest
        URI uri = URI.create(endpoint);
        HttpRequest.Builder requestBuilder = HttpRequest.newBuilder()
                .uri(uri)
                .header("accept", "application/json")
                .GET(); // 或者 .POST(HttpRequest.BodyPublishers.ofString("{\"key\":\"value\"}")) 等

        HttpRequest originalHttpRequest = requestBuilder.build();

        // 4. 将 java.net.http.HttpRequest 转换为 SdkHttpRequest
        // SdkHttpRequest 是 AWS SDK 内部用于签名的抽象
        SdkHttpRequest.Builder sdkRequestBuilder = SdkHttpRequest.builder()
                .uri(uri)
                .method(SdkHttpMethod.fromValue(originalHttpRequest.method()));

        // 复制原始请求的头部
        originalHttpRequest.headers().map().forEach((name, values) -> {
            // 排除Host头,因为SigV4会重新计算并添加
            if (!name.equalsIgnoreCase("Host")) {
                sdkRequestBuilder.putHeader(name, values);
            }
        });

        // 如果有请求体,也需要处理
        // 注意:java.net.http.HttpRequest 的 BodyPublisher 无法直接转换为 SdkHttpRequest 的内容
        // 这里假设是GET请求或POST请求体已提前获取并转换为字节数组
        // 对于POST/PUT等有请求体的操作,需要额外处理请求体并计算其SHA256哈希
        // 简化示例,假设为GET请求或请求体为空
        // 如果有请求体,需要将其内容作为 input.contentStream() 或 input.content() 传入
        // 并在 sdkRequestBuilder 中设置 .contentHash(BinaryUtils.toFipsCompliantSha256(requestBodyBytes))
        // 例如:
        // byte[] requestBody = "{\"key\":\"value\"}".getBytes(StandardCharsets.UTF_8);
        // sdkRequestBuilder.contentHash(BinaryUtils.toFipsCompliantSha256(requestBody));
        // sdkRequestBuilder.content(AsyncRequestBody.fromBytes(requestBody)); // 如果是同步请求,直接用BytesSource

        SdkHttpFullRequest sdkHttpRequest = (SdkHttpFullRequest) sdkRequestBuilder.build();

        // 5. 使用 AwsV4Signer 签署请求
        Signer signer = AwsV4Signer.create();
        SdkHttpFullRequest signedSdkRequest = (SdkHttpFullRequest) signer.sign(sdkHttpRequest, credentialsProvider.resolveCredentials(), region, serviceName);

        // 6. 将签名的 SdkHttpRequest 的头部信息复制回 java.net.http.HttpRequest.Builder
        HttpRequest.Builder signedJavaHttpRequestBuilder = HttpRequest.newBuilder()
                .uri(signedSdkRequest.uri())
                .method(signedSdkRequest.method().name());

        signedSdkRequest.headers().forEach((name, values) -> {
            for (String value : values) {
                signedJavaHttpRequestBuilder.header(name, value);
            }
        });

        // 如果原始请求有请求体,这里也需要复制
        // 对于POST/PUT等,需要确保请求体内容在签名前后一致
        // 简单的GET请求则无需处理请求体
        // signedJavaHttpRequestBuilder.POST(HttpRequest.BodyPublishers.ofByteArray(requestBody));

        HttpRequest signedHttpRequest = signedJavaHttpRequestBuilder.build();

        // 7. 使用 java.net.http.HttpClient 发送签名的请求
        HttpClient client = HttpClient.newHttpClient();
        HttpResponse<String> response = client.send(signedHttpRequest, HttpResponse.BodyHandlers.ofString());

        System.out.println("Signed Request Response Status: " + response.statusCode());
        System.out.println("Signed Request Response Body: " + response.body());
    }
}

代码解释:

  1. 凭证配置: 通过AwsBasicCredentials和StaticCredentialsProvider配置您的AWS访问密钥和秘密访问密钥。在生产环境中,强烈建议使用AWS SDK提供的默认凭证链(例如,从环境变量、IAM角色或配置文件中自动加载),而不是硬编码
  2. 请求构建: 首先构建一个标准的java.net.http.HttpRequest,其中包含目标URI和任何非认证相关的头部。
  3. 转换为SdkHttpRequest: AwsV4Signer工作在AWS SDK的内部HTTP请求抽象SdkHttpRequest上。因此,需要将java.net.http.HttpRequest转换为SdkHttpRequest。
    • 重要提示: 在转换过程中,如果原始请求有请求体(如POST或PUT),您需要将请求体的内容提取出来,并在SdkHttpRequest.Builder中设置content()和contentHash()。contentHash()是请求体内容的SHA256哈希值,这是SigV4签名的一部分。
  4. 签名请求: 创建AwsV4Signer实例,并调用其sign()方法。此方法会返回一个新的SdkHttpFullRequest对象,其中包含了所有必要的Authorization头和其他SigV4相关的头(如X-Amz-Date)。
  5. 复制签名头: 将签名后的SdkHttpFullRequest中的所有头部信息复制回一个新的java.net.http.HttpRequest.Builder中。
  6. 发送请求: 使用原始的java.net.http.HttpClient发送这个带有SigV4签名的HttpRequest。

6. 注意事项与最佳实践

  • 凭证管理: 绝不应将AWS凭证硬编码到代码中。应优先使用环境变量(AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY)、AWS凭证文件(~/.aws/credentials)、IAM角色(适用于EC2实例或Lambda函数)或AWS SSO等方式安全地管理凭证。AWS SDK的DefaultCredentialsProvider会自动按优先级查找这些位置。
  • 区域(Region)匹配: 确保签名时使用的AWS区域(Region.US_EAST_1)与您的API Gateway部署所在的区域一致。
  • 服务名称(Service Name): 对于API Gateway,服务名称通常是execute-api。
  • 请求体哈希: 如果您的请求是POST、PUT等带有请求体的操作,务必在签名之前计算请求体的SHA256哈希值,并将其包含在SdkHttpRequest中。SigV4会使用这个哈希值来验证请求体的完整性。
  • 错误处理: 生产代码中应包含适当的错误处理机制,例如捕获SdkClientException或IOException,并根据响应状态码进行逻辑判断。
  • 日期和时间戳: SigV4对时间戳非常敏感,客户端和服务器之间的时间偏差不能超过5分钟。确保您的系统时间是准确的。AwsV4Signer会自动处理X-Amz-Date头。

7. 总结

在Java中通过IAM权限访问AWS API Gateway,核心在于正确实现AWS Signature Version 4认证。虽然手动实现SigV4非常复杂,但AWS SDK for Java提供了AwsV4Signer等工具,极大地简化了这一过程。通过将java.net.http.HttpRequest转换为SdkHttpRequest,利用SDK进行签名,然后将签名后的头部复制回原始的HttpRequest,您可以在保持使用核心HTTP客户端的同时,成功地向IAM保护的API Gateway发起认证请求。在实际项目中,始终遵循AWS的安全最佳实践来管理您的凭证。

热门AI工具

更多
DeepSeek
DeepSeek

幻方量化公司旗下的开源大模型平台

豆包大模型
豆包大模型

字节跳动自主研发的一系列大型语言模型

通义千问
通义千问

阿里巴巴推出的全能AI助手

腾讯元宝
腾讯元宝

腾讯混元平台推出的AI助手

文心一言
文心一言

文心一言是百度开发的AI聊天机器人,通过对话可以生成各种形式的内容。

讯飞写作
讯飞写作

基于讯飞星火大模型的AI写作工具,可以快速生成新闻稿件、品宣文案、工作总结、心得体会等各种文文稿

即梦AI
即梦AI

一站式AI创作平台,免费AI图片和视频生成。

ChatGPT
ChatGPT

最最强大的AI聊天机器人程序,ChatGPT不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。

相关专题

更多
504 gateway timeout怎么解决
504 gateway timeout怎么解决

504 gateway timeout的解决办法:1、检查服务器负载;2、优化查询和代码;3、增加超时限制;4、检查代理服务器;5、检查网络连接;6、使用负载均衡;7、监控和日志;8、故障排除;9、增加缓存;10、分析请求。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

607

2023.11.27

default gateway怎么配置
default gateway怎么配置

配置default gateway的步骤:1、了解网络环境;2、获取路由器IP地址;3、登录路由器管理界面;4、找到并配置WAN口设置;5、配置默认网关;6、保存设置并退出;7、检查网络连接是否正常。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

235

2023.12.07

Java Maven专题
Java Maven专题

本专题聚焦 Java 主流构建工具 Maven 的学习与应用,系统讲解项目结构、依赖管理、插件使用、生命周期与多模块项目配置。通过企业管理系统、Web 应用与微服务项目实战,帮助学员全面掌握 Maven 在 Java 项目构建与团队协作中的核心技能。

0

2025.09.15

string转int
string转int

在编程中,我们经常会遇到需要将字符串(str)转换为整数(int)的情况。这可能是因为我们需要对字符串进行数值计算,或者需要将用户输入的字符串转换为整数进行处理。php中文网给大家带来了相关的教程以及文章,欢迎大家前来学习阅读。

1010

2023.08.02

登录token无效
登录token无效

登录token无效解决方法:1、检查token的有效期限,如果token已经过期,需要重新获取一个新的token;2、检查token的签名,如果签名不正确,需要重新获取一个新的token;3、检查密钥的正确性,如果密钥不正确,需要重新获取一个新的token;4、使用HTTPS协议传输token,建议使用HTTPS协议进行传输 ;5、使用双因素认证,双因素认证可以提高账户的安全性。

6605

2023.09.14

登录token无效怎么办
登录token无效怎么办

登录token无效的解决办法有检查Token是否过期、检查Token是否正确、检查Token是否被篡改、检查Token是否与用户匹配、清除缓存或Cookie、检查网络连接和服务器状态、重新登录或请求新的Token、联系技术支持或开发人员等。本专题为大家提供token相关的文章、下载、课程内容,供大家免费下载体验。

842

2023.09.14

token怎么获取
token怎么获取

获取token值的方法:1、小程序调用“wx.login()”获取 临时登录凭证code,并回传到开发者服务器;2、开发者服务器以code换取,用户唯一标识openid和会话密钥“session_key”。想了解更详细的内容,可以阅读本专题下面的文章。

1092

2023.12.21

token什么意思
token什么意思

token是一种用于表示用户权限、记录交易信息、支付虚拟货币的数字货币。可以用来在特定的网络上进行交易,用来购买或出售特定的虚拟货币,也可以用来支付特定的服务费用。想了解更多token什么意思的相关内容可以访问本专题下面的文章。

2102

2024.03.01

Go高并发任务调度与Goroutine池化实践
Go高并发任务调度与Goroutine池化实践

本专题围绕 Go 语言在高并发任务处理场景中的实践展开,系统讲解 Goroutine 调度模型、Channel 通信机制以及并发控制策略。内容包括任务队列设计、Goroutine 池化管理、资源限制控制以及并发任务的性能优化方法。通过实际案例演示,帮助开发者构建稳定高效的 Go 并发任务处理系统,提高系统在高负载环境下的处理能力与稳定性。

4

2026.03.10

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
Kotlin 教程
Kotlin 教程

共23课时 | 4.3万人学习

C# 教程
C# 教程

共94课时 | 11.1万人学习

Java 教程
Java 教程

共578课时 | 80.2万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号