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

在Jersey ClientRequestFilter中获取请求实体实现AWS SigV4认证

Jersey Client实现AWS SigV4签名的可行方案

直接在ClientRequestFilter中完成实体序列化与SigV4签名的全流程是最简洁的方案,核心思路是利用Jersey的Providers组件获取对应实体的MessageBodyWriter,手动完成序列化,同时处理无实体的场景(用空字符串作为签名内容)。

实现步骤与代码示例

  1. 注入Providers组件:通过@Context注解注入Jersey的Providers,用于获取适配请求实体的MessageBodyWriter,保证手动序列化的结果与后续实际请求的序列化逻辑一致。
  2. 处理实体序列化:判断请求是否有实体,有则用对应的MessageBodyWriter序列化;无实体则直接使用空字符串。
  3. 生成并添加SigV4签名头:利用AWS SDK提供的签名工具生成SigV4签名,将Authorization等必要头信息添加到请求中。
import jakarta.ws.rs.client.ClientRequestContext;
import jakarta.ws.rs.client.ClientRequestFilter;
import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.MultivaluedMap;
import jakarta.ws.rs.ext.Providers;
import jakarta.annotation.Priority;
import jakarta.ws.rs.Priorities;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.Date;
import com.amazonaws.auth.AWS4Signer;
import com.amazonaws.auth.AWSCredentialsProvider;
import com.amazonaws.auth.DefaultAWSCredentialsProviderChain;
import com.amazonaws.http.HttpMethodName;

@Priority(Priorities.AUTHENTICATION)
public class AwsSigV4Filter implements ClientRequestFilter {

    private final AWSCredentialsProvider credentialsProvider;
    private final AWS4Signer signer;
    private final String serviceName;
    private final String region;

    @Context
    private Providers providers;

    public AwsSigV4Filter(String serviceName, String region) {
        this.credentialsProvider = DefaultAWSCredentialsProviderChain.getInstance();
        this.signer = new AWS4Signer();
        this.signer.setServiceName(serviceName);
        this.signer.setRegionName(region);
    }

    @Override
    public void filter(ClientRequestContext requestContext) throws IOException {
        // 处理请求实体,生成签名用的payload
        String payload;
        Object entity = requestContext.getEntity();
        
        if (entity != null) {
            // 确定请求的媒体类型,无指定则默认JSON
            MediaType mediaType = requestContext.getMediaType() != null ? 
                requestContext.getMediaType() : MediaType.APPLICATION_JSON_TYPE;
            
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            // 获取适配的MessageBodyWriter并序列化实体
            providers.getMessageBodyWriter(entity.getClass(), entity.getClass(), 
                null, mediaType).writeTo(entity, entity.getClass(), entity.getClass(), 
                null, mediaType, requestContext.getHeaders(), baos);
            
            payload = baos.toString(StandardCharsets.UTF_8);
        } else {
            // 无实体时使用空字符串作为签名内容
            payload = "";
        }

        // 准备AWS签名所需的请求参数
        HttpMethodName httpMethod = HttpMethodName.fromValue(requestContext.getMethod());
        String endpoint = requestContext.getUri().toString();
        MultivaluedMap<String, String> requestHeaders = requestContext.getHeaders();

        // 添加X-Amz-Date头(若未存在)
        if (!requestHeaders.containsKey("X-Amz-Date")) {
            requestHeaders.add("X-Amz-Date", new Date().toInstant().toString());
        }

        // 构建AWS HttpRequest对象用于签名
        com.amazonaws.http.HttpRequest awsHttpRequest = 
            new com.amazonaws.http.HttpRequest(httpMethod, endpoint);
        awsHttpRequest.setHeaders(requestHeaders);
        awsHttpRequest.setContent(payload.getBytes(StandardCharsets.UTF_8));

        // 执行SigV4签名
        signer.sign(awsHttpRequest, credentialsProvider.getCredentials());

        // 将签名结果写入请求头
        requestHeaders.putSingle("Authorization", 
            awsHttpRequest.getHeaders().get("Authorization"));
        
        // 处理临时凭证的安全令牌(若存在)
        String securityToken = awsHttpRequest.getHeaders().get("X-Amz-Security-Token");
        if (securityToken != null) {
            requestHeaders.putSingle("X-Amz-Security-Token", securityToken);
        }
    }
}

使用方式

将该Filter注册到Jersey Client即可:

import jakarta.ws.rs.client.Client;
import jakarta.ws.rs.client.ClientBuilder;

public class ClientSetup {
    public static void main(String[] args) {
        Client client = ClientBuilder.newClient();
        // 注册SigV4 Filter,指定服务名称(如neptune-db)和AWS区域
        client.register(new AwsSigV4Filter("neptune-db", "us-east-1"));
        
        // 后续使用client发起请求即可自动添加SigV4签名
    }
}

关键注意事项

  • AWS SDK版本:上述代码基于AWS SDK v1,若使用SDK v2,需替换为software.amazon.awssdk.auth.signer.Aws4Signer等对应类,签名逻辑需做适配。
  • 媒体类型适配:若你的应用使用非JSON的媒体类型(如XML),需确保Jersey已注册对应的MessageBodyWriter,否则手动序列化会失败。
  • Filter优先级:设置@Priority(Priorities.AUTHENTICATION)确保签名Filter在其他可能修改请求实体或头的Filter之后执行,保证签名基于最终的请求内容生成。

内容的提问来源于stack exchange,提问作者Martynas Jusevičius

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.10 07:58:30