All Products
Search
Document Center

ID Verification:IDV server-side sample code

Last Updated:Jun 22, 2026

To obtain integration sample code, see the Use OpenAPI to debug and integrate server-side APIs document and its Obtain sample code and perform online debugging section on the Alibaba Cloud OpenAPI Portal. This topic also provides ready-to-run code samples.

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);
                }
            }
        }
    }