This is an automated email from the ASF dual-hosted git repository.

FANNG1 pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/gravitino.git


The following commit(s) were added to refs/heads/main by this push:
     new 40dfd5358d [#10080] improvement(authz): `IcebergCatalog`, 
`PaimonCatalog` and `JdbcCatalog` support credential vending (#10081)
40dfd5358d is described below

commit 40dfd5358d0aa9ed289b17a0d1a75ba56b8936ad
Author: roryqi <[email protected]>
AuthorDate: Fri May 15 12:55:24 2026 +0800

    [#10080] improvement(authz): `IcebergCatalog`, `PaimonCatalog` and 
`JdbcCatalog` support credential vending (#10081)
    
    ### What changes were proposed in this pull request?
    
    `IcebergCatalog`, `PaimonCatalog` and `JdbcCatalog` support credential
    vending
    
    ### Why are the changes needed?
    
    Fix #10080
    
    ### Does this PR introduce _any_ user-facing change?
    
    No.
    
    ### How was this patch tested?
    
    Added IT.
    
    ---------
    
    Co-authored-by: diqiu50 <[email protected]>
---
 .../workflows/backend-integration-test-action.yml  |   1 +
 .../gravitino/credential/JdbcCredential.java       | 116 +++++++++++
 .../org.apache.gravitino.credential.Credential     |   1 +
 .../apache/gravitino/catalog/jdbc/JdbcCatalog.java |  27 +++
 .../catalog/jdbc/TestJdbcCatalogCredential.java    | 195 ++++++++++++++++++
 .../integration/test/CatalogMysqlCredentialIT.java | 163 +++++++++++++++
 .../catalog/lakehouse/iceberg/IcebergCatalog.java  |  43 ++++
 .../lakehouse/iceberg/TestIcebergCatalog.java      | 215 ++++++++++++++++++++
 .../integration/test/TestMultipleJDBCLoad.java     |  12 ++
 .../catalog/lakehouse/paimon/PaimonCatalog.java    |  43 ++++
 .../lakehouse/paimon/TestPaimonCatalog.java        | 220 +++++++++++++++++++++
 .../test/CatalogPaimonJdbcCredentialIT.java        | 155 +++++++++++++++
 .../apache/gravitino/client/RelationalCatalog.java |  15 +-
 .../credential/TestCredentialFactory.java          |  22 +++
 .../apache/gravitino/connector/BaseCatalog.java    |  54 ++++-
 .../credential/CredentialOperationDispatcher.java  |   2 +-
 .../credential/JdbcCredentialProvider.java         |  62 ++++++
 ....apache.gravitino.credential.CredentialProvider |   3 +-
 .../credential/TestJdbcCredentialProvider.java     | 193 ++++++++++++++++++
 ....apache.gravitino.credential.CredentialProvider |   1 +
 .../gravitino/integration/test/util/BaseIT.java    |  20 +-
 .../integration/test/util/TestDatabaseName.java    |   3 +
 22 files changed, 1556 insertions(+), 10 deletions(-)

diff --git a/.github/workflows/backend-integration-test-action.yml 
b/.github/workflows/backend-integration-test-action.yml
index fe1169001d..9e2239db13 100644
--- a/.github/workflows/backend-integration-test-action.yml
+++ b/.github/workflows/backend-integration-test-action.yml
@@ -104,3 +104,4 @@ jobs:
             catalogs-contrib/**/*.log
             catalogs-contrib/**/*.tar
             distribution/**/*.log
+            distribution/**/*.out
diff --git 
a/api/src/main/java/org/apache/gravitino/credential/JdbcCredential.java 
b/api/src/main/java/org/apache/gravitino/credential/JdbcCredential.java
new file mode 100644
index 0000000000..eeaccc2620
--- /dev/null
+++ b/api/src/main/java/org/apache/gravitino/credential/JdbcCredential.java
@@ -0,0 +1,116 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+
+package org.apache.gravitino.credential;
+
+import com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableMap;
+import java.util.Map;
+import org.apache.commons.lang3.StringUtils;
+
+/** JDBC credential for accessing JDBC backend services. */
+public class JdbcCredential implements Credential {
+
+  /** JDBC credential type. */
+  public static final String JDBC_CREDENTIAL_TYPE = "jdbc-user-password";
+  /** The JDBC user name. */
+  public static final String GRAVITINO_JDBC_USER = "jdbc-user";
+  /** The JDBC password. */
+  public static final String GRAVITINO_JDBC_PASSWORD = "jdbc-password";
+
+  private String jdbcUser;
+  private String jdbcPassword;
+
+  /**
+   * Constructs an instance of {@link JdbcCredential} with the JDBC user and 
password.
+   *
+   * @param jdbcUser The JDBC user name.
+   * @param jdbcPassword The JDBC password.
+   */
+  public JdbcCredential(String jdbcUser, String jdbcPassword) {
+    validate(jdbcUser, jdbcPassword, 0);
+    this.jdbcUser = jdbcUser;
+    this.jdbcPassword = jdbcPassword;
+  }
+
+  /**
+   * This is the constructor that is used by credential factory to create an 
instance of credential
+   * according to the credential information.
+   */
+  public JdbcCredential() {}
+
+  @Override
+  public String credentialType() {
+    return JDBC_CREDENTIAL_TYPE;
+  }
+
+  @Override
+  public long expireTimeInMs() {
+    return 0;
+  }
+
+  @Override
+  public Map<String, String> credentialInfo() {
+    return (new ImmutableMap.Builder<String, String>())
+        .put(GRAVITINO_JDBC_USER, jdbcUser)
+        .put(GRAVITINO_JDBC_PASSWORD, jdbcPassword)
+        .build();
+  }
+
+  @Override
+  public void initialize(Map<String, String> credentialInfo, long 
expireTimeInMs) {
+    String jdbcUser = credentialInfo.get(GRAVITINO_JDBC_USER);
+    String jdbcPassword = credentialInfo.get(GRAVITINO_JDBC_PASSWORD);
+    validate(jdbcUser, jdbcPassword, expireTimeInMs);
+    this.jdbcUser = jdbcUser;
+    this.jdbcPassword = jdbcPassword;
+  }
+
+  /**
+   * Get JDBC user name.
+   *
+   * @return The JDBC user name.
+   */
+  public String jdbcUser() {
+    return jdbcUser;
+  }
+
+  /**
+   * Get JDBC password.
+   *
+   * @return The JDBC password.
+   */
+  public String jdbcPassword() {
+    return jdbcPassword;
+  }
+
+  @Override
+  public String toString() {
+    return "JdbcCredential{jdbcUser='" + jdbcUser + "'}";
+  }
+
+  private void validate(String jdbcUser, String jdbcPassword, long 
expireTimeInMs) {
+    Preconditions.checkArgument(StringUtils.isNotBlank(jdbcUser), "JDBC user 
should not be empty");
+    Preconditions.checkArgument(
+        StringUtils.isNotBlank(jdbcPassword), "JDBC password should not be 
empty");
+    // JDBC credentials are static (no server-issued expiry). expireTimeInMs 
must always be 0.
+    Preconditions.checkArgument(
+        expireTimeInMs == 0, "The expire time of JdbcCredential should be 0");
+  }
+}
diff --git 
a/api/src/main/resources/META-INF/services/org.apache.gravitino.credential.Credential
 
b/api/src/main/resources/META-INF/services/org.apache.gravitino.credential.Credential
index 2cd80032a7..b940abd0b5 100644
--- 
a/api/src/main/resources/META-INF/services/org.apache.gravitino.credential.Credential
+++ 
b/api/src/main/resources/META-INF/services/org.apache.gravitino.credential.Credential
@@ -25,3 +25,4 @@ org.apache.gravitino.credential.OSSSecretKeyCredential
 org.apache.gravitino.credential.ADLSTokenCredential
 org.apache.gravitino.credential.AzureAccountKeyCredential
 org.apache.gravitino.credential.AwsIrsaCredential
+org.apache.gravitino.credential.JdbcCredential
diff --git 
a/catalogs/catalog-jdbc-common/src/main/java/org/apache/gravitino/catalog/jdbc/JdbcCatalog.java
 
b/catalogs/catalog-jdbc-common/src/main/java/org/apache/gravitino/catalog/jdbc/JdbcCatalog.java
index 34b6f6927a..2faf1f9693 100644
--- 
a/catalogs/catalog-jdbc-common/src/main/java/org/apache/gravitino/catalog/jdbc/JdbcCatalog.java
+++ 
b/catalogs/catalog-jdbc-common/src/main/java/org/apache/gravitino/catalog/jdbc/JdbcCatalog.java
@@ -18,8 +18,12 @@
  */
 package org.apache.gravitino.catalog.jdbc;
 
+import com.google.common.collect.Maps;
 import java.util.Collections;
 import java.util.Map;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.gravitino.annotation.Evolving;
+import org.apache.gravitino.catalog.jdbc.config.JdbcConfig;
 import 
org.apache.gravitino.catalog.jdbc.converter.JdbcColumnDefaultValueConverter;
 import org.apache.gravitino.catalog.jdbc.converter.JdbcExceptionConverter;
 import org.apache.gravitino.catalog.jdbc.converter.JdbcTypeConverter;
@@ -30,6 +34,8 @@ import org.apache.gravitino.connector.CatalogOperations;
 import org.apache.gravitino.connector.PropertiesMetadata;
 import org.apache.gravitino.connector.PropertyEntry;
 import org.apache.gravitino.connector.capability.Capability;
+import org.apache.gravitino.credential.CredentialConstants;
+import org.apache.gravitino.credential.JdbcCredential;
 
 /** Implementation of an Jdbc catalog in Gravitino. */
 public abstract class JdbcCatalog extends BaseCatalog<JdbcCatalog> {
@@ -113,4 +119,25 @@ public abstract class JdbcCatalog extends 
BaseCatalog<JdbcCatalog> {
   public PropertiesMetadata tablePropertiesMetadata() throws 
UnsupportedOperationException {
     return TABLE_PROPERTIES_META;
   }
+
+  @Override
+  @Evolving
+  public Map<String, String> propertiesWithCredentialProviders() {
+    Map<String, String> properties = 
Maps.newHashMap(super.propertiesWithCredentialProviders());
+    return applyDefaultCredentialProviders(properties);
+  }
+
+  private Map<String, String> applyDefaultCredentialProviders(Map<String, 
String> properties) {
+    if 
(StringUtils.isNotBlank(properties.get(CredentialConstants.CREDENTIAL_PROVIDERS)))
 {
+      return properties;
+    }
+
+    String jdbcUser = properties.get(JdbcConfig.USERNAME.getKey());
+    String jdbcPassword = properties.get(JdbcConfig.PASSWORD.getKey());
+    if (StringUtils.isNotBlank(jdbcUser) && 
StringUtils.isNotBlank(jdbcPassword)) {
+      properties.put(CredentialConstants.CREDENTIAL_PROVIDERS, 
JdbcCredential.JDBC_CREDENTIAL_TYPE);
+    }
+
+    return properties;
+  }
 }
diff --git 
a/catalogs/catalog-jdbc-common/src/test/java/org/apache/gravitino/catalog/jdbc/TestJdbcCatalogCredential.java
 
b/catalogs/catalog-jdbc-common/src/test/java/org/apache/gravitino/catalog/jdbc/TestJdbcCatalogCredential.java
new file mode 100644
index 0000000000..5c9b4a463b
--- /dev/null
+++ 
b/catalogs/catalog-jdbc-common/src/test/java/org/apache/gravitino/catalog/jdbc/TestJdbcCatalogCredential.java
@@ -0,0 +1,195 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.gravitino.catalog.jdbc;
+
+import com.google.common.collect.Maps;
+import java.time.Instant;
+import java.util.Map;
+import org.apache.gravitino.Namespace;
+import org.apache.gravitino.catalog.jdbc.config.JdbcConfig;
+import 
org.apache.gravitino.catalog.jdbc.converter.JdbcColumnDefaultValueConverter;
+import org.apache.gravitino.catalog.jdbc.converter.JdbcTypeConverter;
+import org.apache.gravitino.catalog.jdbc.operation.JdbcDatabaseOperations;
+import org.apache.gravitino.catalog.jdbc.operation.JdbcTableOperations;
+import org.apache.gravitino.credential.CredentialConstants;
+import org.apache.gravitino.credential.JdbcCredential;
+import org.apache.gravitino.meta.AuditInfo;
+import org.apache.gravitino.meta.CatalogEntity;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+/** Tests for JdbcCatalog credential provider functionality. */
+public class TestJdbcCatalogCredential {
+
+  /** A concrete implementation of JdbcCatalog for testing purposes. */
+  private static class TestableJdbcCatalog extends JdbcCatalog {
+
+    @Override
+    public String shortName() {
+      return "jdbc-test";
+    }
+
+    @Override
+    protected JdbcTypeConverter createJdbcTypeConverter() {
+      throw new UnsupportedOperationException("Not needed for credential 
test");
+    }
+
+    @Override
+    protected JdbcDatabaseOperations createJdbcDatabaseOperations() {
+      throw new UnsupportedOperationException("Not needed for credential 
test");
+    }
+
+    @Override
+    protected JdbcTableOperations createJdbcTableOperations() {
+      throw new UnsupportedOperationException("Not needed for credential 
test");
+    }
+
+    @Override
+    protected JdbcColumnDefaultValueConverter 
createJdbcColumnDefaultValueConverter() {
+      throw new UnsupportedOperationException("Not needed for credential 
test");
+    }
+  }
+
+  @Test
+  void testJdbcCatalogDefaultCredentialProviders() {
+    AuditInfo auditInfo =
+        
AuditInfo.builder().withCreator("creator").withCreateTime(Instant.now()).build();
+
+    // Test JDBC catalog with jdbc-user and jdbc-password
+    Map<String, String> jdbcProps = Maps.newHashMap();
+    jdbcProps.put(JdbcConfig.JDBC_URL.getKey(), 
"jdbc:mysql://localhost:3306/test");
+    jdbcProps.put(JdbcConfig.JDBC_DRIVER.getKey(), "com.mysql.cj.jdbc.Driver");
+    jdbcProps.put(JdbcConfig.USERNAME.getKey(), "test-user");
+    jdbcProps.put(JdbcConfig.PASSWORD.getKey(), "test-password");
+
+    CatalogEntity jdbcEntity =
+        CatalogEntity.builder()
+            .withId(1L)
+            .withName("jdbc-catalog")
+            .withNamespace(Namespace.of("metalake"))
+            .withType(TestableJdbcCatalog.Type.RELATIONAL)
+            .withProvider("jdbc-mysql")
+            .withAuditInfo(auditInfo)
+            .withProperties(jdbcProps)
+            .build();
+
+    TestableJdbcCatalog jdbcCatalog = new TestableJdbcCatalog();
+    jdbcCatalog.withCatalogConf(jdbcProps).withCatalogEntity(jdbcEntity);
+    Map<String, String> properties = 
jdbcCatalog.propertiesWithCredentialProviders();
+
+    // Should have jdbc credential provider
+    String credentialProviders = 
properties.get(CredentialConstants.CREDENTIAL_PROVIDERS);
+    Assertions.assertNotNull(credentialProviders);
+    Assertions.assertEquals(JdbcCredential.JDBC_CREDENTIAL_TYPE, 
credentialProviders);
+  }
+
+  @Test
+  void testJdbcCatalogNoCredentialProvidersWithoutPassword() {
+    AuditInfo auditInfo =
+        
AuditInfo.builder().withCreator("creator").withCreateTime(Instant.now()).build();
+
+    // Test JDBC catalog without password - should not add credential provider
+    Map<String, String> jdbcProps = Maps.newHashMap();
+    jdbcProps.put(JdbcConfig.JDBC_URL.getKey(), 
"jdbc:mysql://localhost:3306/test");
+    jdbcProps.put(JdbcConfig.JDBC_DRIVER.getKey(), "com.mysql.cj.jdbc.Driver");
+    jdbcProps.put(JdbcConfig.USERNAME.getKey(), "test-user");
+
+    CatalogEntity jdbcEntity =
+        CatalogEntity.builder()
+            .withId(2L)
+            .withName("jdbc-catalog-no-password")
+            .withNamespace(Namespace.of("metalake"))
+            .withType(TestableJdbcCatalog.Type.RELATIONAL)
+            .withProvider("jdbc-mysql")
+            .withAuditInfo(auditInfo)
+            .withProperties(jdbcProps)
+            .build();
+
+    TestableJdbcCatalog jdbcCatalog = new TestableJdbcCatalog();
+    jdbcCatalog.withCatalogConf(jdbcProps).withCatalogEntity(jdbcEntity);
+    Map<String, String> properties = 
jdbcCatalog.propertiesWithCredentialProviders();
+
+    // Should not have credential providers
+    String credentialProviders = 
properties.get(CredentialConstants.CREDENTIAL_PROVIDERS);
+    Assertions.assertNull(credentialProviders);
+  }
+
+  @Test
+  void testJdbcCatalogNoCredentialProvidersWithNeitherUserNorPassword() {
+    AuditInfo auditInfo =
+        
AuditInfo.builder().withCreator("creator").withCreateTime(Instant.now()).build();
+
+    // Test JDBC catalog with neither user nor password
+    Map<String, String> jdbcProps = Maps.newHashMap();
+    jdbcProps.put(JdbcConfig.JDBC_URL.getKey(), 
"jdbc:mysql://localhost:3306/test");
+    jdbcProps.put(JdbcConfig.JDBC_DRIVER.getKey(), "com.mysql.cj.jdbc.Driver");
+
+    CatalogEntity jdbcEntity =
+        CatalogEntity.builder()
+            .withId(4L)
+            .withName("jdbc-catalog-no-creds")
+            .withNamespace(Namespace.of("metalake"))
+            .withType(TestableJdbcCatalog.Type.RELATIONAL)
+            .withProvider("jdbc-mysql")
+            .withAuditInfo(auditInfo)
+            .withProperties(jdbcProps)
+            .build();
+
+    TestableJdbcCatalog jdbcCatalog = new TestableJdbcCatalog();
+    jdbcCatalog.withCatalogConf(jdbcProps).withCatalogEntity(jdbcEntity);
+    Map<String, String> properties = 
jdbcCatalog.propertiesWithCredentialProviders();
+
+    // Should not have credential providers
+    String credentialProviders = 
properties.get(CredentialConstants.CREDENTIAL_PROVIDERS);
+    Assertions.assertNull(credentialProviders);
+  }
+
+  @Test
+  void testJdbcCatalogExplicitCredentialProvidersNotOverridden() {
+    AuditInfo auditInfo =
+        
AuditInfo.builder().withCreator("creator").withCreateTime(Instant.now()).build();
+
+    // Test that explicit credential-providers setting is not overridden
+    Map<String, String> explicitProps = Maps.newHashMap();
+    explicitProps.put(JdbcConfig.JDBC_URL.getKey(), 
"jdbc:mysql://localhost:3306/test");
+    explicitProps.put(JdbcConfig.JDBC_DRIVER.getKey(), 
"com.mysql.cj.jdbc.Driver");
+    explicitProps.put(JdbcConfig.USERNAME.getKey(), "test-user");
+    explicitProps.put(JdbcConfig.PASSWORD.getKey(), "test-password");
+    explicitProps.put(CredentialConstants.CREDENTIAL_PROVIDERS, 
"custom-provider");
+
+    CatalogEntity explicitEntity =
+        CatalogEntity.builder()
+            .withId(3L)
+            .withName("explicit-catalog")
+            .withNamespace(Namespace.of("metalake"))
+            .withType(TestableJdbcCatalog.Type.RELATIONAL)
+            .withProvider("jdbc-mysql")
+            .withAuditInfo(auditInfo)
+            .withProperties(explicitProps)
+            .build();
+
+    TestableJdbcCatalog explicitCatalog = new TestableJdbcCatalog();
+    
explicitCatalog.withCatalogConf(explicitProps).withCatalogEntity(explicitEntity);
+    Map<String, String> properties = 
explicitCatalog.propertiesWithCredentialProviders();
+
+    // Should keep explicit credential providers, not override
+    String credentialProviders = 
properties.get(CredentialConstants.CREDENTIAL_PROVIDERS);
+    Assertions.assertEquals("custom-provider", credentialProviders);
+  }
+}
diff --git 
a/catalogs/catalog-jdbc-mysql/src/test/java/org/apache/gravitino/catalog/mysql/integration/test/CatalogMysqlCredentialIT.java
 
b/catalogs/catalog-jdbc-mysql/src/test/java/org/apache/gravitino/catalog/mysql/integration/test/CatalogMysqlCredentialIT.java
new file mode 100644
index 0000000000..4d909e98f8
--- /dev/null
+++ 
b/catalogs/catalog-jdbc-mysql/src/test/java/org/apache/gravitino/catalog/mysql/integration/test/CatalogMysqlCredentialIT.java
@@ -0,0 +1,163 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.gravitino.catalog.mysql.integration.test;
+
+import com.google.common.collect.Maps;
+import java.sql.SQLException;
+import java.util.Collections;
+import java.util.Map;
+import org.apache.gravitino.Catalog;
+import org.apache.gravitino.catalog.jdbc.config.JdbcConfig;
+import org.apache.gravitino.client.GravitinoMetalake;
+import org.apache.gravitino.credential.Credential;
+import org.apache.gravitino.credential.JdbcCredential;
+import org.apache.gravitino.integration.test.container.ContainerSuite;
+import org.apache.gravitino.integration.test.container.MySQLContainer;
+import org.apache.gravitino.integration.test.util.BaseIT;
+import org.apache.gravitino.integration.test.util.GravitinoITUtils;
+import org.apache.gravitino.integration.test.util.TestDatabaseName;
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Tag;
+import org.junit.jupiter.api.Test;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Integration test for MySQL catalog credential vending. Tests that JDBC 
credentials can be
+ * retrieved through the credential API.
+ */
+@Tag("gravitino-docker-test")
+public class CatalogMysqlCredentialIT extends BaseIT {
+
+  private static final Logger LOG = 
LoggerFactory.getLogger(CatalogMysqlCredentialIT.class);
+  private static final ContainerSuite containerSuite = 
ContainerSuite.getInstance();
+  private static final String provider = "jdbc-mysql";
+  private static final TestDatabaseName TEST_DB_NAME = 
TestDatabaseName.MYSQL_CATALOG_CREDENTIAL_IT;
+
+  private String metalakeName = 
GravitinoITUtils.genRandomName("mysql_credential_metalake");
+  private String catalogName = 
GravitinoITUtils.genRandomName("mysql_credential_catalog");
+  private GravitinoMetalake metalake;
+  private MySQLContainer mysqlContainer;
+
+  @BeforeAll
+  public void startIntegrationTest() {
+    // Do nothing - override to prevent auto start
+  }
+
+  @BeforeAll
+  public void startUp() throws Exception {
+    containerSuite.startMySQLContainer(TEST_DB_NAME);
+    mysqlContainer = containerSuite.getMySQLContainer();
+    super.startIntegrationTest();
+
+    Assertions.assertFalse(client.metalakeExists(metalakeName));
+    metalake = client.createMetalake(metalakeName, "metalake comment", 
Collections.emptyMap());
+    Assertions.assertTrue(client.metalakeExists(metalakeName));
+
+    createCatalog();
+  }
+
+  @AfterAll
+  public void tearDown() {
+    try {
+      if (metalake != null && metalake.catalogExists(catalogName)) {
+        metalake.disableCatalog(catalogName);
+        metalake.dropCatalog(catalogName, true);
+      }
+      if (client != null && client.metalakeExists(metalakeName)) {
+        client.disableMetalake(metalakeName);
+        client.dropMetalake(metalakeName, true);
+      }
+    } finally {
+      if (client != null) {
+        try {
+          client.close();
+        } catch (Exception e) {
+          LOG.error("Exception in closing client", e);
+        }
+        client = null;
+      }
+      try {
+        super.stopIntegrationTest();
+      } catch (Exception e) {
+        LOG.error("Exception in closing BaseIT", e);
+      }
+    }
+  }
+
+  @AfterAll
+  public void stopIntegrationTest() {
+    // Do nothing - override to prevent auto stop
+  }
+
+  private void createCatalog() throws SQLException {
+    Map<String, String> catalogProperties = Maps.newHashMap();
+    catalogProperties.put(
+        JdbcConfig.JDBC_URL.getKey(),
+        String.format(
+            "jdbc:mysql://%s:%d/",
+            mysqlContainer.getContainerIpAddress(), 
MySQLContainer.MYSQL_PORT));
+    catalogProperties.put(
+        JdbcConfig.JDBC_DRIVER.getKey(), 
mysqlContainer.getDriverClassName(TEST_DB_NAME));
+    catalogProperties.put(JdbcConfig.USERNAME.getKey(), 
mysqlContainer.getUsername());
+    catalogProperties.put(JdbcConfig.PASSWORD.getKey(), 
mysqlContainer.getPassword());
+
+    Catalog createdCatalog =
+        metalake.createCatalog(
+            catalogName,
+            Catalog.Type.RELATIONAL,
+            provider,
+            "MySQL catalog for credential testing",
+            catalogProperties);
+    Assertions.assertNotNull(createdCatalog);
+    Assertions.assertTrue(metalake.catalogExists(catalogName));
+  }
+
+  @Test
+  void testGetJdbcCredentialFromCatalog() {
+    Catalog catalog = metalake.loadCatalog(catalogName);
+    Credential[] credentials = catalog.supportsCredentials().getCredentials();
+
+    // Should have JDBC credential automatically configured
+    Assertions.assertEquals(1, credentials.length);
+    Assertions.assertInstanceOf(JdbcCredential.class, credentials[0]);
+
+    JdbcCredential jdbcCredential = (JdbcCredential) credentials[0];
+    Assertions.assertEquals(mysqlContainer.getUsername(), 
jdbcCredential.jdbcUser());
+    Assertions.assertEquals(mysqlContainer.getPassword(), 
jdbcCredential.jdbcPassword());
+    Assertions.assertEquals(0, jdbcCredential.expireTimeInMs());
+    Assertions.assertEquals(JdbcCredential.JDBC_CREDENTIAL_TYPE, 
jdbcCredential.credentialType());
+  }
+
+  @Test
+  void testGetJdbcCredentialByType() {
+    Catalog catalog = metalake.loadCatalog(catalogName);
+    Credential credential =
+        
catalog.supportsCredentials().getCredential(JdbcCredential.JDBC_CREDENTIAL_TYPE);
+
+    Assertions.assertNotNull(credential);
+    Assertions.assertInstanceOf(JdbcCredential.class, credential);
+
+    JdbcCredential jdbcCredential = (JdbcCredential) credential;
+    Assertions.assertEquals(mysqlContainer.getUsername(), 
jdbcCredential.jdbcUser());
+    Assertions.assertEquals(mysqlContainer.getPassword(), 
jdbcCredential.jdbcPassword());
+  }
+}
diff --git 
a/catalogs/catalog-lakehouse-iceberg/src/main/java/org/apache/gravitino/catalog/lakehouse/iceberg/IcebergCatalog.java
 
b/catalogs/catalog-lakehouse-iceberg/src/main/java/org/apache/gravitino/catalog/lakehouse/iceberg/IcebergCatalog.java
index 2e838e2be5..9dc1d53414 100644
--- 
a/catalogs/catalog-lakehouse-iceberg/src/main/java/org/apache/gravitino/catalog/lakehouse/iceberg/IcebergCatalog.java
+++ 
b/catalogs/catalog-lakehouse-iceberg/src/main/java/org/apache/gravitino/catalog/lakehouse/iceberg/IcebergCatalog.java
@@ -18,11 +18,18 @@
  */
 package org.apache.gravitino.catalog.lakehouse.iceberg;
 
+import com.google.common.collect.Maps;
+import java.util.ArrayList;
+import java.util.List;
 import java.util.Map;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.gravitino.annotation.Evolving;
 import org.apache.gravitino.connector.BaseCatalog;
 import org.apache.gravitino.connector.CatalogOperations;
 import org.apache.gravitino.connector.PropertiesMetadata;
 import org.apache.gravitino.connector.capability.Capability;
+import org.apache.gravitino.credential.CredentialConstants;
+import org.apache.gravitino.credential.JdbcCredential;
 import org.apache.gravitino.rel.ViewCatalog;
 
 /** Implementation of an Apache Iceberg catalog in Apache Gravitino. */
@@ -81,4 +88,40 @@ public class IcebergCatalog extends 
BaseCatalog<IcebergCatalog> {
   public PropertiesMetadata schemaPropertiesMetadata() throws 
UnsupportedOperationException {
     return SCHEMA_PROPERTIES_META;
   }
+
+  @Override
+  @Evolving
+  public Map<String, String> propertiesWithCredentialProviders() {
+    Map<String, String> properties = 
Maps.newHashMap(super.propertiesWithCredentialProviders());
+    return applyDefaultCredentialProviders(properties);
+  }
+
+  private Map<String, String> applyDefaultCredentialProviders(Map<String, 
String> properties) {
+    // If credential providers already set, return as is
+    if 
(StringUtils.isNotBlank(properties.get(CredentialConstants.CREDENTIAL_PROVIDERS)))
 {
+      return properties;
+    }
+
+    List<String> credentialProviders = new ArrayList<>();
+
+    // Add JDBC credential provider if backend is JDBC and 
jdbc-user/jdbc-password are set
+    String catalogBackend = properties.get(IcebergConstants.CATALOG_BACKEND);
+    if (catalogBackend != null
+        && IcebergCatalogBackend.JDBC.name().equalsIgnoreCase(catalogBackend)) 
{
+      String jdbcUser = properties.get(IcebergConstants.GRAVITINO_JDBC_USER);
+      String jdbcPassword = 
properties.get(IcebergConstants.GRAVITINO_JDBC_PASSWORD);
+      if (StringUtils.isNotBlank(jdbcUser) && 
StringUtils.isNotBlank(jdbcPassword)) {
+        credentialProviders.add(JdbcCredential.JDBC_CREDENTIAL_TYPE);
+      }
+    }
+
+    addStorageCredentialProviders(properties, credentialProviders);
+
+    if (!credentialProviders.isEmpty()) {
+      properties.put(
+          CredentialConstants.CREDENTIAL_PROVIDERS, String.join(",", 
credentialProviders));
+    }
+
+    return properties;
+  }
 }
diff --git 
a/catalogs/catalog-lakehouse-iceberg/src/test/java/org/apache/gravitino/catalog/lakehouse/iceberg/TestIcebergCatalog.java
 
b/catalogs/catalog-lakehouse-iceberg/src/test/java/org/apache/gravitino/catalog/lakehouse/iceberg/TestIcebergCatalog.java
index e6e2f20741..ca8fb57c34 100644
--- 
a/catalogs/catalog-lakehouse-iceberg/src/test/java/org/apache/gravitino/catalog/lakehouse/iceberg/TestIcebergCatalog.java
+++ 
b/catalogs/catalog-lakehouse-iceberg/src/test/java/org/apache/gravitino/catalog/lakehouse/iceberg/TestIcebergCatalog.java
@@ -30,10 +30,18 @@ import 
org.apache.gravitino.catalog.PropertiesMetadataHelpers;
 import org.apache.gravitino.connector.CatalogOperations;
 import org.apache.gravitino.connector.HasPropertyMetadata;
 import org.apache.gravitino.connector.PropertiesMetadata;
+import org.apache.gravitino.credential.AzureAccountKeyCredential;
+import org.apache.gravitino.credential.CredentialConstants;
+import org.apache.gravitino.credential.JdbcCredential;
+import org.apache.gravitino.credential.OSSSecretKeyCredential;
+import org.apache.gravitino.credential.S3SecretKeyCredential;
 import org.apache.gravitino.iceberg.common.ops.IcebergCatalogWrapper;
 import org.apache.gravitino.meta.AuditInfo;
 import org.apache.gravitino.meta.CatalogEntity;
 import org.apache.gravitino.rel.ViewCatalog;
+import org.apache.gravitino.storage.AzureProperties;
+import org.apache.gravitino.storage.OSSProperties;
+import org.apache.gravitino.storage.S3Properties;
 import org.apache.iceberg.rest.responses.ListNamespacesResponse;
 import org.junit.jupiter.api.Assertions;
 import org.junit.jupiter.api.Test;
@@ -224,4 +232,211 @@ public class TestIcebergCatalog {
     Assertions.assertNotNull(viewCatalog);
     Assertions.assertTrue(viewCatalog instanceof IcebergCatalogOperations);
   }
+
+  @Test
+  void testJdbcBackendDefaultCredentialProviders() {
+    AuditInfo auditInfo =
+        
AuditInfo.builder().withCreator("creator").withCreateTime(Instant.now()).build();
+
+    // Test JDBC backend with jdbc-user and jdbc-password
+    Map<String, String> jdbcProps = Maps.newHashMap();
+    jdbcProps.put(IcebergConstants.CATALOG_BACKEND, "jdbc");
+    jdbcProps.put(IcebergConstants.URI, "jdbc:sqlite::memory:");
+    jdbcProps.put(IcebergConstants.GRAVITINO_JDBC_USER, "test-user");
+    jdbcProps.put(IcebergConstants.GRAVITINO_JDBC_PASSWORD, "test-password");
+
+    CatalogEntity jdbcEntity =
+        CatalogEntity.builder()
+            .withId(1L)
+            .withName("jdbc-catalog")
+            .withNamespace(Namespace.of("metalake"))
+            .withType(IcebergCatalog.Type.RELATIONAL)
+            .withProvider("iceberg")
+            .withAuditInfo(auditInfo)
+            .withProperties(jdbcProps)
+            .build();
+
+    IcebergCatalog jdbcCatalog =
+        new 
IcebergCatalog().withCatalogConf(jdbcProps).withCatalogEntity(jdbcEntity);
+    Map<String, String> properties = 
jdbcCatalog.propertiesWithCredentialProviders();
+
+    // Should have jdbc credential provider
+    String credentialProviders = 
properties.get(CredentialConstants.CREDENTIAL_PROVIDERS);
+    Assertions.assertNotNull(credentialProviders);
+    
Assertions.assertTrue(credentialProviders.contains(JdbcCredential.JDBC_CREDENTIAL_TYPE));
+  }
+
+  @Test
+  void testJdbcBackendWithS3CredentialProviders() {
+    AuditInfo auditInfo =
+        
AuditInfo.builder().withCreator("creator").withCreateTime(Instant.now()).build();
+
+    // Test JDBC backend with jdbc-user, jdbc-password, and S3 credentials
+    Map<String, String> jdbcS3Props = Maps.newHashMap();
+    jdbcS3Props.put(IcebergConstants.CATALOG_BACKEND, "jdbc");
+    jdbcS3Props.put(IcebergConstants.URI, "jdbc:sqlite::memory:");
+    jdbcS3Props.put(IcebergConstants.GRAVITINO_JDBC_USER, "test-user");
+    jdbcS3Props.put(IcebergConstants.GRAVITINO_JDBC_PASSWORD, "test-password");
+    jdbcS3Props.put(S3Properties.GRAVITINO_S3_ACCESS_KEY_ID, "access-key");
+    jdbcS3Props.put(S3Properties.GRAVITINO_S3_SECRET_ACCESS_KEY, "secret-key");
+
+    CatalogEntity jdbcS3Entity =
+        CatalogEntity.builder()
+            .withId(2L)
+            .withName("jdbc-s3-catalog")
+            .withNamespace(Namespace.of("metalake"))
+            .withType(IcebergCatalog.Type.RELATIONAL)
+            .withProvider("iceberg")
+            .withAuditInfo(auditInfo)
+            .withProperties(jdbcS3Props)
+            .build();
+
+    IcebergCatalog jdbcS3Catalog =
+        new 
IcebergCatalog().withCatalogConf(jdbcS3Props).withCatalogEntity(jdbcS3Entity);
+    Map<String, String> properties = 
jdbcS3Catalog.propertiesWithCredentialProviders();
+
+    // Should have both jdbc and s3-secret-key credential providers
+    String credentialProviders = 
properties.get(CredentialConstants.CREDENTIAL_PROVIDERS);
+    Assertions.assertNotNull(credentialProviders);
+    
Assertions.assertTrue(credentialProviders.contains(JdbcCredential.JDBC_CREDENTIAL_TYPE));
+    Assertions.assertTrue(
+        
credentialProviders.contains(S3SecretKeyCredential.S3_SECRET_KEY_CREDENTIAL_TYPE));
+  }
+
+  @Test
+  void testNonJdbcBackendNoDefaultCredentialProviders() {
+    AuditInfo auditInfo =
+        
AuditInfo.builder().withCreator("creator").withCreateTime(Instant.now()).build();
+
+    // Test non-JDBC backend (hive) - should not add default credential 
providers
+    Map<String, String> hiveProps = Maps.newHashMap();
+    hiveProps.put(IcebergConstants.CATALOG_BACKEND, "hive");
+    hiveProps.put(IcebergConstants.URI, "thrift://localhost:9083");
+
+    CatalogEntity hiveEntity =
+        CatalogEntity.builder()
+            .withId(3L)
+            .withName("hive-catalog")
+            .withNamespace(Namespace.of("metalake"))
+            .withType(IcebergCatalog.Type.RELATIONAL)
+            .withProvider("iceberg")
+            .withAuditInfo(auditInfo)
+            .withProperties(hiveProps)
+            .build();
+
+    IcebergCatalog hiveCatalog =
+        new 
IcebergCatalog().withCatalogConf(hiveProps).withCatalogEntity(hiveEntity);
+    Map<String, String> properties = 
hiveCatalog.propertiesWithCredentialProviders();
+
+    // Should not have credential providers for non-JDBC backend
+    String credentialProviders = 
properties.get(CredentialConstants.CREDENTIAL_PROVIDERS);
+    Assertions.assertNull(credentialProviders);
+  }
+
+  @Test
+  void testExplicitCredentialProvidersNotOverridden() {
+    AuditInfo auditInfo =
+        
AuditInfo.builder().withCreator("creator").withCreateTime(Instant.now()).build();
+
+    // Test that explicit credential-providers setting is not overridden
+    Map<String, String> explicitProps = Maps.newHashMap();
+    explicitProps.put(IcebergConstants.CATALOG_BACKEND, "jdbc");
+    explicitProps.put(IcebergConstants.URI, "jdbc:sqlite::memory:");
+    explicitProps.put(IcebergConstants.GRAVITINO_JDBC_USER, "test-user");
+    explicitProps.put(IcebergConstants.GRAVITINO_JDBC_PASSWORD, 
"test-password");
+    explicitProps.put(CredentialConstants.CREDENTIAL_PROVIDERS, 
"custom-provider");
+
+    CatalogEntity explicitEntity =
+        CatalogEntity.builder()
+            .withId(4L)
+            .withName("explicit-catalog")
+            .withNamespace(Namespace.of("metalake"))
+            .withType(IcebergCatalog.Type.RELATIONAL)
+            .withProvider("iceberg")
+            .withAuditInfo(auditInfo)
+            .withProperties(explicitProps)
+            .build();
+
+    IcebergCatalog explicitCatalog =
+        new 
IcebergCatalog().withCatalogConf(explicitProps).withCatalogEntity(explicitEntity);
+    Map<String, String> properties = 
explicitCatalog.propertiesWithCredentialProviders();
+
+    // Should keep explicit credential providers, not override
+    String credentialProviders = 
properties.get(CredentialConstants.CREDENTIAL_PROVIDERS);
+    Assertions.assertEquals("custom-provider", credentialProviders);
+  }
+
+  @Test
+  void testJdbcBackendWithOSSCredentialProviders() {
+    AuditInfo auditInfo =
+        
AuditInfo.builder().withCreator("creator").withCreateTime(Instant.now()).build();
+
+    // Test JDBC backend with jdbc-user, jdbc-password, and OSS credentials
+    Map<String, String> jdbcOssProps = Maps.newHashMap();
+    jdbcOssProps.put(IcebergConstants.CATALOG_BACKEND, "jdbc");
+    jdbcOssProps.put(IcebergConstants.URI, "jdbc:sqlite::memory:");
+    jdbcOssProps.put(IcebergConstants.GRAVITINO_JDBC_USER, "test-user");
+    jdbcOssProps.put(IcebergConstants.GRAVITINO_JDBC_PASSWORD, 
"test-password");
+    jdbcOssProps.put(OSSProperties.GRAVITINO_OSS_ACCESS_KEY_ID, 
"oss-access-key");
+    jdbcOssProps.put(OSSProperties.GRAVITINO_OSS_ACCESS_KEY_SECRET, 
"oss-secret-key");
+
+    CatalogEntity jdbcOssEntity =
+        CatalogEntity.builder()
+            .withId(5L)
+            .withName("jdbc-oss-catalog")
+            .withNamespace(Namespace.of("metalake"))
+            .withType(IcebergCatalog.Type.RELATIONAL)
+            .withProvider("iceberg")
+            .withAuditInfo(auditInfo)
+            .withProperties(jdbcOssProps)
+            .build();
+
+    IcebergCatalog jdbcOssCatalog =
+        new 
IcebergCatalog().withCatalogConf(jdbcOssProps).withCatalogEntity(jdbcOssEntity);
+    Map<String, String> properties = 
jdbcOssCatalog.propertiesWithCredentialProviders();
+
+    // Should have both jdbc and oss-secret-key credential providers
+    String credentialProviders = 
properties.get(CredentialConstants.CREDENTIAL_PROVIDERS);
+    Assertions.assertNotNull(credentialProviders);
+    
Assertions.assertTrue(credentialProviders.contains(JdbcCredential.JDBC_CREDENTIAL_TYPE));
+    Assertions.assertTrue(
+        
credentialProviders.contains(OSSSecretKeyCredential.OSS_SECRET_KEY_CREDENTIAL_TYPE));
+  }
+
+  @Test
+  void testJdbcBackendWithAzureCredentialProviders() {
+    AuditInfo auditInfo =
+        
AuditInfo.builder().withCreator("creator").withCreateTime(Instant.now()).build();
+
+    // Test JDBC backend with jdbc-user, jdbc-password, and Azure credentials
+    Map<String, String> jdbcAzureProps = Maps.newHashMap();
+    jdbcAzureProps.put(IcebergConstants.CATALOG_BACKEND, "jdbc");
+    jdbcAzureProps.put(IcebergConstants.URI, "jdbc:sqlite::memory:");
+    jdbcAzureProps.put(IcebergConstants.GRAVITINO_JDBC_USER, "test-user");
+    jdbcAzureProps.put(IcebergConstants.GRAVITINO_JDBC_PASSWORD, 
"test-password");
+    jdbcAzureProps.put(AzureProperties.GRAVITINO_AZURE_STORAGE_ACCOUNT_NAME, 
"azure-account-name");
+    jdbcAzureProps.put(AzureProperties.GRAVITINO_AZURE_STORAGE_ACCOUNT_KEY, 
"azure-account-key");
+
+    CatalogEntity jdbcAzureEntity =
+        CatalogEntity.builder()
+            .withId(6L)
+            .withName("jdbc-azure-catalog")
+            .withNamespace(Namespace.of("metalake"))
+            .withType(IcebergCatalog.Type.RELATIONAL)
+            .withProvider("iceberg")
+            .withAuditInfo(auditInfo)
+            .withProperties(jdbcAzureProps)
+            .build();
+
+    IcebergCatalog jdbcAzureCatalog =
+        new 
IcebergCatalog().withCatalogConf(jdbcAzureProps).withCatalogEntity(jdbcAzureEntity);
+    Map<String, String> properties = 
jdbcAzureCatalog.propertiesWithCredentialProviders();
+
+    // Should have both jdbc and azure-account-key credential providers
+    String credentialProviders = 
properties.get(CredentialConstants.CREDENTIAL_PROVIDERS);
+    Assertions.assertNotNull(credentialProviders);
+    
Assertions.assertTrue(credentialProviders.contains(JdbcCredential.JDBC_CREDENTIAL_TYPE));
+    Assertions.assertTrue(
+        
credentialProviders.contains(AzureAccountKeyCredential.AZURE_ACCOUNT_KEY_CREDENTIAL_TYPE));
+  }
 }
diff --git 
a/catalogs/catalog-lakehouse-iceberg/src/test/java/org/apache/gravitino/catalog/lakehouse/iceberg/integration/test/TestMultipleJDBCLoad.java
 
b/catalogs/catalog-lakehouse-iceberg/src/test/java/org/apache/gravitino/catalog/lakehouse/iceberg/integration/test/TestMultipleJDBCLoad.java
index 75a7a8be4a..c2e5077c7b 100644
--- 
a/catalogs/catalog-lakehouse-iceberg/src/test/java/org/apache/gravitino/catalog/lakehouse/iceberg/integration/test/TestMultipleJDBCLoad.java
+++ 
b/catalogs/catalog-lakehouse-iceberg/src/test/java/org/apache/gravitino/catalog/lakehouse/iceberg/integration/test/TestMultipleJDBCLoad.java
@@ -31,6 +31,8 @@ import java.util.Map;
 import org.apache.gravitino.Catalog;
 import org.apache.gravitino.NameIdentifier;
 import org.apache.gravitino.client.GravitinoMetalake;
+import org.apache.gravitino.credential.Credential;
+import org.apache.gravitino.credential.JdbcCredential;
 import org.apache.gravitino.exceptions.ConnectionFailedException;
 import org.apache.gravitino.iceberg.common.IcebergConfig;
 import org.apache.gravitino.integration.test.container.MySQLContainer;
@@ -181,6 +183,16 @@ public class TestMultipleJDBCLoad extends BaseIT {
             comment,
             Collections.emptyMap());
 
+    Credential credential =
+        
postgreSqlCatalog.supportsCredentials().getCredential(JdbcCredential.JDBC_CREDENTIAL_TYPE);
+
+    Assertions.assertNotNull(credential);
+    Assertions.assertInstanceOf(JdbcCredential.class, credential);
+
+    JdbcCredential jdbcCredential = (JdbcCredential) credential;
+    Assertions.assertEquals(postgreSQLContainer.getUsername(), 
jdbcCredential.jdbcUser());
+    Assertions.assertEquals(postgreSQLContainer.getPassword(), 
jdbcCredential.jdbcPassword());
+
     Assertions.assertTrue(
         
mysqlCatalog.asTableCatalog().tableExists(NameIdentifier.of(schemaName, 
tableName)));
     Assertions.assertTrue(
diff --git 
a/catalogs/catalog-lakehouse-paimon/src/main/java/org/apache/gravitino/catalog/lakehouse/paimon/PaimonCatalog.java
 
b/catalogs/catalog-lakehouse-paimon/src/main/java/org/apache/gravitino/catalog/lakehouse/paimon/PaimonCatalog.java
index 7fa8499b77..ba69cbfc0b 100644
--- 
a/catalogs/catalog-lakehouse-paimon/src/main/java/org/apache/gravitino/catalog/lakehouse/paimon/PaimonCatalog.java
+++ 
b/catalogs/catalog-lakehouse-paimon/src/main/java/org/apache/gravitino/catalog/lakehouse/paimon/PaimonCatalog.java
@@ -18,12 +18,19 @@
  */
 package org.apache.gravitino.catalog.lakehouse.paimon;
 
+import com.google.common.collect.Maps;
+import java.util.ArrayList;
+import java.util.List;
 import java.util.Map;
+import org.apache.commons.lang3.StringUtils;
 import org.apache.gravitino.Catalog;
+import org.apache.gravitino.annotation.Evolving;
 import org.apache.gravitino.connector.BaseCatalog;
 import org.apache.gravitino.connector.CatalogOperations;
 import org.apache.gravitino.connector.PropertiesMetadata;
 import org.apache.gravitino.connector.capability.Capability;
+import org.apache.gravitino.credential.CredentialConstants;
+import org.apache.gravitino.credential.JdbcCredential;
 
 /**
  * Implementation of {@link Catalog} that represents an Apache Paimon catalog 
in Apache Gravitino.
@@ -77,4 +84,40 @@ public class PaimonCatalog extends 
BaseCatalog<PaimonCatalog> {
   public PropertiesMetadata schemaPropertiesMetadata() throws 
UnsupportedOperationException {
     return SCHEMA_PROPERTIES_META;
   }
+
+  @Override
+  @Evolving
+  public Map<String, String> propertiesWithCredentialProviders() {
+    Map<String, String> properties = 
Maps.newHashMap(super.propertiesWithCredentialProviders());
+    return applyDefaultCredentialProviders(properties);
+  }
+
+  private Map<String, String> applyDefaultCredentialProviders(Map<String, 
String> properties) {
+    // If credential providers already set, return as is
+    if 
(StringUtils.isNotBlank(properties.get(CredentialConstants.CREDENTIAL_PROVIDERS)))
 {
+      return properties;
+    }
+
+    List<String> credentialProviders = new ArrayList<>();
+
+    // Add JDBC credential provider if backend is JDBC and 
jdbc-user/jdbc-password are set
+    String catalogBackend = properties.get(PaimonConstants.CATALOG_BACKEND);
+    if (catalogBackend != null
+        && PaimonCatalogBackend.JDBC.name().equalsIgnoreCase(catalogBackend)) {
+      String jdbcUser = properties.get(PaimonConstants.GRAVITINO_JDBC_USER);
+      String jdbcPassword = 
properties.get(PaimonConstants.GRAVITINO_JDBC_PASSWORD);
+      if (StringUtils.isNotBlank(jdbcUser) && 
StringUtils.isNotBlank(jdbcPassword)) {
+        credentialProviders.add(JdbcCredential.JDBC_CREDENTIAL_TYPE);
+      }
+    }
+
+    addStorageCredentialProviders(properties, credentialProviders);
+
+    if (!credentialProviders.isEmpty()) {
+      properties.put(
+          CredentialConstants.CREDENTIAL_PROVIDERS, String.join(",", 
credentialProviders));
+    }
+
+    return properties;
+  }
 }
diff --git 
a/catalogs/catalog-lakehouse-paimon/src/test/java/org/apache/gravitino/catalog/lakehouse/paimon/TestPaimonCatalog.java
 
b/catalogs/catalog-lakehouse-paimon/src/test/java/org/apache/gravitino/catalog/lakehouse/paimon/TestPaimonCatalog.java
index f7187258f4..e83f8c7d78 100644
--- 
a/catalogs/catalog-lakehouse-paimon/src/test/java/org/apache/gravitino/catalog/lakehouse/paimon/TestPaimonCatalog.java
+++ 
b/catalogs/catalog-lakehouse-paimon/src/test/java/org/apache/gravitino/catalog/lakehouse/paimon/TestPaimonCatalog.java
@@ -37,8 +37,16 @@ import 
org.apache.gravitino.catalog.lakehouse.paimon.ops.PaimonCatalogOps;
 import org.apache.gravitino.connector.CatalogOperations;
 import org.apache.gravitino.connector.HasPropertyMetadata;
 import org.apache.gravitino.connector.PropertiesMetadata;
+import org.apache.gravitino.credential.AzureAccountKeyCredential;
+import org.apache.gravitino.credential.CredentialConstants;
+import org.apache.gravitino.credential.JdbcCredential;
+import org.apache.gravitino.credential.OSSSecretKeyCredential;
+import org.apache.gravitino.credential.S3SecretKeyCredential;
 import org.apache.gravitino.meta.AuditInfo;
 import org.apache.gravitino.meta.CatalogEntity;
+import org.apache.gravitino.storage.AzureProperties;
+import org.apache.gravitino.storage.OSSProperties;
+import org.apache.gravitino.storage.S3Properties;
 import org.junit.jupiter.api.AfterAll;
 import org.junit.jupiter.api.Assertions;
 import org.junit.jupiter.api.Test;
@@ -183,4 +191,216 @@ public class TestPaimonCatalog {
               
.contains(PaimonCatalogPropertiesMetadata.GRAVITINO_CATALOG_BACKEND));
     }
   }
+
+  @Test
+  void testJdbcBackendDefaultCredentialProviders() {
+    AuditInfo auditInfo =
+        
AuditInfo.builder().withCreator("creator").withCreateTime(Instant.now()).build();
+
+    // Test JDBC backend with jdbc-user and jdbc-password
+    Map<String, String> jdbcProps = Maps.newHashMap();
+    jdbcProps.put(PaimonConstants.CATALOG_BACKEND, "jdbc");
+    jdbcProps.put(PaimonConstants.URI, "jdbc:sqlite::memory:");
+    jdbcProps.put(PaimonConstants.WAREHOUSE, tempDir);
+    jdbcProps.put(PaimonConstants.GRAVITINO_JDBC_USER, "test-user");
+    jdbcProps.put(PaimonConstants.GRAVITINO_JDBC_PASSWORD, "test-password");
+
+    CatalogEntity jdbcEntity =
+        CatalogEntity.builder()
+            .withId(1L)
+            .withName("jdbc-catalog")
+            .withNamespace(Namespace.of("metalake"))
+            .withType(PaimonCatalog.Type.RELATIONAL)
+            .withProvider("lakehouse-paimon")
+            .withAuditInfo(auditInfo)
+            .withProperties(jdbcProps)
+            .build();
+
+    PaimonCatalog jdbcCatalog =
+        new 
PaimonCatalog().withCatalogConf(jdbcProps).withCatalogEntity(jdbcEntity);
+    Map<String, String> properties = 
jdbcCatalog.propertiesWithCredentialProviders();
+
+    // Should have jdbc credential provider
+    String credentialProviders = 
properties.get(CredentialConstants.CREDENTIAL_PROVIDERS);
+    Assertions.assertNotNull(credentialProviders);
+    
Assertions.assertTrue(credentialProviders.contains(JdbcCredential.JDBC_CREDENTIAL_TYPE));
+  }
+
+  @Test
+  void testJdbcBackendWithS3CredentialProviders() {
+    AuditInfo auditInfo =
+        
AuditInfo.builder().withCreator("creator").withCreateTime(Instant.now()).build();
+
+    // Test JDBC backend with jdbc-user, jdbc-password, and S3 credentials
+    Map<String, String> jdbcS3Props = Maps.newHashMap();
+    jdbcS3Props.put(PaimonConstants.CATALOG_BACKEND, "jdbc");
+    jdbcS3Props.put(PaimonConstants.URI, "jdbc:sqlite::memory:");
+    jdbcS3Props.put(PaimonConstants.WAREHOUSE, tempDir);
+    jdbcS3Props.put(PaimonConstants.GRAVITINO_JDBC_USER, "test-user");
+    jdbcS3Props.put(PaimonConstants.GRAVITINO_JDBC_PASSWORD, "test-password");
+    jdbcS3Props.put(S3Properties.GRAVITINO_S3_ACCESS_KEY_ID, "access-key");
+    jdbcS3Props.put(S3Properties.GRAVITINO_S3_SECRET_ACCESS_KEY, "secret-key");
+
+    CatalogEntity jdbcS3Entity =
+        CatalogEntity.builder()
+            .withId(2L)
+            .withName("jdbc-s3-catalog")
+            .withNamespace(Namespace.of("metalake"))
+            .withType(PaimonCatalog.Type.RELATIONAL)
+            .withProvider("lakehouse-paimon")
+            .withAuditInfo(auditInfo)
+            .withProperties(jdbcS3Props)
+            .build();
+
+    PaimonCatalog jdbcS3Catalog =
+        new 
PaimonCatalog().withCatalogConf(jdbcS3Props).withCatalogEntity(jdbcS3Entity);
+    Map<String, String> properties = 
jdbcS3Catalog.propertiesWithCredentialProviders();
+
+    // Should have both jdbc and s3-secret-key credential providers
+    String credentialProviders = 
properties.get(CredentialConstants.CREDENTIAL_PROVIDERS);
+    Assertions.assertNotNull(credentialProviders);
+    
Assertions.assertTrue(credentialProviders.contains(JdbcCredential.JDBC_CREDENTIAL_TYPE));
+    Assertions.assertTrue(
+        
credentialProviders.contains(S3SecretKeyCredential.S3_SECRET_KEY_CREDENTIAL_TYPE));
+  }
+
+  @Test
+  void testNonJdbcBackendNoDefaultCredentialProviders() {
+    AuditInfo auditInfo =
+        
AuditInfo.builder().withCreator("creator").withCreateTime(Instant.now()).build();
+
+    // Test non-JDBC backend (filesystem) - should not add default credential 
providers
+    Map<String, String> fsProps = Maps.newHashMap();
+    fsProps.put(PaimonConstants.CATALOG_BACKEND, "filesystem");
+    fsProps.put(PaimonConstants.WAREHOUSE, tempDir);
+
+    CatalogEntity fsEntity =
+        CatalogEntity.builder()
+            .withId(3L)
+            .withName("fs-catalog")
+            .withNamespace(Namespace.of("metalake"))
+            .withType(PaimonCatalog.Type.RELATIONAL)
+            .withProvider("lakehouse-paimon")
+            .withAuditInfo(auditInfo)
+            .withProperties(fsProps)
+            .build();
+
+    PaimonCatalog fsCatalog =
+        new 
PaimonCatalog().withCatalogConf(fsProps).withCatalogEntity(fsEntity);
+    Map<String, String> properties = 
fsCatalog.propertiesWithCredentialProviders();
+
+    // Should not have credential providers for non-JDBC backend
+    String credentialProviders = 
properties.get(CredentialConstants.CREDENTIAL_PROVIDERS);
+    Assertions.assertNull(credentialProviders);
+  }
+
+  @Test
+  void testJdbcBackendWithOSSCredentialProviders() {
+    AuditInfo auditInfo =
+        
AuditInfo.builder().withCreator("creator").withCreateTime(Instant.now()).build();
+
+    // Test JDBC backend with jdbc-user, jdbc-password, and OSS credentials
+    Map<String, String> jdbcOssProps = Maps.newHashMap();
+    jdbcOssProps.put(PaimonConstants.CATALOG_BACKEND, "jdbc");
+    jdbcOssProps.put(PaimonConstants.URI, "jdbc:sqlite::memory:");
+    jdbcOssProps.put(PaimonConstants.WAREHOUSE, tempDir);
+    jdbcOssProps.put(PaimonConstants.GRAVITINO_JDBC_USER, "test-user");
+    jdbcOssProps.put(PaimonConstants.GRAVITINO_JDBC_PASSWORD, "test-password");
+    jdbcOssProps.put(OSSProperties.GRAVITINO_OSS_ACCESS_KEY_ID, 
"oss-access-key");
+    jdbcOssProps.put(OSSProperties.GRAVITINO_OSS_ACCESS_KEY_SECRET, 
"oss-secret-key");
+
+    CatalogEntity jdbcOssEntity =
+        CatalogEntity.builder()
+            .withId(5L)
+            .withName("jdbc-oss-catalog")
+            .withNamespace(Namespace.of("metalake"))
+            .withType(PaimonCatalog.Type.RELATIONAL)
+            .withProvider("lakehouse-paimon")
+            .withAuditInfo(auditInfo)
+            .withProperties(jdbcOssProps)
+            .build();
+
+    PaimonCatalog jdbcOssCatalog =
+        new 
PaimonCatalog().withCatalogConf(jdbcOssProps).withCatalogEntity(jdbcOssEntity);
+    Map<String, String> properties = 
jdbcOssCatalog.propertiesWithCredentialProviders();
+
+    // Should have both jdbc and oss-secret-key credential providers
+    String credentialProviders = 
properties.get(CredentialConstants.CREDENTIAL_PROVIDERS);
+    Assertions.assertNotNull(credentialProviders);
+    
Assertions.assertTrue(credentialProviders.contains(JdbcCredential.JDBC_CREDENTIAL_TYPE));
+    Assertions.assertTrue(
+        
credentialProviders.contains(OSSSecretKeyCredential.OSS_SECRET_KEY_CREDENTIAL_TYPE));
+  }
+
+  @Test
+  void testJdbcBackendWithAzureCredentialProviders() {
+    AuditInfo auditInfo =
+        
AuditInfo.builder().withCreator("creator").withCreateTime(Instant.now()).build();
+
+    // Test JDBC backend with jdbc-user, jdbc-password, and Azure credentials
+    Map<String, String> jdbcAzureProps = Maps.newHashMap();
+    jdbcAzureProps.put(PaimonConstants.CATALOG_BACKEND, "jdbc");
+    jdbcAzureProps.put(PaimonConstants.URI, "jdbc:sqlite::memory:");
+    jdbcAzureProps.put(PaimonConstants.WAREHOUSE, tempDir);
+    jdbcAzureProps.put(PaimonConstants.GRAVITINO_JDBC_USER, "test-user");
+    jdbcAzureProps.put(PaimonConstants.GRAVITINO_JDBC_PASSWORD, 
"test-password");
+    jdbcAzureProps.put(AzureProperties.GRAVITINO_AZURE_STORAGE_ACCOUNT_NAME, 
"azure-account-name");
+    jdbcAzureProps.put(AzureProperties.GRAVITINO_AZURE_STORAGE_ACCOUNT_KEY, 
"azure-account-key");
+
+    CatalogEntity jdbcAzureEntity =
+        CatalogEntity.builder()
+            .withId(6L)
+            .withName("jdbc-azure-catalog")
+            .withNamespace(Namespace.of("metalake"))
+            .withType(PaimonCatalog.Type.RELATIONAL)
+            .withProvider("lakehouse-paimon")
+            .withAuditInfo(auditInfo)
+            .withProperties(jdbcAzureProps)
+            .build();
+
+    PaimonCatalog jdbcAzureCatalog =
+        new 
PaimonCatalog().withCatalogConf(jdbcAzureProps).withCatalogEntity(jdbcAzureEntity);
+    Map<String, String> properties = 
jdbcAzureCatalog.propertiesWithCredentialProviders();
+
+    // Should have both jdbc and azure-account-key credential providers
+    String credentialProviders = 
properties.get(CredentialConstants.CREDENTIAL_PROVIDERS);
+    Assertions.assertNotNull(credentialProviders);
+    
Assertions.assertTrue(credentialProviders.contains(JdbcCredential.JDBC_CREDENTIAL_TYPE));
+    Assertions.assertTrue(
+        
credentialProviders.contains(AzureAccountKeyCredential.AZURE_ACCOUNT_KEY_CREDENTIAL_TYPE));
+  }
+
+  @Test
+  void testExplicitCredentialProvidersNotOverridden() {
+    AuditInfo auditInfo =
+        
AuditInfo.builder().withCreator("creator").withCreateTime(Instant.now()).build();
+
+    // Test that explicit credential-providers setting is not overridden
+    Map<String, String> explicitProps = Maps.newHashMap();
+    explicitProps.put(PaimonConstants.CATALOG_BACKEND, "jdbc");
+    explicitProps.put(PaimonConstants.URI, "jdbc:sqlite::memory:");
+    explicitProps.put(PaimonConstants.WAREHOUSE, tempDir);
+    explicitProps.put(PaimonConstants.GRAVITINO_JDBC_USER, "test-user");
+    explicitProps.put(PaimonConstants.GRAVITINO_JDBC_PASSWORD, 
"test-password");
+    explicitProps.put(CredentialConstants.CREDENTIAL_PROVIDERS, 
"custom-provider");
+
+    CatalogEntity explicitEntity =
+        CatalogEntity.builder()
+            .withId(4L)
+            .withName("explicit-catalog")
+            .withNamespace(Namespace.of("metalake"))
+            .withType(PaimonCatalog.Type.RELATIONAL)
+            .withProvider("lakehouse-paimon")
+            .withAuditInfo(auditInfo)
+            .withProperties(explicitProps)
+            .build();
+
+    PaimonCatalog explicitCatalog =
+        new 
PaimonCatalog().withCatalogConf(explicitProps).withCatalogEntity(explicitEntity);
+    Map<String, String> properties = 
explicitCatalog.propertiesWithCredentialProviders();
+
+    // Should keep explicit credential providers, not override
+    String credentialProviders = 
properties.get(CredentialConstants.CREDENTIAL_PROVIDERS);
+    Assertions.assertEquals("custom-provider", credentialProviders);
+  }
 }
diff --git 
a/catalogs/catalog-lakehouse-paimon/src/test/java/org/apache/gravitino/catalog/lakehouse/paimon/integration/test/CatalogPaimonJdbcCredentialIT.java
 
b/catalogs/catalog-lakehouse-paimon/src/test/java/org/apache/gravitino/catalog/lakehouse/paimon/integration/test/CatalogPaimonJdbcCredentialIT.java
new file mode 100644
index 0000000000..b143105e01
--- /dev/null
+++ 
b/catalogs/catalog-lakehouse-paimon/src/test/java/org/apache/gravitino/catalog/lakehouse/paimon/integration/test/CatalogPaimonJdbcCredentialIT.java
@@ -0,0 +1,155 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.gravitino.catalog.lakehouse.paimon.integration.test;
+
+import com.google.common.collect.Maps;
+import java.io.IOException;
+import java.util.Collections;
+import java.util.Map;
+import org.apache.gravitino.Catalog;
+import 
org.apache.gravitino.catalog.lakehouse.paimon.PaimonCatalogPropertiesMetadata;
+import org.apache.gravitino.catalog.lakehouse.paimon.PaimonConstants;
+import org.apache.gravitino.client.GravitinoMetalake;
+import org.apache.gravitino.credential.Credential;
+import org.apache.gravitino.credential.JdbcCredential;
+import org.apache.gravitino.integration.test.util.BaseIT;
+import org.apache.gravitino.integration.test.util.GravitinoITUtils;
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Tag;
+import org.junit.jupiter.api.Test;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Integration test for Paimon catalog with JDBC backend credential vending. 
Tests that JDBC
+ * credentials can be retrieved through the credential API.
+ */
+@Tag("gravitino-docker-test")
+public class CatalogPaimonJdbcCredentialIT extends BaseIT {
+
+  private static final Logger LOG = 
LoggerFactory.getLogger(CatalogPaimonJdbcCredentialIT.class);
+
+  private static final String JDBC_USER = "paimon_user";
+  private static final String JDBC_PASSWORD = "paimon_password";
+
+  private String metalakeName = 
GravitinoITUtils.genRandomName("paimon_jdbc_credential_metalake");
+  private String catalogName = 
GravitinoITUtils.genRandomName("paimon_jdbc_credential_catalog");
+  private GravitinoMetalake metalake;
+
+  @BeforeAll
+  public void startIntegrationTest() {
+    // Do nothing - override to prevent auto start
+  }
+
+  @BeforeAll
+  public void startUp() throws Exception {
+    super.startIntegrationTest();
+
+    Assertions.assertFalse(client.metalakeExists(metalakeName));
+    metalake = client.createMetalake(metalakeName, "metalake comment", 
Collections.emptyMap());
+    Assertions.assertTrue(client.metalakeExists(metalakeName));
+
+    createCatalog();
+  }
+
+  @AfterAll
+  public void tearDown() throws IOException {
+    try {
+      if (metalake != null && metalake.catalogExists(catalogName)) {
+        metalake.disableCatalog(catalogName);
+        metalake.dropCatalog(catalogName, true);
+      }
+      if (client != null && client.metalakeExists(metalakeName)) {
+        client.disableMetalake(metalakeName);
+        client.dropMetalake(metalakeName, true);
+      }
+    } finally {
+      if (client != null) {
+        try {
+          client.close();
+        } catch (Exception e) {
+          LOG.error("Exception in closing client", e);
+        }
+        client = null;
+      }
+      try {
+        super.stopIntegrationTest();
+      } catch (Exception e) {
+        LOG.error("Exception in closing BaseIT", e);
+      }
+    }
+  }
+
+  @AfterAll
+  public void stopIntegrationTest() {
+    // Do nothing - override to prevent auto stop
+  }
+
+  private void createCatalog() {
+    Map<String, String> catalogProperties = Maps.newHashMap();
+    
catalogProperties.put(PaimonCatalogPropertiesMetadata.GRAVITINO_CATALOG_BACKEND,
 "jdbc");
+    catalogProperties.put(PaimonCatalogPropertiesMetadata.URI, 
"jdbc:sqlite::memory:");
+    catalogProperties.put(PaimonCatalogPropertiesMetadata.WAREHOUSE, 
"/tmp/paimon-jdbc-test");
+    catalogProperties.put(PaimonConstants.GRAVITINO_JDBC_DRIVER, 
"org.sqlite.JDBC");
+    catalogProperties.put(PaimonConstants.GRAVITINO_JDBC_USER, JDBC_USER);
+    catalogProperties.put(PaimonConstants.GRAVITINO_JDBC_PASSWORD, 
JDBC_PASSWORD);
+
+    Catalog createdCatalog =
+        metalake.createCatalog(
+            catalogName,
+            Catalog.Type.RELATIONAL,
+            "lakehouse-paimon",
+            "JDBC backend Paimon catalog for credential testing",
+            catalogProperties);
+    Assertions.assertNotNull(createdCatalog);
+    Assertions.assertTrue(metalake.catalogExists(catalogName));
+  }
+
+  @Test
+  void testGetJdbcCredentialFromCatalog() {
+    Catalog catalog = metalake.loadCatalog(catalogName);
+    Credential[] credentials = catalog.supportsCredentials().getCredentials();
+
+    // Should have JDBC credential automatically configured for JDBC backend
+    Assertions.assertEquals(1, credentials.length);
+    Assertions.assertInstanceOf(JdbcCredential.class, credentials[0]);
+
+    JdbcCredential jdbcCredential = (JdbcCredential) credentials[0];
+    Assertions.assertEquals(JDBC_USER, jdbcCredential.jdbcUser());
+    Assertions.assertEquals(JDBC_PASSWORD, jdbcCredential.jdbcPassword());
+    Assertions.assertEquals(0, jdbcCredential.expireTimeInMs());
+    Assertions.assertEquals(JdbcCredential.JDBC_CREDENTIAL_TYPE, 
jdbcCredential.credentialType());
+  }
+
+  @Test
+  void testGetJdbcCredentialByType() {
+    Catalog catalog = metalake.loadCatalog(catalogName);
+    Credential credential =
+        
catalog.supportsCredentials().getCredential(JdbcCredential.JDBC_CREDENTIAL_TYPE);
+
+    Assertions.assertNotNull(credential);
+    Assertions.assertInstanceOf(JdbcCredential.class, credential);
+
+    JdbcCredential jdbcCredential = (JdbcCredential) credential;
+    Assertions.assertEquals(JDBC_USER, jdbcCredential.jdbcUser());
+    Assertions.assertEquals(JDBC_PASSWORD, jdbcCredential.jdbcPassword());
+  }
+}
diff --git 
a/clients/client-java/src/main/java/org/apache/gravitino/client/RelationalCatalog.java
 
b/clients/client-java/src/main/java/org/apache/gravitino/client/RelationalCatalog.java
index 2df786d2d9..c94d3bba2c 100644
--- 
a/clients/client-java/src/main/java/org/apache/gravitino/client/RelationalCatalog.java
+++ 
b/clients/client-java/src/main/java/org/apache/gravitino/client/RelationalCatalog.java
@@ -37,6 +37,8 @@ import org.apache.gravitino.Catalog;
 import org.apache.gravitino.NameIdentifier;
 import org.apache.gravitino.Namespace;
 import org.apache.gravitino.authorization.Privilege;
+import org.apache.gravitino.credential.Credential;
+import org.apache.gravitino.credential.SupportsCredentials;
 import org.apache.gravitino.dto.AuditDTO;
 import org.apache.gravitino.dto.CatalogDTO;
 import org.apache.gravitino.dto.rel.ColumnDTO;
@@ -75,7 +77,8 @@ import org.apache.gravitino.rest.RESTUtils;
  * operations, for example, schemas and tables list, creation, update and 
deletion. A Relational
  * catalog is under the metalake.
  */
-class RelationalCatalog extends BaseSchemaCatalog implements TableCatalog, 
ViewCatalog {
+class RelationalCatalog extends BaseSchemaCatalog
+    implements TableCatalog, ViewCatalog, SupportsCredentials {
 
   public static final String PRIVILEGES = "privileges";
 
@@ -300,6 +303,16 @@ class RelationalCatalog extends BaseSchemaCatalog 
implements TableCatalog, ViewC
     return resp.dropped();
   }
 
+  @Override
+  public SupportsCredentials supportsCredentials() throws 
UnsupportedOperationException {
+    return this;
+  }
+
+  @Override
+  public Credential[] getCredentials() {
+    return objectCredentialOperations.getCredentials();
+  }
+
   /**
    * List all the views under the given Schema namespace.
    *
diff --git 
a/common/src/test/java/org/apache/gravitino/credential/TestCredentialFactory.java
 
b/common/src/test/java/org/apache/gravitino/credential/TestCredentialFactory.java
index 7bb766a462..443399daf6 100644
--- 
a/common/src/test/java/org/apache/gravitino/credential/TestCredentialFactory.java
+++ 
b/common/src/test/java/org/apache/gravitino/credential/TestCredentialFactory.java
@@ -190,4 +190,26 @@ public class TestCredentialFactory {
     Assertions.assertEquals(storageAccountKey, 
azureAccountKeyCredential.accountKey());
     Assertions.assertEquals(expireTime, 
azureAccountKeyCredential.expireTimeInMs());
   }
+
+  @Test
+  void testJdbcCredential() {
+    String jdbcUser = "test-user";
+    String jdbcPassword = "test-password";
+    Map<String, String> jdbcCredentialInfo =
+        ImmutableMap.of(
+            JdbcCredential.GRAVITINO_JDBC_USER,
+            jdbcUser,
+            JdbcCredential.GRAVITINO_JDBC_PASSWORD,
+            jdbcPassword);
+    long expireTime = 0;
+    Credential credential =
+        CredentialFactory.create(
+            JdbcCredential.JDBC_CREDENTIAL_TYPE, jdbcCredentialInfo, 
expireTime);
+    Assertions.assertEquals(JdbcCredential.JDBC_CREDENTIAL_TYPE, 
credential.credentialType());
+    Assertions.assertInstanceOf(JdbcCredential.class, credential);
+    JdbcCredential jdbcCredential = (JdbcCredential) credential;
+    Assertions.assertEquals(jdbcUser, jdbcCredential.jdbcUser());
+    Assertions.assertEquals(jdbcPassword, jdbcCredential.jdbcPassword());
+    Assertions.assertEquals(expireTime, jdbcCredential.expireTimeInMs());
+  }
 }
diff --git a/core/src/main/java/org/apache/gravitino/connector/BaseCatalog.java 
b/core/src/main/java/org/apache/gravitino/connector/BaseCatalog.java
index 2cfed0f645..947474008f 100644
--- a/core/src/main/java/org/apache/gravitino/connector/BaseCatalog.java
+++ b/core/src/main/java/org/apache/gravitino/connector/BaseCatalog.java
@@ -24,8 +24,10 @@ import com.google.common.base.Preconditions;
 import com.google.common.collect.Maps;
 import java.io.Closeable;
 import java.io.IOException;
+import java.util.List;
 import java.util.Map;
 import java.util.Optional;
+import org.apache.commons.lang3.StringUtils;
 import org.apache.gravitino.Audit;
 import org.apache.gravitino.Catalog;
 import org.apache.gravitino.CatalogProvider;
@@ -33,10 +35,16 @@ import org.apache.gravitino.annotation.Evolving;
 import org.apache.gravitino.connector.authorization.AuthorizationPlugin;
 import org.apache.gravitino.connector.authorization.BaseAuthorization;
 import org.apache.gravitino.connector.capability.Capability;
+import org.apache.gravitino.credential.AzureAccountKeyCredential;
 import org.apache.gravitino.credential.CatalogCredentialManager;
+import org.apache.gravitino.credential.OSSSecretKeyCredential;
+import org.apache.gravitino.credential.S3SecretKeyCredential;
 import org.apache.gravitino.exceptions.CatalogNotInUseException;
 import org.apache.gravitino.exceptions.MetalakeNotInUseException;
 import org.apache.gravitino.meta.CatalogEntity;
+import org.apache.gravitino.storage.AzureProperties;
+import org.apache.gravitino.storage.OSSProperties;
+import org.apache.gravitino.storage.S3Properties;
 import org.apache.gravitino.utils.IsolatedClassLoader;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -341,7 +349,8 @@ public abstract class BaseCatalog<T extends BaseCatalog>
     if (catalogCredentialManager == null) {
       synchronized (this) {
         if (catalogCredentialManager == null) {
-          this.catalogCredentialManager = new CatalogCredentialManager(name(), 
properties());
+          this.catalogCredentialManager =
+              new CatalogCredentialManager(name(), 
propertiesWithCredentialProviders());
         }
       }
     }
@@ -445,6 +454,49 @@ public abstract class BaseCatalog<T extends BaseCatalog>
     return properties;
   }
 
+  /**
+   * Retrieves the properties of the catalog including credential providers. 
Subclasses should
+   * override this method to inject auto-detected credential provider names 
into the properties map
+   * before the {@link CatalogCredentialManager} is initialized. The default 
implementation returns
+   * {@link #properties()} unchanged.
+   *
+   * @return A map of properties including credential providers.
+   */
+  public Map<String, String> propertiesWithCredentialProviders() {
+    return properties();
+  }
+
+  /**
+   * Detects storage credential providers (S3, OSS, Azure) from catalog 
properties and appends them
+   * to the provided list. Subclasses can call this method in their {@link
+   * #propertiesWithCredentialProviders()} implementation to avoid duplicating 
storage credential
+   * detection logic.
+   *
+   * @param properties The catalog properties map to scan for storage 
credentials.
+   * @param credentialProviders The list to append detected storage credential 
providers to.
+   */
+  @Evolving
+  protected void addStorageCredentialProviders(
+      Map<String, String> properties, List<String> credentialProviders) {
+    String s3AccessKeyId = 
properties.get(S3Properties.GRAVITINO_S3_ACCESS_KEY_ID);
+    String s3SecretAccessKey = 
properties.get(S3Properties.GRAVITINO_S3_SECRET_ACCESS_KEY);
+    if (StringUtils.isNotBlank(s3AccessKeyId) && 
StringUtils.isNotBlank(s3SecretAccessKey)) {
+      
credentialProviders.add(S3SecretKeyCredential.S3_SECRET_KEY_CREDENTIAL_TYPE);
+    }
+
+    String ossAccessKeyId = 
properties.get(OSSProperties.GRAVITINO_OSS_ACCESS_KEY_ID);
+    String ossSecretAccessKey = 
properties.get(OSSProperties.GRAVITINO_OSS_ACCESS_KEY_SECRET);
+    if (StringUtils.isNotBlank(ossAccessKeyId) && 
StringUtils.isNotBlank(ossSecretAccessKey)) {
+      
credentialProviders.add(OSSSecretKeyCredential.OSS_SECRET_KEY_CREDENTIAL_TYPE);
+    }
+
+    String azureAccountName = 
properties.get(AzureProperties.GRAVITINO_AZURE_STORAGE_ACCOUNT_NAME);
+    String azureAccountKey = 
properties.get(AzureProperties.GRAVITINO_AZURE_STORAGE_ACCOUNT_KEY);
+    if (StringUtils.isNotBlank(azureAccountName) && 
StringUtils.isNotBlank(azureAccountKey)) {
+      
credentialProviders.add(AzureAccountKeyCredential.AZURE_ACCOUNT_KEY_CREDENTIAL_TYPE);
+    }
+  }
+
   @Override
   public Audit auditInfo() {
     Preconditions.checkArgument(entity != null, ENTITY_IS_NOT_SET);
diff --git 
a/core/src/main/java/org/apache/gravitino/credential/CredentialOperationDispatcher.java
 
b/core/src/main/java/org/apache/gravitino/credential/CredentialOperationDispatcher.java
index ba25fe3e5a..88430cb693 100644
--- 
a/core/src/main/java/org/apache/gravitino/credential/CredentialOperationDispatcher.java
+++ 
b/core/src/main/java/org/apache/gravitino/credential/CredentialOperationDispatcher.java
@@ -88,7 +88,7 @@ public class CredentialOperationDispatcher extends 
OperationDispatcher {
   private Map<String, CredentialContext> getCredentialContexts(
       BaseCatalog baseCatalog, NameIdentifier nameIdentifier, 
CredentialPrivilege privilege) {
     if 
(nameIdentifier.equals(NameIdentifierUtil.getCatalogIdentifier(nameIdentifier)))
 {
-      return getCatalogCredentialContexts(baseCatalog.properties());
+      return 
getCatalogCredentialContexts(baseCatalog.propertiesWithCredentialProviders());
     }
 
     if (baseCatalog.ops() instanceof SupportsPathBasedCredentials) {
diff --git 
a/core/src/main/java/org/apache/gravitino/credential/JdbcCredentialProvider.java
 
b/core/src/main/java/org/apache/gravitino/credential/JdbcCredentialProvider.java
new file mode 100644
index 0000000000..9971cd78ca
--- /dev/null
+++ 
b/core/src/main/java/org/apache/gravitino/credential/JdbcCredentialProvider.java
@@ -0,0 +1,62 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+
+package org.apache.gravitino.credential;
+
+import java.util.Map;
+import javax.annotation.Nullable;
+import org.apache.commons.lang3.StringUtils;
+
+/** Generate JDBC user and password credentials to access JDBC backend 
services. */
+public class JdbcCredentialProvider implements CredentialProvider {
+
+  private String jdbcUser;
+  private String jdbcPassword;
+
+  @Override
+  public void initialize(Map<String, String> properties) {
+    if (properties == null) {
+      return;
+    }
+    // jdbcUser / jdbcPassword will be null when the catalog was not 
configured with JDBC
+    // credentials.
+    // getCredential() handles this by returning null (no credential 
available).
+    // Note: The property keys "jdbc-user" and "jdbc-password" match the 
configuration keys
+    // used by JDBC-based catalogs (e.g., Iceberg JDBC backend, Paimon JDBC 
backend).
+    this.jdbcUser = properties.get(JdbcCredential.GRAVITINO_JDBC_USER);
+    this.jdbcPassword = properties.get(JdbcCredential.GRAVITINO_JDBC_PASSWORD);
+  }
+
+  @Override
+  public void close() {}
+
+  @Override
+  public String credentialType() {
+    return JdbcCredential.JDBC_CREDENTIAL_TYPE;
+  }
+
+  @Nullable
+  @Override
+  public Credential getCredential(CredentialContext context) {
+    if (StringUtils.isBlank(jdbcUser) || StringUtils.isBlank(jdbcPassword)) {
+      return null;
+    }
+    return new JdbcCredential(jdbcUser, jdbcPassword);
+  }
+}
diff --git 
a/core/src/test/resources/META-INF/services/org.apache.gravitino.credential.CredentialProvider
 
b/core/src/main/resources/META-INF/services/org.apache.gravitino.credential.CredentialProvider
similarity index 87%
copy from 
core/src/test/resources/META-INF/services/org.apache.gravitino.credential.CredentialProvider
copy to 
core/src/main/resources/META-INF/services/org.apache.gravitino.credential.CredentialProvider
index 6e1fdde4bd..6b992cebfb 100644
--- 
a/core/src/test/resources/META-INF/services/org.apache.gravitino.credential.CredentialProvider
+++ 
b/core/src/main/resources/META-INF/services/org.apache.gravitino.credential.CredentialProvider
@@ -16,5 +16,4 @@
 # specific language governing permissions and limitations
 # under the License.
 #
-org.apache.gravitino.credential.DummyCredentialProvider
-org.apache.gravitino.credential.Dummy2CredentialProvider
+org.apache.gravitino.credential.JdbcCredentialProvider
diff --git 
a/core/src/test/java/org/apache/gravitino/credential/TestJdbcCredentialProvider.java
 
b/core/src/test/java/org/apache/gravitino/credential/TestJdbcCredentialProvider.java
new file mode 100644
index 0000000000..41343d651c
--- /dev/null
+++ 
b/core/src/test/java/org/apache/gravitino/credential/TestJdbcCredentialProvider.java
@@ -0,0 +1,193 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+
+package org.apache.gravitino.credential;
+
+import com.google.common.collect.ImmutableMap;
+import java.util.Map;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+public class TestJdbcCredentialProvider {
+
+  @Test
+  void testJdbcCredentialProvider() {
+    String jdbcUser = "test-user";
+    String jdbcPassword = "test-password";
+    Map<String, String> catalogProperties =
+        ImmutableMap.of(
+            JdbcCredential.GRAVITINO_JDBC_USER,
+            jdbcUser,
+            JdbcCredential.GRAVITINO_JDBC_PASSWORD,
+            jdbcPassword);
+
+    CredentialProvider credentialProvider =
+        CredentialProviderFactory.create(JdbcCredential.JDBC_CREDENTIAL_TYPE, 
catalogProperties);
+
+    Assertions.assertEquals(
+        JdbcCredential.JDBC_CREDENTIAL_TYPE, 
credentialProvider.credentialType());
+    Assertions.assertInstanceOf(JdbcCredentialProvider.class, 
credentialProvider);
+
+    CatalogCredentialContext context = new 
CatalogCredentialContext("test-user");
+    Credential credential = credentialProvider.getCredential(context);
+
+    Assertions.assertNotNull(credential);
+    Assertions.assertInstanceOf(JdbcCredential.class, credential);
+    JdbcCredential jdbcCredential = (JdbcCredential) credential;
+
+    Assertions.assertEquals(jdbcUser, jdbcCredential.jdbcUser());
+    Assertions.assertEquals(jdbcPassword, jdbcCredential.jdbcPassword());
+    Assertions.assertEquals(0, jdbcCredential.expireTimeInMs());
+  }
+
+  @Test
+  void testJdbcCredentialProviderWithMissingProperties() {
+    Map<String, String> catalogProperties = ImmutableMap.of();
+
+    CredentialProvider credentialProvider =
+        CredentialProviderFactory.create(JdbcCredential.JDBC_CREDENTIAL_TYPE, 
catalogProperties);
+
+    CatalogCredentialContext context = new 
CatalogCredentialContext("test-user");
+    Credential credential = credentialProvider.getCredential(context);
+
+    Assertions.assertNull(credential);
+  }
+
+  @Test
+  void testJdbcCredentialInfo() {
+    String jdbcUser = "test-user";
+    String jdbcPassword = "test-password";
+    JdbcCredential jdbcCredential = new JdbcCredential(jdbcUser, jdbcPassword);
+
+    Map<String, String> credentialInfo = jdbcCredential.credentialInfo();
+    Assertions.assertEquals(2, credentialInfo.size());
+    Assertions.assertEquals(jdbcUser, 
credentialInfo.get(JdbcCredential.GRAVITINO_JDBC_USER));
+    Assertions.assertEquals(
+        jdbcPassword, 
credentialInfo.get(JdbcCredential.GRAVITINO_JDBC_PASSWORD));
+  }
+
+  @Test
+  void testJdbcCredentialInitialize() {
+    String jdbcUser = "test-user";
+    String jdbcPassword = "test-password";
+    Map<String, String> credentialInfo =
+        ImmutableMap.of(
+            JdbcCredential.GRAVITINO_JDBC_USER,
+            jdbcUser,
+            JdbcCredential.GRAVITINO_JDBC_PASSWORD,
+            jdbcPassword);
+
+    JdbcCredential jdbcCredential = new JdbcCredential();
+    jdbcCredential.initialize(credentialInfo, 0);
+
+    Assertions.assertEquals(jdbcUser, jdbcCredential.jdbcUser());
+    Assertions.assertEquals(jdbcPassword, jdbcCredential.jdbcPassword());
+  }
+
+  @Test
+  void testPartialCredentials() {
+    Map<String, String> catalogProperties =
+        ImmutableMap.of(JdbcCredential.GRAVITINO_JDBC_USER, "test-user");
+
+    CredentialProvider credentialProvider =
+        CredentialProviderFactory.create(JdbcCredential.JDBC_CREDENTIAL_TYPE, 
catalogProperties);
+
+    CatalogCredentialContext context = new 
CatalogCredentialContext("test-user");
+    Credential credential = credentialProvider.getCredential(context);
+
+    Assertions.assertNull(credential);
+  }
+
+  @Test
+  void testEmptyStringCredentials() {
+    Map<String, String> catalogProperties =
+        ImmutableMap.of(
+            JdbcCredential.GRAVITINO_JDBC_USER, "",
+            JdbcCredential.GRAVITINO_JDBC_PASSWORD, "test-password");
+
+    CredentialProvider credentialProvider =
+        CredentialProviderFactory.create(JdbcCredential.JDBC_CREDENTIAL_TYPE, 
catalogProperties);
+
+    CatalogCredentialContext context = new 
CatalogCredentialContext("test-user");
+    Credential credential = credentialProvider.getCredential(context);
+
+    Assertions.assertNull(credential);
+  }
+
+  @Test
+  void testNullProperties() {
+    CredentialProvider credentialProvider =
+        CredentialProviderFactory.create(JdbcCredential.JDBC_CREDENTIAL_TYPE, 
null);
+
+    CatalogCredentialContext context = new 
CatalogCredentialContext("test-user");
+    Credential credential = credentialProvider.getCredential(context);
+
+    Assertions.assertNull(credential);
+  }
+
+  @Test
+  void testEmptyPasswordReturnsNull() {
+    Map<String, String> catalogProperties =
+        ImmutableMap.of(
+            JdbcCredential.GRAVITINO_JDBC_USER,
+            "test-user",
+            JdbcCredential.GRAVITINO_JDBC_PASSWORD,
+            "");
+
+    CredentialProvider credentialProvider =
+        CredentialProviderFactory.create(JdbcCredential.JDBC_CREDENTIAL_TYPE, 
catalogProperties);
+
+    CatalogCredentialContext context = new 
CatalogCredentialContext("test-user");
+    Credential credential = credentialProvider.getCredential(context);
+
+    Assertions.assertNull(credential);
+  }
+
+  @Test
+  void testJdbcCredentialConstructorBlankUserThrows() {
+    Assertions.assertThrows(
+        IllegalArgumentException.class, () -> new JdbcCredential("", 
"password"));
+  }
+
+  @Test
+  void testJdbcCredentialConstructorBlankPasswordThrows() {
+    Assertions.assertThrows(IllegalArgumentException.class, () -> new 
JdbcCredential("user", ""));
+  }
+
+  @Test
+  void testJdbcCredentialInitializeNonZeroExpireTimeThrows() {
+    Map<String, String> credentialInfo =
+        ImmutableMap.of(
+            JdbcCredential.GRAVITINO_JDBC_USER,
+            "user",
+            JdbcCredential.GRAVITINO_JDBC_PASSWORD,
+            "pass");
+    JdbcCredential jdbcCredential = new JdbcCredential();
+    Assertions.assertThrows(
+        IllegalArgumentException.class, () -> 
jdbcCredential.initialize(credentialInfo, 1000L));
+  }
+
+  @Test
+  void testToStringDoesNotContainPassword() {
+    JdbcCredential jdbcCredential = new JdbcCredential("user", "secret");
+    String str = jdbcCredential.toString();
+    Assertions.assertTrue(str.contains("user"));
+    Assertions.assertFalse(str.contains("secret"));
+  }
+}
diff --git 
a/core/src/test/resources/META-INF/services/org.apache.gravitino.credential.CredentialProvider
 
b/core/src/test/resources/META-INF/services/org.apache.gravitino.credential.CredentialProvider
index 6e1fdde4bd..e672bd50eb 100644
--- 
a/core/src/test/resources/META-INF/services/org.apache.gravitino.credential.CredentialProvider
+++ 
b/core/src/test/resources/META-INF/services/org.apache.gravitino.credential.CredentialProvider
@@ -18,3 +18,4 @@
 #
 org.apache.gravitino.credential.DummyCredentialProvider
 org.apache.gravitino.credential.Dummy2CredentialProvider
+org.apache.gravitino.credential.JdbcCredentialProvider
diff --git 
a/integration-test-common/src/test/java/org/apache/gravitino/integration/test/util/BaseIT.java
 
b/integration-test-common/src/test/java/org/apache/gravitino/integration/test/util/BaseIT.java
index 73ec2fa3ce..b003b6e5f8 100644
--- 
a/integration-test-common/src/test/java/org/apache/gravitino/integration/test/util/BaseIT.java
+++ 
b/integration-test-common/src/test/java/org/apache/gravitino/integration/test/util/BaseIT.java
@@ -368,12 +368,20 @@ public class BaseIT {
 
     serverConfig = new ServerConfig();
     customConfigs.put(ENTITY_RELATIONAL_JDBC_BACKEND_PATH.getKey(), 
file.getAbsolutePath());
-    if (ignoreLanceAuxRestService && ignoreIcebergAuxRestService) {
-      customConfigs.put(
-          AuxiliaryServiceManager.GRAVITINO_AUX_SERVICE_PREFIX
-              + AuxiliaryServiceManager.AUX_SERVICE_NAMES,
-          "");
+
+    List<String> auxServicesList = new ArrayList<>();
+    if (!ignoreIcebergAuxRestService) {
+      auxServicesList.add("iceberg-rest");
+    }
+    if (!ignoreLanceAuxRestService) {
+      auxServicesList.add("lance-rest");
     }
+    String auxServices = String.join(",", auxServicesList);
+
+    customConfigs.put(
+        AuxiliaryServiceManager.GRAVITINO_AUX_SERVICE_PREFIX
+            + AuxiliaryServiceManager.AUX_SERVICE_NAMES,
+        auxServices);
     if (!ignoreLanceAuxRestService) {
       customConfigs.put(
           LANCE_CONFIG_PREFIX + METALAKE_NAME.getKey(),
@@ -415,6 +423,8 @@ public class BaseIT {
 
     List<String> authenticators = new ArrayList<>();
     String authenticatorStr = 
customConfigs.get(Configs.AUTHENTICATORS.getKey());
+    LOG.info("Creating authenticator {}", authenticatorStr);
+
     if (authenticatorStr != null) {
       authenticators = COMMA.splitToList(authenticatorStr);
     }
diff --git 
a/integration-test-common/src/test/java/org/apache/gravitino/integration/test/util/TestDatabaseName.java
 
b/integration-test-common/src/test/java/org/apache/gravitino/integration/test/util/TestDatabaseName.java
index 610bb6730c..1c178036e2 100644
--- 
a/integration-test-common/src/test/java/org/apache/gravitino/integration/test/util/TestDatabaseName.java
+++ 
b/integration-test-common/src/test/java/org/apache/gravitino/integration/test/util/TestDatabaseName.java
@@ -60,6 +60,9 @@ public enum TestDatabaseName {
   /** Represents the MySQL database used for testing the catalog integration 
with MySQL. */
   MYSQL_CATALOG_MYSQL_IT,
 
+  /** Represents the MySQL database used for testing catalog credential 
integration. */
+  MYSQL_CATALOG_CREDENTIAL_IT,
+
   PG_JDBC_BACKEND,
 
   /** Represents the PostgreSQL database for CatalogPostgreSqlIT. */

Reply via email to