All Products
Search
Document Center

ID Verification:IDV server-side sample code

Last Updated:Nov 18, 2025

We recommend that you use the Use OpenAPI to debug and integrate server-side APIs document and its Obtain sample code and perform online debugging section to obtain integration sample code from the Alibaba Cloud OpenAPI Portal. This document also provides code samples that you can run directly.

Sample code overview

Programming language

Integration method

Credential initialization method

Sample code

Java

SDK

Using the AccessKey of a RAM user

Java+SDK+AK

Using OIDCRoleArn

SDK+OIDCRoleArn

Native HTTPS (not recommended)

Using the AccessKey of a RAM user

Native HTTP+AK

Using OIDCRoleArn

Native HTTP+OIDCRoleArn

Java+SDK+AK

  • Programming language: Java

  • Integration method: SDK

  • Credential initialization method: Using the AccessKey of a RAM user

  • Dependencies:

    <dependency>
       <groupId>com.aliyun</groupId>
       <artifactId>cloudauth_intl20220809</artifactId>
       <version>***</version> // To get the latest version, visit the SDK Center: https://next.api.alibabacloud.com/api-tools/sdk/Cloudauth-intl?version=2022-08-09&language=java-tea&tab=primer-doc
    </dependency>
  • Sample code

    import com.aliyun.cloudauth_intl20220809.Client;
    import com.aliyun.cloudauth_intl20220809.models.InitializeRequest;
    import com.aliyun.cloudauth_intl20220809.models.InitializeResponse;
    import com.aliyun.cloudauth_intl20220809.models.InitializeResponseBody;
    import com.aliyun.teaopenapi.models.Config;
    
    public class Sample {
    
        private static Client client = null;
    
        static {
            try {
                // Build the client.
                client =  createClient();
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }
    
        public static void main(String[] args) throws Exception {
            try {
                // 1. Construct the request parameters. This example uses the Initialize operation. Replace it with the operation you need.
                InitializeRequest request = new InitializeRequest();
                request.setProductCode("***");
                request.setMerchantBizId("***");
                request.setMerchantUserId("***");
                request.setMetaInfo("***");
                request.setDocType("********");
                request.setAuthorize("*");
    
                // 2. Call the API operation. This example uses the Initialize operation. Replace it with the operation you need.
                InitializeResponse response = client.initialize(request);
    
                // 3. Get the call result.
                Integer statusCode = response.getStatusCode();// The status code of the call.
                String code = response.getBody().getCode();// The code of the call.
                InitializeResponseBody.InitializeResponseBodyResult result = response.getBody().getResult(); // The call result.
            } catch (Exception e) {
                // This is for printing and demonstration only. Handle exceptions with care. Do not ignore exceptions in your project.
                e.printStackTrace();
            }
        }
    
    
        /**
         * Build the client.
         * @return
         * @throws Exception
         */
        public static Client createClient() throws Exception {
            Config config = new Config();
            // Set the AccessKey pair of your account. Do not hard-code the AccessKey pair in your code. We recommend that you retrieve it from environment variables to avoid security vulnerabilities.
            config.setAccessKeyId("<your access key id>");
            config.setAccessKeySecret("<your access key secret>");
            // Set the endpoint and regionId. This example uses China (Hong Kong). Replace them with your actual endpoint and region ID.
            config.setEndpoint("cloudauth-intl.cn-hongkong.aliyuncs.com");
            config.setRegionId("cn-hongkong");
            return new Client(config);
        }
    }

SDK+OIDCRoleArn

  • Programming language: Java

  • Integration method: SDK

  • Credential initialization method: Using OIDCRoleArn

  • Dependencies:

    <dependency>
       <groupId>com.aliyun</groupId>
       <artifactId>credentials-java</artifactId>
       <version>***</version>
    </dependency> // For the latest version of credentials-java, see: https://central.sonatype.com/artifact/com.aliyun/credentials-java
      
    <dependency>
       <groupId>com.aliyun</groupId>
       <artifactId>cloudauth_intl20220809</artifactId>
       <version>***</version> // For the latest version of cloudauth_intl20220809, see: https://next.api.alibabacloud.com/api-tools/sdk/Cloudauth-intl?version=2022-08-09&language=java-tea&tab=primer-doc
    </dependency>
  • Sample code

    import com.aliyun.cloudauth_intl20220809.Client;
    import com.aliyun.cloudauth_intl20220809.models.InitializeRequest;
    import com.aliyun.cloudauth_intl20220809.models.InitializeResponse;
    import com.aliyun.cloudauth_intl20220809.models.InitializeResponseBody;
    import com.aliyun.teaopenapi.models.Config;
    
    public class Sample {
    
        private static Client client = null;
    
        static {
            try {
                // Build the client.
                client =  createClient();
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }
    
        public static void main(String[] args) throws Exception {
            try {
                // 1. Construct the request parameters. This example uses the Initialize operation. Replace it with the operation you need.
                InitializeRequest request = new InitializeRequest();
                request.setProductCode("***");
                request.setMerchantBizId("***");
                request.setMerchantUserId("***");
                request.setMetaInfo("***");
                request.setDocType("********");
                request.setAuthorize("*");
    
                // 2. Call the API operation. This example uses the Initialize operation. Replace it with the operation you need.
                InitializeResponse response = client.initialize(request);
    
                // 3. Get the call result.
                Integer statusCode = response.getStatusCode();// The status code.
                String code = response.getBody().getCode();// The code.
                InitializeResponseBody.InitializeResponseBodyResult result = response.getBody().getResult();// The detailed response.
            } catch (Exception e) {
                // This is for printing and demonstration only. Handle exceptions with care. Do not ignore exceptions in your project.
                e.printStackTrace();
            }
        }
    
        /**
         * Build the client.
         * @return
         * @throws Exception
         */
        public static Client createClient() throws Exception {
            Config config = new Config();
            // Set the credentials.
            config.setCredential(getCredentialClient());
            // Set the endpoint and regionId. This example uses China (Hong Kong). Replace them with your actual endpoint and region ID.
            config.setEndpoint("cloudauth-intl.cn-hongkong.aliyuncs.com");
            config.setRegionId("cn-hongkong");
            return new Client(config);
        }
    
        public static com.aliyun.credentials.Client getCredentialClient() throws Exception {
            com.aliyun.credentials.models.Config credentialConfig = new com.aliyun.credentials.models.Config();
            credentialConfig.setType("oidc_role_arn");
            // The Alibaba Cloud Resource Name (ARN) of the RAM role. You can set RoleArn using the ALIBABA_CLOUD_ROLE_ARN environment variable.
            credentialConfig.setRoleArn("<RoleArn>");
            // The ARN of the OIDC provider. You can set OidcProviderArn using the ALIBABA_CLOUD_OIDC_PROVIDER_ARN environment variable.
            credentialConfig.setOidcProviderArn("<OidcProviderArn>");
            // The path of the OIDC token file. You can set OidcTokenFilePath using the ALIBABA_CLOUD_OIDC_TOKEN_FILE environment variable.
            credentialConfig.setOidcTokenFilePath("<OidcTokenFilePath>");
            // The name of the role session. You can set RoleSessionName using the ALIBABA_CLOUD_ROLE_SESSION_NAME environment variable.
            credentialConfig.setRoleSessionName("<RoleSessionName>");
            // Set a more restrictive access policy. This parameter is optional. Example: {"Statement": [{"Action": ["*"],"Effect": "Allow","Resource": ["*"]}],"Version":"1"}
            credentialConfig.setPolicy("<Policy>");
            // Set the session expiration time.
            credentialConfig.setRoleSessionExpiration(3600);
            return new com.aliyun.credentials.Client(credentialConfig);
        }
    }

Native HTTP+AK

  • Programming language: Java

  • Integration method: Native HTTPS

  • Credential initialization method: Using the AccessKey of a RAM user

  • Dependencies:

    <dependency>
        <groupId>org.apache.httpcomponents</groupId>
        <artifactId>httpclient</artifactId>
        <version>4.5.13</version>
    </dependency>
    <dependency>
        <groupId>com.google.code.gson</groupId>
        <artifactId>gson</artifactId>
        <version>2.9.0</version>
    </dependency>
  • Sample code

    import com.google.gson.Gson;
    import com.google.gson.GsonBuilder;
    import org.apache.http.client.methods.CloseableHttpResponse;
    import org.apache.http.client.methods.HttpPost;
    import org.apache.http.client.methods.HttpUriRequest;
    import org.apache.http.client.utils.URIBuilder;
    import org.apache.http.entity.ByteArrayEntity;
    import org.apache.http.entity.ContentType;
    import org.apache.http.impl.client.CloseableHttpClient;
    import org.apache.http.impl.client.HttpClients;
    import org.apache.http.util.EntityUtils;
    
    import javax.crypto.Mac;
    import javax.crypto.spec.SecretKeySpec;
    import javax.xml.bind.DatatypeConverter;
    import java.io.IOException;
    import java.io.UnsupportedEncodingException;
    import java.net.URISyntaxException;
    import java.net.URLEncoder;
    import java.nio.charset.StandardCharsets;
    import java.security.MessageDigest;
    import java.text.SimpleDateFormat;
    import java.util.*;
    
    public class Sample {
    
        /**
         * Sample request
         * @param args
         * @throws IOException
         */
        public static void main(String[] args) throws IOException {
    
            // Set the API operation name. This example uses the Initialize operation. Replace it with the operation you need.
            String action= "Initialize";
    
            // The version number of the IDV API operation is fixed at 2022-08-09.
            String version=  "2022-08-09";
    
            // Set the endpoint. This example uses China (Hong Kong). Replace it with your actual endpoint.
            String host = "cloudauth-intl.cn-hongkong.aliyuncs.com";
    
            // Set the AccessKey pair of your account. Do not hard-code the AccessKey pair in your code. We recommend that you retrieve it from environment variables to avoid security vulnerabilities.
            String accessKey = "<your access key id>";
            String accessSecret = "<your access key secret>";
    
            // API request parameters. This example uses the Initialize operation. Replace them with the parameters for the operation you need.
            Map<String, String> params = new HashMap<>();
            params.put("MerchantBizId", "**********");
            params.put("ProductCode", "**********");
            params.put("MerchantUserId", "**********");
            params.put("MetaInfo", "**********");
    
            // 1. Build the service API request.
            CallRequest callRequest = buildIDVCallRequest(accessKey, accessSecret, action, version, host, params);
    
            // 2. Call the IDV service API.
            String result = callApi(callRequest);
    
            // 3. Print the result.
            System.out.println(result);
        }
    
        /**
         * Construct the request parameters for calling the IDV service. This is for reference only. Pass in parameters as needed.
         * @return
         */
        private static CallRequest buildIDVCallRequest(String accessKeyId, String accessKeySecret, String action, String version, String host, Map<String, String> params) {
            // Request URL.
            String httpMethod = "POST";
            String canonicalUri = "/";
    
            CallRequest callRequest = new CallRequest(httpMethod, canonicalUri, host, action, version, accessKeyId, accessKeySecret, null);
    
            Gson gson = (new GsonBuilder()).disableHtmlEscaping().create();
            callRequest.body = gson.toJson(params).getBytes(StandardCharsets.UTF_8);
            callRequest.headers.put("content-type", "application/json");
            // Calculate and set the signature.
            String authorization = getAuthorization(callRequest);
            callRequest.headers.put("Authorization", authorization);
            return callRequest;
        }
    
        private static String callApi(CallRequest callRequest) {
            try {
                // Send the request using HttpClient.
                String url = "https://" + callRequest.host + callRequest.canonicalUri;
                URIBuilder uriBuilder = new URIBuilder(url);
                // Add request parameters.
                for (Map.Entry<String, Object> entry : callRequest.queryParam.entrySet()) {
                    uriBuilder.addParameter(entry.getKey(), String.valueOf(entry.getValue()));
                }
                HttpUriRequest httpRequest;
    
                HttpPost httpPost = new HttpPost(uriBuilder.build());
                if (callRequest.body != null) {
                    httpPost.setEntity(new ByteArrayEntity(callRequest.body, ContentType.create(callRequest.headers.get("content-type"))));
                }
                httpRequest = httpPost;
    
                // Add HTTP request headers.
                for (Map.Entry<String, String> entry : callRequest.headers.entrySet()) {
                    httpRequest.addHeader(entry.getKey(), String.valueOf(entry.getValue()));
                }
                // Send the request.
                try (CloseableHttpClient httpClient = HttpClients.createDefault(); CloseableHttpResponse response = httpClient.execute(httpRequest)) {
                    String result = EntityUtils.toString(response.getEntity(), "UTF-8");
                    return result;
                } catch (IOException e) {
                    // Handle the exception.
                    System.out.println("Failed to send request");
                    e.printStackTrace();
                }
            } catch (URISyntaxException e) {
                // Handle the exception.
                System.out.println("Invalid URI syntax");
                e.printStackTrace();
            }
            return null;
        }
    
        /**
         * This method calculates and generates authorization information based on the HTTP request method, canonical URI, query parameters, and other input.
         */
        private static String getAuthorization(CallRequest callRequest) {
            try {
                // Flatten parameters in queryParam that have List or Map values.
                TreeMap<String, Object> newQueryParam = new TreeMap<>();
                processObject(newQueryParam, "", callRequest.queryParam);
                callRequest.queryParam = newQueryParam;
                // Step 1: Concatenate a canonical request string.
                // Request parameters. If the query string is empty, use an empty string as the canonical query string.
                StringBuilder canonicalQueryString = new StringBuilder();
                callRequest.queryParam.entrySet().stream().map(entry -> percentCode(entry.getKey()) + "="
                        + percentCode(String.valueOf(entry.getValue()))).forEachOrdered(queryPart -> {
                    // If canonicalQueryString is not empty, add an ampersand (&) before the query parameter.
                    if (canonicalQueryString.length() > 0) {
                        canonicalQueryString.append("&");
                    }
                    canonicalQueryString.append(queryPart);
                });
    
                // Calculate the hash of the request body.
                String requestPayload = ""; // Request body. If the request body is empty, such as in a GET request, RequestPayload is a fixed empty string.
                String hashedRequestPayload = callRequest.body != null ? sha256Hex(callRequest.body) : sha256Hex(requestPayload.getBytes(StandardCharsets.UTF_8));
                callRequest.headers.put("x-acs-content-sha256", hashedRequestPayload);
                // Construct the request headers. Multiple canonical headers are concatenated in ascending order of their lowercase header names.
                StringBuilder canonicalHeaders = new StringBuilder();
                // A list of signed headers. Multiple lowercase header names are sorted in alphabetical order and separated by semicolons (;).
                StringBuilder signedHeadersSb = new StringBuilder();
                callRequest.headers.entrySet().stream().filter(entry -> entry.getKey().toLowerCase().startsWith("x-acs-") || "host".equalsIgnoreCase(entry.getKey()) || "content-type".equalsIgnoreCase(entry.getKey())).sorted(Map.Entry.comparingByKey()).forEach(entry -> {
                    String lowerKey = entry.getKey().toLowerCase();
                    String value = String.valueOf(entry.getValue()).trim();
                    canonicalHeaders.append(lowerKey).append(":").append(value).append("\n");
                    signedHeadersSb.append(lowerKey).append(";");
                });
                String signedHeaders = signedHeadersSb.substring(0, signedHeadersSb.length() - 1);
                String canonicalRequest = callRequest.httpMethod + "\n" + callRequest.canonicalUri + "\n" + canonicalQueryString + "\n" + canonicalHeaders + "\n" + signedHeaders + "\n" + hashedRequestPayload;
    
                // Step 2: Concatenate the string to be signed.
                String hashedCanonicalRequest = sha256Hex(canonicalRequest.getBytes(StandardCharsets.UTF_8)); // Calculate the hash of the canonical request.
                String stringToSign = "ACS3-HMAC-SHA256" + "\n" + hashedCanonicalRequest;
    
                // Step 3: Calculate the signature.
                String signature = DatatypeConverter.printHexBinary(hmac256(callRequest.accessKeySecret.getBytes(StandardCharsets.UTF_8), stringToSign)).toLowerCase();
    
                // Step 4: Concatenate the Authorization header.
                return "ACS3-HMAC-SHA256" + " " + "Credential=" + callRequest.accessKeyId + ",SignedHeaders=" + signedHeaders + ",Signature=" + signature;
            } catch (Exception e) {
                // Handle the exception.
                e.printStackTrace();
                throw new RuntimeException("Get authorization failed.", e);
            }
        }
    
        /**
         * Recursively process an object to flatten complex objects, such as maps and lists, into key-value pairs.
         *
         * @param map   The original collection of key-value pairs, which will be recursively updated.
         * @param key   The key being processed. As recursion deepens, the key will contain nested path information.
         * @param value The value corresponding to the key, which can be a nested Map, List, or other type.
         */
        private static void processObject(Map<String, Object> map, String key, Object value) {
            // If the value is null, no further processing is needed.
            if (value == null) {
                return;
            }
            if (key == null) {
                key = "";
            }
            // If the value is a List, traverse each element in the list and process it recursively.
            if (value instanceof List<?>) {
                List<?> list = (List<?>) value;
                for (int i = 0; i < list.size(); ++i) {
                    processObject(map, key + "." + (i + 1), list.get(i));
                }
            } else if (value instanceof Map<?, ?>) {
                // If the value is a Map, traverse each key-value pair in the map and process it recursively.
                Map<?, ?> subMap = (Map<?, ?>) value;
                for (Map.Entry<?, ?> entry : subMap.entrySet()) {
                    processObject(map, key + "." + entry.getKey().toString(), entry.getValue());
                }
            } else {
                // For keys that start with a period (.), remove the leading period to maintain key continuity.
                if (key.startsWith(".")) {
                    key = key.substring(1);
                }
                // For values of the byte[] type, convert them to UTF-8 encoded strings.
                if (value instanceof byte[]) {
                    map.put(key, new String((byte[]) value, StandardCharsets.UTF_8));
                } else {
                    // For other types of values, convert them directly to strings.
                    map.put(key, String.valueOf(value));
                }
            }
        }
    
        /**
         * Generate a Message Authentication Code (MAC) using the HmacSHA256 algorithm.
         *
         * @param secretKey The secret key used to generate the MAC. It must be kept confidential.
         * @param str       The message to be authenticated with the MAC.
         * @return Returns the MAC calculated using the HmacSHA256 algorithm.
         * @throws Exception Throws an exception if an error occurs during MAC initialization or calculation.
         */
        public static byte[] hmac256(byte[] secretKey, String str) throws Exception {
            // Instantiate the HmacSHA256 MAC generator.
            Mac mac = Mac.getInstance("HmacSHA256");
            // Create a secret key specification to initialize the MAC generator.
            SecretKeySpec secretKeySpec = new SecretKeySpec(secretKey, mac.getAlgorithm());
            // Initialize the MAC generator.
            mac.init(secretKeySpec);
            // Calculate and return the MAC.
            return mac.doFinal(str.getBytes(StandardCharsets.UTF_8));
        }
    
        /**
         * Calculate the SHA-256 hash of a string and return it as a hexadecimal string.
         *
         * @param input The byte array to be hashed with SHA-256.
         * @return The result is a lowercase hexadecimal string.
         * @throws Exception If an error occurs while getting the SHA-256 message digest instance.
         */
        public static String sha256Hex(byte[] input) throws Exception {
            // Get a SHA-256 message digest instance.
            MessageDigest md = MessageDigest.getInstance("SHA-256");
            // Calculate the SHA-256 hash of the string s.
            byte[] d = md.digest(input);
            // Convert the hash value to a lowercase hexadecimal string and return it.
            return DatatypeConverter.printHexBinary(d).toLowerCase();
        }
    
        /**
         * URL-encode the specified string.
         * Encodes the string using the UTF-8 character set and replaces specific characters to comply with URL encoding standards.
         *
         * @param str The string to be URL-encoded.
         * @return The encoded string. The plus sign (+) is replaced with "%20", the asterisk (*) with "%2A", and the tilde "%7E" with "~".
         */
        public static String percentCode(String str) {
            if (str == null) {
                throw new IllegalArgumentException("The input string cannot be null.");
            }
            try {
                return URLEncoder.encode(str, "UTF-8").replace("+", "%20").replace("*", "%2A").replace("%7E", "~");
            } catch (UnsupportedEncodingException e) {
                throw new RuntimeException("UTF-8 encoding is not supported.", e);
            }
        }
    
    
        private static class CallRequest {
            // HTTP method
            private final String httpMethod;
            // The URI of the request. If the resource path is empty, use a forward slash (/) as the CanonicalURI.
            private final String canonicalUri;
            // The endpoint.
            private final String host;
            // The API operation name.
            private final String xAcsAction;
            // The API version.
            private final String xAcsVersion;
            // The AccessKey ID.
            private final String accessKeyId;
            // The AccessKey secret.
            private final String accessKeySecret;
            // The STS token. This parameter is empty for non-STS authentication.
            private final String securityToken;
            // The headers.
            TreeMap<String, String> headers = new TreeMap<>();
            // The byte array corresponding to the body parameter. If a request parameter is marked as "in":"body" or "in": "formData" in the metadata, it is placed in the body.
            byte[] body;
            // The query parameter. If a request parameter is marked as "in":"query" in the metadata, it is appended to the request URL.
            TreeMap<String, Object> queryParam = new TreeMap<>();
    
            public CallRequest(String httpMethod, String canonicalUri, String host, String xAcsAction, String xAcsVersion, String accessKeyId, String accessKeySecret, String securityToken) {
                this.httpMethod = httpMethod;
                this.canonicalUri = canonicalUri;
                this.host = host;
                this.xAcsAction = xAcsAction;
                this.xAcsVersion = xAcsVersion;
                this.accessKeyId = accessKeyId;
                this.accessKeySecret = accessKeySecret;
                this.securityToken = securityToken;
                initHeader();
            }
    
            // Initialize headers.
            private void initHeader() {
                headers.put("host", host);
                headers.put("x-acs-action", xAcsAction);
                headers.put("x-acs-version", xAcsVersion);
                SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
                sdf.setTimeZone(new SimpleTimeZone(0, "GMT")); // Set the time zone for date formatting to GMT.
                headers.put("x-acs-date", sdf.format(new Date()));
                headers.put("x-acs-signature-nonce", UUID.randomUUID().toString());
                if (securityToken != null) {
                    headers.put("x-acs-security-token", securityToken);
                }
            }
        }
    }
    

Native HTTP+OIDCRoleArn

  • Programming language: Java

  • Integration method: Native HTTPS

  • Credential initialization method: Using OIDCRoleArn

  • Dependencies:

    <dependency>
        <groupId>org.apache.httpcomponents</groupId>
        <artifactId>httpclient</artifactId>
        <version>4.5.13</version>
    </dependency>
    <dependency>
        <groupId>com.google.code.gson</groupId>
        <artifactId>gson</artifactId>
        <version>2.9.0</version>
    </dependency>
    <dependency>
        <groupId>com.aliyun</groupId>
        <artifactId>sts20150401</artifactId>
        <version>1.1.7</version>
    </dependency>
  • Sample code

    import com.aliyun.sts20150401.models.AssumeRoleWithOIDCResponse;
    import com.aliyun.sts20150401.models.AssumeRoleWithOIDCResponseBody;
    import com.aliyun.tea.TeaException;
    import com.google.gson.Gson;
    import com.google.gson.GsonBuilder;
    import org.apache.http.client.methods.*;
    import org.apache.http.client.utils.URIBuilder;
    import org.apache.http.entity.ByteArrayEntity;
    import org.apache.http.impl.client.CloseableHttpClient;
    import org.apache.http.impl.client.HttpClients;
    import org.apache.http.util.EntityUtils;
    import org.apache.http.entity.ContentType;
    
    import javax.crypto.Mac;
    import javax.crypto.spec.SecretKeySpec;
    import javax.xml.bind.DatatypeConverter;
    import java.io.IOException;
    import java.io.UnsupportedEncodingException;
    import java.net.URISyntaxException;
    import java.net.URLEncoder;
    import java.nio.charset.StandardCharsets;
    import java.security.MessageDigest;
    import java.text.SimpleDateFormat;
    import java.util.*;
    
    public class Sample {
    
        /**
         * The STS client, used to obtain a Security Token Service (STS) token.
         */
        private static com.aliyun.sts20150401.Client stsClient = null;
    
        static {
            com.aliyun.teaopenapi.models.Config config = new com.aliyun.teaopenapi.models.Config();
            // Set the endpoint for the STS service. This example uses Japan (Tokyo). Replace it with your actual endpoint.
            config.endpoint = "sts.ap-northeast-1.aliyuncs.com";
            try {
                stsClient = new com.aliyun.sts20150401.Client(config);
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }
    
        /**
         * Sample request
         * @param args
         * @throws IOException
         */
        public static void main(String[] args) throws IOException {
    
            // 1. Obtain a temporary Security Token Service (STS) token based on the OIDC role. The token is valid for a limited period. In a production environment, you can cache and reuse the token within its validity period.
    AssumeRoleWithOIDCResponseBody.AssumeRoleWithOIDCResponseBodyCredentials credentials = getSTSToken();
    
            // Set the API operation name. This example uses the Initialize operation. Replace it with the operation you need.
            String action= "Initialize";
    
            // The version number of the IDV API operation is fixed at 2022-08-09.
            String version=  "2022-08-09";
            // Set the endpoint. This example uses China (Hong Kong). Replace it with your actual endpoint.
            String host = "cloudauth-intl.cn-hongkong.aliyuncs.com";
    
            // API request parameters. This is for reference only. Pass in parameters as needed during actual development.
            Map<String, String> params = new HashMap<>();
            params.put("MerchantBizId", "**********");
            params.put("ProductCode", "**********");
            params.put("MerchantUserId", "**********");
            params.put("MetaInfo", "**********");
    
            // 2. Build the service API request.
            CallRequest callRequest = buildIDVCallRequest(credentials, action, version, host, params);
    
    
            // 3. Call the IDV service API.
            String result = callApi(callRequest);
    
            // 4. Print the result.
            System.out.println(result);
        }
    
        /**
         * Construct the request parameters for calling the IDV service. This is for reference only. Pass in parameters as needed.
         * @return
         */
        private static CallRequest buildIDVCallRequest(AssumeRoleWithOIDCResponseBody.AssumeRoleWithOIDCResponseBodyCredentials credentials, String action, String version, String host, Map<String, String> params) {
            // Request URL.
            String httpMethod = "POST";
            String canonicalUri = "/";
    
            CallRequest callRequest = new CallRequest(httpMethod, canonicalUri, host, action, version, credentials.getAccessKeyId(), credentials.getAccessKeySecret(), credentials.getSecurityToken());
    
            Gson gson = (new GsonBuilder()).disableHtmlEscaping().create();
            callRequest.body = gson.toJson(params).getBytes(StandardCharsets.UTF_8);
            callRequest.headers.put("content-type", "application/json");
            // Calculate and set the signature.
            String authorization = getAuthorization(callRequest);
            callRequest.headers.put("Authorization", authorization);
            return callRequest;
        }
    
    
        /**
         * Obtain temporary identity credentials (an STS token) by assuming a role.
         * For more information about STS, see https://www.alibabacloud.com/help/en/ram/product-overview/what-is-sts
         * This example uses the AssumeRoleWithOIDC method. You can also use other methods such as AssumeRole to obtain temporary identity credentials. For more information, see https://www.alibabacloud.com/help/en/ram/developer-reference/api-sts-2015-04-01-dir-role-assuming
         * @return
         */
        public static AssumeRoleWithOIDCResponseBody.AssumeRoleWithOIDCResponseBodyCredentials getSTSToken() {
            com.aliyun.sts20150401.models.AssumeRoleWithOIDCRequest assumeRoleWithOIDCRequest = new com.aliyun.sts20150401.models.AssumeRoleWithOIDCRequest()
                    .setOIDCProviderArn("***")
                    .setRoleArn("***")
                    .setDurationSeconds(3600L)
                    .setOIDCToken("***")
                    .setRoleSessionName("***");
            com.aliyun.teautil.models.RuntimeOptions runtime = new com.aliyun.teautil.models.RuntimeOptions();
            try {
                // If you copy the code to run, print the API return value yourself.
                AssumeRoleWithOIDCResponse assumeRoleWithOIDCResponse = stsClient.assumeRoleWithOIDCWithOptions(assumeRoleWithOIDCRequest, runtime);
                return assumeRoleWithOIDCResponse.body.credentials;
            } catch (TeaException error) {
                // This is for printing and demonstration only. Handle exceptions with care. Do not ignore exceptions in your project.
                // Error message.
                System.out.println(error.getMessage());
                // Diagnosis address.
                System.out.println(error.getData().get("Recommend"));
                com.aliyun.teautil.Common.assertAsString(error.message);
                throw new RuntimeException(error);
            } catch (Exception _error) {
                TeaException error = new TeaException(_error.getMessage(), _error);
                // This is for printing and demonstration only. Handle exceptions with care. Do not ignore exceptions in your project.
                // Error message.
                System.out.println(error.getMessage());
                // Diagnosis address.
                System.out.println(error.getData().get("Recommend"));
                com.aliyun.teautil.Common.assertAsString(error.message);
                throw new RuntimeException(error);
            }
        }
    
        private static String callApi(CallRequest callRequest) {
            try {
                // Send the request using HttpClient.
                String url = "https://" + callRequest.host + callRequest.canonicalUri;
                URIBuilder uriBuilder = new URIBuilder(url);
                // Add request parameters.
                for (Map.Entry<String, Object> entry : callRequest.queryParam.entrySet()) {
                    uriBuilder.addParameter(entry.getKey(), String.valueOf(entry.getValue()));
                }
                HttpUriRequest httpRequest;
    
                HttpPost httpPost = new HttpPost(uriBuilder.build());
                if (callRequest.body != null) {
                    httpPost.setEntity(new ByteArrayEntity(callRequest.body, ContentType.create(callRequest.headers.get("content-type"))));
                }
                httpRequest = httpPost;
    
                // Add HTTP request headers.
                for (Map.Entry<String, String> entry : callRequest.headers.entrySet()) {
                    httpRequest.addHeader(entry.getKey(), String.valueOf(entry.getValue()));
                }
                // Send the request.
                try (CloseableHttpClient httpClient = HttpClients.createDefault(); CloseableHttpResponse response = httpClient.execute(httpRequest)) {
                    String result = EntityUtils.toString(response.getEntity(), "UTF-8");
                    return result;
                } catch (IOException e) {
                    // Handle the exception.
                    System.out.println("Failed to send request");
                    e.printStackTrace();
                }
            } catch (URISyntaxException e) {
                // Handle the exception.
                System.out.println("Invalid URI syntax");
                e.printStackTrace();
            }
            return null;
        }
    
        /**
         * This method calculates and generates authorization information based on the HTTP request method, canonical URI, query parameters, and other input.
         */
        private static String getAuthorization(CallRequest callRequest) {
            try {
                // Flatten parameters in queryParam that have List or Map values.
                TreeMap<String, Object> newQueryParam = new TreeMap<>();
                processObject(newQueryParam, "", callRequest.queryParam);
                callRequest.queryParam = newQueryParam;
                // Step 1: Concatenate a canonical request string.
                // Request parameters. If the query string is empty, use an empty string as the canonical query string.
                StringBuilder canonicalQueryString = new StringBuilder();
                callRequest.queryParam.entrySet().stream().map(entry -> percentCode(entry.getKey()) + "="
                        + percentCode(String.valueOf(entry.getValue()))).forEachOrdered(queryPart -> {
                    // If canonicalQueryString is not empty, add an ampersand (&) before the query parameter.
                    if (canonicalQueryString.length() > 0) {
                        canonicalQueryString.append("&");
                    }
                    canonicalQueryString.append(queryPart);
                });
    
                // Calculate the hash of the request body.
                String requestPayload = ""; // Request body. If the request body is empty, such as in a GET request, RequestPayload is a fixed empty string.
                String hashedRequestPayload = callRequest.body != null ? sha256Hex(callRequest.body) : sha256Hex(requestPayload.getBytes(StandardCharsets.UTF_8));
                callRequest.headers.put("x-acs-content-sha256", hashedRequestPayload);
                // Construct the request headers. Multiple canonical headers are concatenated in ascending order of their lowercase header names.
                StringBuilder canonicalHeaders = new StringBuilder();
                // A list of signed headers. Multiple lowercase header names are sorted in alphabetical order and separated by semicolons (;).
                StringBuilder signedHeadersSb = new StringBuilder();
                callRequest.headers.entrySet().stream().filter(entry -> entry.getKey().toLowerCase().startsWith("x-acs-") || "host".equalsIgnoreCase(entry.getKey()) || "content-type".equalsIgnoreCase(entry.getKey())).sorted(Map.Entry.comparingByKey()).forEach(entry -> {
                    String lowerKey = entry.getKey().toLowerCase();
                    String value = String.valueOf(entry.getValue()).trim();
                    canonicalHeaders.append(lowerKey).append(":").append(value).append("\n");
                    signedHeadersSb.append(lowerKey).append(";");
                });
                String signedHeaders = signedHeadersSb.substring(0, signedHeadersSb.length() - 1);
                String canonicalRequest = callRequest.httpMethod + "\n" + callRequest.canonicalUri + "\n" + canonicalQueryString + "\n" + canonicalHeaders + "\n" + signedHeaders + "\n" + hashedRequestPayload;
    
                // Step 2: Concatenate the string to be signed.
                String hashedCanonicalRequest = sha256Hex(canonicalRequest.getBytes(StandardCharsets.UTF_8)); // Calculate the hash of the canonical request.
                String stringToSign = "ACS3-HMAC-SHA256" + "\n" + hashedCanonicalRequest;
    
                // Step 3: Calculate the signature.
                String signature = DatatypeConverter.printHexBinary(hmac256(callRequest.accessKeySecret.getBytes(StandardCharsets.UTF_8), stringToSign)).toLowerCase();
    
                // Step 4: Concatenate the Authorization header.
                return "ACS3-HMAC-SHA256" + " " + "Credential=" + callRequest.accessKeyId + ",SignedHeaders=" + signedHeaders + ",Signature=" + signature;
            } catch (Exception e) {
                // Handle the exception.
                e.printStackTrace();
                throw new RuntimeException("Get authorization failed.", e);
            }
        }
    
        /**
         * Recursively process an object to flatten complex objects, such as maps and lists, into key-value pairs.
         *
         * @param map   The original collection of key-value pairs, which will be recursively updated.
         * @param key   The key being processed. As recursion deepens, the key will contain nested path information.
         * @param value The value corresponding to the key, which can be a nested Map, List, or other type.
         */
        private static void processObject(Map<String, Object> map, String key, Object value) {
            // If the value is null, no further processing is needed.
            if (value == null) {
                return;
            }
            if (key == null) {
                key = "";
            }
            // If the value is a List, traverse each element in the list and process it recursively.
            if (value instanceof List<?>) {
                List<?> list = (List<?>) value;
                for (int i = 0; i < list.size(); ++i) {
                    processObject(map, key + "." + (i + 1), list.get(i));
                }
            } else if (value instanceof Map<?, ?>) {
                // If the value is a Map, traverse each key-value pair in the map and process it recursively.
                Map<?, ?> subMap = (Map<?, ?>) value;
                for (Map.Entry<?, ?> entry : subMap.entrySet()) {
                    processObject(map, key + "." + entry.getKey().toString(), entry.getValue());
                }
            } else {
                // For keys that start with a period (.), remove the leading period to maintain key continuity.
                if (key.startsWith(".")) {
                    key = key.substring(1);
                }
                // For values of the byte[] type, convert them to UTF-8 encoded strings.
                if (value instanceof byte[]) {
                    map.put(key, new String((byte[]) value, StandardCharsets.UTF_8));
                } else {
                    // For other types of values, convert them directly to strings.
                    map.put(key, String.valueOf(value));
                }
            }
        }
    
        /**
         * Generate a Message Authentication Code (MAC) using the HmacSHA256 algorithm.
         *
         * @param secretKey The secret key used to generate the MAC. It must be kept confidential.
         * @param str       The message to be authenticated with the MAC.
         * @return Returns the MAC calculated using the HmacSHA256 algorithm.
         * @throws Exception Throws an exception if an error occurs during MAC initialization or calculation.
         */
        public static byte[] hmac256(byte[] secretKey, String str) throws Exception {
            // Instantiate the HmacSHA256 MAC generator.
            Mac mac = Mac.getInstance("HmacSHA256");
            // Create a secret key specification to initialize the MAC generator.
            SecretKeySpec secretKeySpec = new SecretKeySpec(secretKey, mac.getAlgorithm());
            // Initialize the MAC generator.
            mac.init(secretKeySpec);
            // Calculate and return the MAC.
            return mac.doFinal(str.getBytes(StandardCharsets.UTF_8));
        }
    
        /**
         * Calculate the SHA-256 hash of a string and return it as a hexadecimal string.
         *
         * @param input The byte array to be hashed with SHA-256.
         * @return The result is a lowercase hexadecimal string.
         * @throws Exception If an error occurs while getting the SHA-256 message digest instance.
         */
        public static String sha256Hex(byte[] input) throws Exception {
            // Get a SHA-256 message digest instance.
            MessageDigest md = MessageDigest.getInstance("SHA-256");
            // Calculate the SHA-256 hash of the string s.
            byte[] d = md.digest(input);
            // Convert the hash value to a lowercase hexadecimal string and return it.
            return DatatypeConverter.printHexBinary(d).toLowerCase();
        }
    
        /**
         * URL-encode the specified string.
         * Encodes the string using the UTF-8 character set and replaces specific characters to comply with URL encoding standards.
         *
         * @param str The string to be URL-encoded.
         * @return The encoded string. The plus sign (+) is replaced with "%20", the asterisk (*) with "%2A", and the tilde "%7E" with "~".
         */
        public static String percentCode(String str) {
            if (str == null) {
                throw new IllegalArgumentException("The input string cannot be null.");
            }
            try {
                return URLEncoder.encode(str, "UTF-8").replace("+", "%20").replace("*", "%2A").replace("%7E", "~");
            } catch (UnsupportedEncodingException e) {
                throw new RuntimeException("UTF-8 encoding is not supported.", e);
            }
        }
    
    
        private static class CallRequest {
            // HTTP method
            private final String httpMethod;
            // The URI of the request. If the resource path is empty, use a forward slash (/) as the CanonicalURI.
            private final String canonicalUri;
            // The endpoint.
            private final String host;
            // The API operation name.
            private final String xAcsAction;
            // The API version.
            private final String xAcsVersion;
            // The AccessKey ID.
            private final String accessKeyId;
            // The AccessKey secret.
            private final String accessKeySecret;
            // The STS token. This parameter is empty for non-STS authentication.
            private final String securityToken;
            // The headers.
            TreeMap<String, String> headers = new TreeMap<>();
            // The byte array corresponding to the body parameter. If a request parameter is marked as "in":"body" or "in": "formData" in the metadata, it is placed in the body.
            byte[] body;
            // The query parameter. If a request parameter is marked as "in":"query" in the metadata, it is appended to the request URL.
            TreeMap<String, Object> queryParam = new TreeMap<>();
    
            public CallRequest(String httpMethod, String canonicalUri, String host, String xAcsAction, String xAcsVersion, String accessKeyId, String accessKeySecret, String securityToken) {
                this.httpMethod = httpMethod;
                this.canonicalUri = canonicalUri;
                this.host = host;
                this.xAcsAction = xAcsAction;
                this.xAcsVersion = xAcsVersion;
                this.accessKeyId = accessKeyId;
                this.accessKeySecret = accessKeySecret;
                this.securityToken = securityToken;
                initHeader();
            }
    
            // Initialize headers.
            private void initHeader() {
                headers.put("host", host);
                headers.put("x-acs-action", xAcsAction);
                headers.put("x-acs-version", xAcsVersion);
                SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
                sdf.setTimeZone(new SimpleTimeZone(0, "GMT")); // Set the time zone for date formatting to GMT.
                headers.put("x-acs-date", sdf.format(new Date()));
                headers.put("x-acs-signature-nonce", UUID.randomUUID().toString());
                if (securityToken != null) {
                    headers.put("x-acs-security-token", securityToken);
                }
            }
        }
    }