Implementation:Apache Paimon DLFOpenApiSigner
| Knowledge Sources | |
|---|---|
| Domains | Authentication, Alibaba Cloud, OpenAPI, Request Signing |
| Last Updated | 2026-02-08 00:00 GMT |
Overview
DLFOpenApiSigner implements Alibaba Cloud OpenAPI signature authentication for DlfNext (2026-01-18) service endpoints using HMAC-SHA1.
Description
DLFOpenApiSigner is a specialized implementation of DLFRequestSigner designed for the newer Alibaba Cloud OpenAPI protocol, specifically targeting the DlfNext service (API version 2026-01-18). Unlike the default signer which uses HMAC-SHA256, this signer implements the ROA (Resource Oriented Architecture) signing mechanism using HMAC-SHA1, which is the standard for Aliyun OpenAPI services.
The signing process follows Alibaba Cloud's OpenAPI ROA signature specification, which differs significantly from the traditional DLF VPC endpoint protocol. The signer constructs headers in a specific format required by OpenAPI, including GMT-formatted Date header, Accept and Content-Type headers for JSON, and various x-acs-* headers for signature metadata (signature method, nonce, signature version, and API version).
The signature calculation involves building a canonicalized representation of the request. This includes canonicalizing x-acs-* headers by sorting them alphabetically and formatting them as key:value pairs; canonicalizing the resource path and query parameters by decoding URLs and sorting parameters; and constructing a StringToSign from the HTTP method, Accept, Content-MD5, Content-Type, Date, canonicalized headers, and canonicalized resource.
A critical aspect of this implementation is its careful handling of URL encoding. The canonicalized resource must use decoded values to ensure signature consistency, as different URL encoding schemes could produce different signatures for logically equivalent requests. The signer properly decodes both the resource path and query parameter values before including them in the signature calculation.
Usage
Use DLFOpenApiSigner when connecting to Alibaba Cloud DlfNext OpenAPI endpoints (identifiable by "dlfnext" in the hostname). This signer is automatically selected when the signing algorithm is set to "openapi" or when auto-detection determines the endpoint is a DlfNext service endpoint.
Code Reference
Source Location
- Repository: Apache_Paimon
- File: paimon-api/src/main/java/org/apache/paimon/rest/auth/DLFOpenApiSigner.java
Signature
public class DLFOpenApiSigner implements DLFRequestSigner {
public static final String IDENTIFIER = "openapi";
private static final String HMAC_SHA1 = "HmacSHA1";
private static final String DATE_HEADER = "Date";
private static final String ACCEPT_HEADER = "Accept";
private static final String CONTENT_MD5_HEADER = "Content-MD5";
private static final String CONTENT_TYPE_HEADER = "Content-Type";
private static final String HOST_HEADER = "Host";
private static final String X_ACS_SIGNATURE_METHOD = "x-acs-signature-method";
private static final String X_ACS_SIGNATURE_NONCE = "x-acs-signature-nonce";
private static final String X_ACS_SIGNATURE_VERSION = "x-acs-signature-version";
private static final String X_ACS_VERSION = "x-acs-version";
private static final String X_ACS_SECURITY_TOKEN = "x-acs-security-token";
private static final String SIGNATURE_METHOD_VALUE = "HMAC-SHA1";
private static final String SIGNATURE_VERSION_VALUE = "1.0";
private static final String API_VERSION = "2026-01-18";
@Override
public Map<String, String> signHeaders(
@Nullable String body, Instant now, @Nullable String securityToken, String host);
@Override
public String authorization(
RESTAuthParameter restAuthParameter,
DLFToken token,
String host,
Map<String, String> signHeaders) throws Exception;
@Override
public String identifier();
}
Import
import org.apache.paimon.rest.auth.DLFOpenApiSigner;
I/O Contract
Inputs
| Name | Type | Required | Description |
|---|---|---|---|
| body | String | No | Request body for MD5 calculation |
| now | Instant | Yes | Current timestamp for Date header (GMT format) |
| securityToken | String | No | Security token for temporary credentials |
| host | String | Yes | Host header value |
| restAuthParameter | RESTAuthParameter | Yes | Request parameters (path, method, query params) |
| token | DLFToken | Yes | DLF token containing access key ID and secret |
| signHeaders | Map<String, String> | Yes | Headers generated by signHeaders() method |
Outputs
| Name | Type | Description |
|---|---|---|
| signHeaders() | Map<String, String> | Returns headers including Date, Accept, Host, x-acs-* headers |
| authorization() | String | Returns Authorization header value: "acs <accessKeyId>:<signature>" |
| identifier() | String | Returns "openapi" as the signer identifier |
Usage Examples
import org.apache.paimon.rest.auth.DLFOpenApiSigner;
import org.apache.paimon.rest.auth.DLFToken;
import org.apache.paimon.rest.auth.RESTAuthParameter;
import java.time.Instant;
import java.util.HashMap;
import java.util.Map;
// Example 1: Create OpenAPI signer
DLFOpenApiSigner signer = new DLFOpenApiSigner();
// Example 2: Generate sign headers for GET request
Instant now = Instant.now();
Map<String, String> signHeaders = signer.signHeaders(
null, // no body for GET
now,
null, // no security token
"dlfnext.cn-hangzhou.aliyuncs.com"
);
// Sign headers include:
// Date: Wed, 08 Feb 2026 12:00:00 GMT
// Accept: application/json
// Host: dlfnext.cn-hangzhou.aliyuncs.com
// x-acs-signature-method: HMAC-SHA1
// x-acs-signature-nonce: <uuid>
// x-acs-signature-version: 1.0
// x-acs-version: 2026-01-18
// Example 3: Generate sign headers for POST request with body
String requestBody = "{\"name\":\"my_table\",\"schema\":{...}}";
Map<String, String> postHeaders = signer.signHeaders(
requestBody,
now,
null,
"dlfnext.cn-hangzhou.aliyuncs.com"
);
// Additional headers for POST:
// Content-MD5: <base64-encoded-md5>
// Content-Type: application/json
// Example 4: Generate sign headers with security token (STS)
Map<String, String> stsHeaders = signer.signHeaders(
null,
now,
"STS_security_token_value",
"dlfnext.cn-hangzhou.aliyuncs.com"
);
// Additional header:
// x-acs-security-token: STS_security_token_value
// Example 5: Generate authorization header
DLFToken token = new DLFToken("LTAI***", "secret***", null, null);
RESTAuthParameter authParam = new RESTAuthParameter(
"/api/v1/tables",
new HashMap<>(),
"GET",
""
);
String authorization = signer.authorization(
authParam,
token,
"dlfnext.cn-hangzhou.aliyuncs.com",
signHeaders
);
// Authorization format:
// acs LTAI***:<base64-hmac-sha1-signature>
// Example 6: Complete signing workflow for OpenAPI
public Map<String, String> signOpenApiRequest(
String method,
String path,
Map<String, String> queryParams,
String body,
DLFToken token,
String host) throws Exception {
DLFOpenApiSigner signer = new DLFOpenApiSigner();
Instant now = Instant.now();
// Step 1: Generate sign headers
Map<String, String> signHeaders = signer.signHeaders(
body,
now,
token.getSecurityToken(),
host
);
// Step 2: Create auth parameter
RESTAuthParameter authParam = new RESTAuthParameter(
path,
queryParams,
method,
body
);
// Step 3: Generate authorization
String authorization = signer.authorization(
authParam,
token,
host,
signHeaders
);
// Step 4: Combine all headers
Map<String, String> allHeaders = new HashMap<>(signHeaders);
allHeaders.put("Authorization", authorization);
return allHeaders;
}
// Example 7: POST request with query parameters
Map<String, String> queryParams = new HashMap<>();
queryParams.put("namespace", "default");
queryParams.put("createMode", "overwrite");
String postBody = "{\"schema\":{...}}";
Map<String, String> headers = signOpenApiRequest(
"POST",
"/api/v1/tables/my_table",
queryParams,
postBody,
token,
"dlfnext.cn-hangzhou.aliyuncs.com"
);
// Example 8: Request with special characters (automatically decoded for signature)
Map<String, String> specialHeaders = signOpenApiRequest(
"GET",
"/api/v1/tables/my_table%24partition", // URL-encoded path
new HashMap<>(),
"",
token,
"dlfnext.cn-hangzhou.aliyuncs.com"
);
// Path is decoded to "/api/v1/tables/my_table$partition" for signature
// Example 9: Compare with default signer
import org.apache.paimon.rest.auth.DLFDefaultSigner;
DLFDefaultSigner defaultSigner = new DLFDefaultSigner("cn-hangzhou");
DLFOpenApiSigner openApiSigner = new DLFOpenApiSigner();
System.out.println("Default signer: " + defaultSigner.identifier()); // "default"
System.out.println("OpenAPI signer: " + openApiSigner.identifier()); // "openapi"
// Default uses: DLF4-HMAC-SHA256 with region-specific credentials
// OpenAPI uses: HMAC-SHA1 with acs prefix
// Example 10: Handling query parameters with special characters
Map<String, String> complexParams = new HashMap<>();
complexParams.put("filter", "type=managed");
complexParams.put("name", "table with spaces");
// Query parameters are decoded for signature calculation:
// filter=type=managed&name=table with spaces
Map<String, String> complexHeaders = signOpenApiRequest(
"GET",
"/api/v1/tables",
complexParams,
"",
token,
"dlfnext.cn-hangzhou.aliyuncs.com"
);
// Example 11: Date header format
SimpleDateFormat dateFormat = new SimpleDateFormat(
"EEE, dd MMM yyyy HH:mm:ss 'GMT'",
Locale.ENGLISH
);
dateFormat.setTimeZone(TimeZone.getTimeZone("GMT"));
String dateHeader = dateFormat.format(Date.from(now));
// Example: "Wed, 08 Feb 2026 12:00:00 GMT"